2017年3月18日土曜日

sshでVPN。やってみる。


我が家のubuntuサーバでVPN(Virtual Private Network)を試みることに。
ssh(Secure Shell)のトンネリング機能を利用したVPNの備忘録。


(1) sshの準備

sshのトンネリングでは、デバイスの設定などroot権限が必要なこととVPNを行うためのトンネリング設定をconfig ファイル(/etc/ssh/sshd_conf)に書く必要がある。
PermitRootLoginとPermitTunnelを以下に。PermitRootLoginのforced-commands-onlyってのはルートでログインはできるけど、コマンド一発叩いて終わりってものらしい。
PermitRootLogin forced-commands-only
# 次にPermitRootLogin。レイヤー2(TAP)のみを許可する場合は、
# PermitTunnel ethernet
# レイヤー3(TUN) のみを許可する場合は、
PermitTunnel point-to-point
# 両方のトンネリングを許可する場合は、
# PermitTunnel yes
今回はレイヤー3のpoint-to-pointoに設定↑。ここで、sshdの再起動
server$ service sshd reload 

(2) ssh:鍵の生成と配置

クライアント側で秘密鍵と公開鍵をssh-keygenコマンドで生成。
鍵の名前を「vpn」としてrootで実行。パスフレーズはなしで設定。
client$ ssh-keygen -f ~/.ssh/vpn
(略)
  Your identification has been saved in /root/.ssh/vpn.
  Your public key has been saved in /root/.ssh/vpn.pub.
秘密鍵「vpn」と公開鍵「vpn.pub」が生成される。

サーバーにこの公開鍵(vpn.pub)を配置する。
scpなどで、クライアント側のvpn.pubをサーバーに転送し、鍵認証設定ファイル(authorized_keys)に追記する。
server$ cat vpn.pub >> authorized_keys
server$ cat authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDi0BlNgdq・・・
(略)
server$ chmod 600 authorized_keys
server$ rm -fv vpn.pub

(3) 仮想ネットワークデバイスのTUN/TAP

TAPはデバイスのシミュレートで、TUNはネットワーク層のシミュレートだって。レイヤーが違うとのこと。
ubuntuはいいんですが、私のメインマシンのMacOSXはどうなんでしょう。
よくわからんが、Mac App Storeに「tuntap」なるものがあったので、とりあえずインストール。
client$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null
client$ brew install tuntap
pythonでtunを開いたり設定したりできるみたい。

That Handy Tap Interface on Mac OSX
#!/usr/bin/python
import os
while 1:
 try:
  file_path = '/dev/tap1'
  dev_file = os.open(file_path, os.O_RDWR)
  interface = 'tap1'
  while 1:
   pass
 except:
  print "tap interface is closing"
  exit()
  break
PythonでTUN/TAPを使う
#!/usr/bin/python
import os
import subprocess
tun = os.open('/dev/tun0', os.O_RDWR)
subprocess.check_call('sudo ifconfig tun0
 192.168.100.2 192.168.100.1
 netmask 255.255.255.0 up',
 shell=True)
while True:
 data = os.read(tun, 1500)
 print(data)
 os.write(tun, data)

(4) sshでこの仮想ネットワークデバイスを利用した接続設定

authorized_keysの先頭に、rootでログインしたときの設定やコマンドを登録。
特定のコマンドを実行する際には、
command="/bin/ls -l" ssh-rsa ・・・
今回はtunを使ったデバイス設定コマンド(ifconfig)と禁止事項の設定を書く。
仮想ネットワークデバイスのtun0にアドレスを設定。サーバーのアドレスを10.0.0.1にクライアント側を10.0.0.2に。
ifconfig tun0 10.0.0.1 pointopoint 10.0.0.2 # デバイス設定コマンド
no-X11-forwarding  # X11 フォワードを禁止
no-agent-forwarding # 認証エージェント転送の禁止
no-pty # 端末の割り当て禁止
これらをまとめてauthorized_keysの先頭に。ssh-rsa ・・・の直前に記載する。
server$ cat authorized_keys
no-X11-forwarding,no-agent-forwarding,no-pty,command="ifconfig tun0 10.0.0.1 pointopoint 10.0.0.2" ssh-rsa AAAAB3NzaC1yc2EAAAADAQ・・・
(略)
これでサーバー側のsshの設定は完了。

クライアント側のssh接続の際の設定ファイル(.ssh/config)にも、クライアント側のデバイス設定などを記載。
client$ cat config
Host server
  HostName server
  IdentityFile ~/.ssh/vpn
  Tunnel point-to-point
  TunnelDevice 0:0
  RequestTTY no
  PermitLocalCommand yes
  LocalCommand ifconfig tun0 inet 10.0.0.2 10.0.0.1
ssh接続の際に実行するコマンドをLocalCommandで設定。
クライアント側のtun0は当然のことながらサーバー設定の逆。(クライアント自身のアドレスを10.0.0.2にサーバート側を10.0.0.1に)

ほぼ、これで準備完了。

(5) いざ、接続

某プロバイダーに接続を変えて(一旦、外部に接続を変更)、クライアントからssh接続をrootで実行。ポートはxxxに変更。
-wオプションは、ローカルtun [:リモートtun ] クライアント側のtunデバイス番号とサーバ側のtunデバイス番号を使ったトンネリングの要求)、-vオプションでメッセージを表示。
client$ ssh -v -w0:0 server -p xxx
debug1: Reading configuration data /root/.ssh/config
debug1: /root/.ssh/config line 1: Applying options for server
debug1: Reading configuration data /etc/ssh_config
debug1: /etc/ssh_config line 20: Applying options for *
debug1: /etc/ssh_config line 102: Applying options for *
debug1: Connecting to server [aaa.bbb.ccc.ddd] port xxx.
debug1: Connection established.
(略)
debug1: sys_tun_open: /dev/tun0 mode 1 fd 5
debug1: channel 0: new [tun]
debug1: channel 1: new [client-session]
(略)
debug1: Remote: X11 forwarding disabled.
debug1: Remote: Agent forwarding disabled.
(略)
debug1: client_input_channel_req: channel 1 rtype exit-status reply 0
debug1: client_input_channel_req: channel 1 rtype eow@openssh.com reply 0
debug1: channel 1: free: client-session, nchannels 2
って感じ。
サーバー側の状況。
server$ ifconfig tun0
tun0      Link encap:不明なネット  ハードウェアアドレス 00-00-00-00-00-・・・
          inetアドレス:10.0.0.1  P-t-P:10.0.0.2  マスク:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  メトリック:1
          RXパケット:0 エラー:0 損失:0 オーバラン:0 フレーム:0
          TXパケット:24 エラー:0 損失:0 オーバラン:0 キャリア:0
          衝突(Collisions):0 TXキュー長:500 
          RXバイト:0 (0.0 B)  TXバイト:6820 (6.8 KB)
クライアント側。
client$ ifconfig tun0
tun0: flags=8851 mtu 1500
 inet 10.0.0.2 --> 10.0.0.1 netmask 0xff000000 
 open (pid 1289)
ちなみにpsコマンドでプロセスを確認すると、
client$ ps aux
USER PID  %CPU %MEM   VSZ    RSS  TT   STAT  STARTED   TIME    COMMAND
(略)
root 1289  0.0  0.1 2461144  2260 s001  S+    2:01PM   0:00.04 ssh -v -w0:0 server -p xxx
(略)
となっており、上記のifconfigで確認したクライアント側のtun0のプロセスid(pid 1289)とsshの接続コマンドのpidの一致がわかる。
で、クライアント側からpingで確認(10.0.0.2→10.0.0.1)。
client$ ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1): 56 data bytes
64 bytes from 10.0.0.1: icmp_seq=0 ttl=64 time=2.212 ms
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=3.751 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=2.054 ms
サーバーからも同様に(10.0.0.1→10.0.0.2)。
server$ ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=2.15 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=89.6 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=111 ms
とりあずpoint to pointは開通。
クライアントのルーティングテーブルを見ると、
client$ netstat -nr
Routing tables
Internet:
Destination        Gateway            Flags        Refs      Use   Netif Expire
default            192.168.0.1        UGSc           19        0     en0
10.0.0.1           10.0.0.2           UH              1        6    tun0
127                127.0.0.1          UCS             0        0     lo0
127.0.0.1          127.0.0.1          UH              3     3512     lo0
169.254            link#4             UCS             0        0     en0
192.168.0          link#4             UCS             9        0     en0
このままではまだサーバー側のネットワーク(192.168.0.0/24)にアクセスできないので、クライアントのRouting tablesにゲートウェイとしてtun0を介したサーバーアドレスの10.0.0.1を追加。
client$ route add -net 192.168.0.0/24 10.0.0.1
client$ netstat -nr
(略)
Destination        Gateway            Flags        Refs      Use   Netif Expire
(略)
192.168.0          10.0.0.1           UGSc            0        0    tun0
pingをかけると、
client$ ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1): 56 data bytes
Request timeout for icmp_seq 0
Request timeout for icmp_seq 1
Request timeout for icmp_seq 2
いかん。まだ192.168.0.0側に通じてない。
サーバー側のtun0の様子をtcpdumpで見る。
client$ ping 192.168.0.1

server$ tcpdump -s0 -i tun0 -X
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
12:46:21.578871 IP 10.0.0.2 > 192.168.0.1: ICMP echo request, id 17924, seq 0, length 64
 0x0000:  4500 0054 de39 0000 4001 d1c4 0a00 0002  E..T.9..@.......
(略)
 0x0040:  2425 2627 2829 2a2b 2c2d 2e2f 3031 3233  $%&'()*+,-./0123
 0x0050:  3435 3637                                4567
12:46:22.531078 IP 10.0.0.2 > 192.168.0.1: ICMP echo request, id 17924, seq 1, length 64
 0x0000:  4500 0054 d813 0000 4001 d7ea 0a00 0002  E..T....@.......
(略)
 0x0020:  0006 ba82 0809 0a0b 0c0d 0e0f 1011 1213  ................
 0x0030:  1415 1617 1819 1a1b 1c1d 1e1f 2021 2223  .............!"#
ルーティングは効いてつながってはいる。
ってことで、クライアントの.ssh/configのコマンドにルーティング設定も加える。
client$ cat config
Host server
  HostName server
  IdentityFile ~/.ssh/vpn
  Tunnel point-to-point
  TunnelDevice 0:0
  RequestTTY no
  PermitLocalCommand yes
  LocalCommand ifconfig tun0 inet 10.0.0.2 10.0.0.1;
 route add -net 192.168.0.0/24 10.0.0.1
ただ、サーバー側がまだ足りない。natして外に出られるようにする。
iptablesでルールの設定。「enp2s0」はサーバー側(192.168.0.0/24)のインターフェース。
tun0からは制限をかけず、ESTABLISHED,RELATEDは許可ってのも設定しておく。
server$ iptables -A FORWARD -i tun0 -o enp2s0 -s 10.0.0.0/24 -j ACCEPT
server$ iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
tun0(10.0.0.0/24)から出て行くパケットをサーバーのアドレスに書き換える。
server$ iptables -t nat -A POSTROUTING -o enp2s0 -s 10.0.0.0/24 -j MASQUERADE
iptablesの様子。
server$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy DROP)
target     prot opt source               destination         
ACCEPT     all  --  10.0.0.0/24          anywhere
ACCEPT     all  --  anywhere             anywhere             state RELATED,ESTABLISHED
(略)

server$ sudo iptables -L -t nat
(略)
Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  10.0.0.0/24          anywhere
iptablesの保存。一応。
iptables-save
再度、ping。
client$ ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1): 56 data bytes
64 bytes from 192.168.0.1: icmp_seq=0 ttl=63 time=724.364 ms
64 bytes from 192.168.0.1: icmp_seq=1 ttl=63 time=217.941 ms
64 bytes from 192.168.0.1: icmp_seq=2 ttl=63 time=177.990 ms
いいんじゃないんっすか。
ちなみに別のMacBookで動いているWebサーバーにhttpかけてみると、
client$ telnet 192.168.0.49 80
Trying 192.168.0.49...
Connected to 192.168.0.49.
Escape character is '^]'.
GET /
<body><h1>
It works!</h1>
</body></html>Connection closed by foreign host.

OK。
いいんじゃないんっすか。
某プロバイダ経由はちょっとスピードは遅いけど、sshトンネルでvpnができた模様です。

以下のページを参考にしました。Special Thanks.


簡単に書いてみたけど、結構すったもんだ時間かかりました。勉強になった。。。


徒然なるままに今日も明日も。

0 件のコメント:

コメントを投稿