tinc で CentOS 7 -> CentOS 7 な VPN を作る

tinc というものを使えば簡単に VPN ができるようなので試してみました。

クライアント/サーバ型で VPN 作ります。クライアント・サーバともに CentOS 7 です。VPN の端点は次の通り IP アドレスにします。

  • Server: 192.169.255.1
  • Client: 192.169.255.2

tinc はホストが元々持っている NIC とは別に TUN デバイスを作成してその TUN 同士を接続します。TUN というのは L3 をエミュレーションする仮想デバイスです。OpenVPN や OpenSSL で作る VPN もだいたい同じ仕組みなので Linux でソフトウェア VPN といえば TUN なのかもしれません。なお TAP という L2 をエミュレーションする仮想デバイスもあるのですが今回は使いません。

インストール

tinc は CentOS 7 なら epel からインストールできます。

yum install -y epel-release
yum install -y tinc

以下のファイルが含まれていました。

rpm -ql tinc
# /usr/lib/systemd/system/tinc.service
# /usr/lib/systemd/system/tinc@.service
# /usr/sbin/tincd
# /usr/share/doc/tinc-1.0.35
# /usr/share/doc/tinc-1.0.35/AUTHORS
# /usr/share/doc/tinc-1.0.35/COPYING.README
# /usr/share/doc/tinc-1.0.35/NEWS
# /usr/share/doc/tinc-1.0.35/README
# /usr/share/doc/tinc-1.0.35/THANKS
# /usr/share/doc/tinc-1.0.35/sample-config
# /usr/share/doc/tinc-1.0.35/sample-config/hosts
# /usr/share/doc/tinc-1.0.35/sample-config/hosts/alpha
# /usr/share/doc/tinc-1.0.35/sample-config/hosts/beta
# /usr/share/doc/tinc-1.0.35/sample-config/rsa_key.priv
# /usr/share/doc/tinc-1.0.35/sample-config/tinc-down
# /usr/share/doc/tinc-1.0.35/sample-config/tinc-up
# /usr/share/doc/tinc-1.0.35/sample-config/tinc.conf
# /usr/share/doc/tinc-1.0.35/texinfo.tex
# /usr/share/info/tinc.info.gz
# /usr/share/licenses/tinc-1.0.35
# /usr/share/licenses/tinc-1.0.35/COPYING
# /usr/share/man/man5/tinc.conf.5.gz
# /usr/share/man/man8/tincd.8.gz

サーバの設定

まずはサーバを設定します。

作成する VPN に関係するファイルを配置するディレクトリを作成します。このディレクトリの名前は最後に行う tuncd-n オプションの名前になります(systemd のテンプレートユニットのパラメータ部分でもあります)。

mkdir -p /etc/tinc/my_vpn/hosts

tinc が開始したときと終了したときに実行されるスクリプトを作成します。このスクリプトで TUN デバイスのアクティブ化や IP アドレスの設定を行います。tinc が開始する前は TUN デバイスがまだ無いので開始時に行う必要があります。

cat <<'EOS'> /etc/tinc/my_vpn/tinc-up
#!/bin/sh
ip link set $INTERFACE up
ip addr add 192.168.255.1/32 dev $INTERFACE
ip route add 192.168.255.0/24 dev $INTERFACE
EOS

cat <<'EOS'> /etc/tinc/my_vpn/tinc-down
#!/bin/sh
ip route del 192.168.255.0/24 dev $INTERFACE
ip addr del 192.168.255.1/32 dev $INTERFACE
ip link set $INTERFACE down
EOS

chmod +x /etc/tinc/my_vpn/tinc-up
chmod +x /etc/tinc/my_vpn/tinc-down

tinc の設定ファイルを作ります。Name/etc/tinc/my_vpn/hosts/ に作成するファイル名と一致する必要があります。Device には TUN デバイスを指定します。/dev/net/tun とすれば良いようです。

cat <<'EOS'> /etc/tinc/my_vpn/tinc.conf
Name = server
Device = /dev/net/tun
EOS

次にこのサーバのホスト設定ファイルを作成します。前述の通り↑の Name と一致している必要があります。Address にはクライアントから見たサーバの IP アドレスを、Subnet はこの VPN 上でのネットワークアドレスを指定します。

cat <<EOS> /etc/tinc/my_vpn/hosts/server
Address = 203.0.113.100
Subnet = 192.168.255.0/24
EOS

サーバの認証用の RSA 鍵を作成します。

tincd -n my_vpn -K4096

↑のコマンドでサーバのホスト設定ファイルに公開鍵が追記されます。

cat /etc/tinc/my_vpn/hosts/server

クライアントの設定

次にクライアントを設定します。ディレクトリや開始・終了のスクリプトの作成はサーバと同じです。ただし、スクリプトにはクライアント側の IP アドレスを記載する必要があります。

mkdir -p /etc/tinc/my_vpn/hosts

cat <<'EOS'> /etc/tinc/my_vpn/tinc-up
#!/bin/sh
ip link set $INTERFACE up
ip addr add 192.168.255.2/32 dev $INTERFACE
ip route add 192.168.255.0/24 dev $INTERFACE
EOS

cat <<'EOS'> /etc/tinc/my_vpn/tinc-down
#!/bin/sh
ip route del 192.168.255.0/24 dev $INTERFACE
ip addr del 192.168.255.2/32 dev $INTERFACE
ip link set $INTERFACE down
EOS

chmod +x /etc/tinc/my_vpn/tinc-up
chmod +x /etc/tinc/my_vpn/tinc-down

tinc の設定ファイルを作ります。クライアントは ConnectTo で接続するサーバ名を指定します。この名前も /etc/tinc/my_vpn/hosts/ の中のファイル名に一致する必要があります。その他はサーバと特に変わりありません。

cat <<'EOS'> /etc/tinc/my_vpn/tinc.conf
Name = client
Device = /dev/net/tun
ConnectTo = server
EOS

クライアントのホスト設定ファイルを作成します。クライアントは Address は不要です。Subnet には VPN 接続上のこのホストの IP アドレスを指定します。

cat <<EOS> /etc/tinc/my_vpn/hosts/client
Subnet = 192.168.255.2/32
EOS

認証用の RSA 鍵を作成します。

tincd -n my_vpn -K4096

↑のコマンドでクライアントのホスト設定ファイルに公開鍵が追記されます。

cat /etc/tinc/my_vpn/hosts/client

ホストの設定ファイルの交換

何らかの方法でサーバとクライアントのホスト設定ファイルをコピーします。

  • サーバの /etc/tinc/my_vpn/hosts/server をクライアントの同ディレクトリにコピー
  • クライアントの /etc/tinc/my_vpn/hosts/client をサーバの同ディレクトリにコピー

クライアント・サーバともに次のようなディレクトリ構成になります。

/etc/tinc/my_vpn/
├── hosts
│   ├── client
│   └── server
├── rsa_key.priv
├── tinc.conf
├── tinc-down
└── tinc-up

VPN の開始

クライアントとサーバの両方で tincd をお試しで開始します。

tincd -n my_vpn -D -d3

起動に成功して接続も問題なさそうなら殺します。

pkill tincd

systemd でサービスとして実行します。

systemctl start tinc@my_vpn.service
systemctl status tinc@my_vpn.service
systemctl enable tinc@my_vpn.service

相互に ping が通れば成功です。

ping -n 192.168.255.1
ping -n 192.168.255.2

サーバで NAT して VPN 経由で外に出る

これだけだとサーバとクライアントが P2P で繋がっているだけなので、クライアントから VPN を通ってサーバを経由して外に出られるようにしてみます。

まず、サーバのホスト設定ファイルで Subnet = 0.0.0.0/0 を追加します。これはクライアントとサーバの両方で行います。

vim /etc/tinc/my_vpn/hosts/server
Address = 203.0.113.100
Subnet = 192.168.255.0/24
Subnet = 0.0.0.0/0

-----BEGIN RSA PUBLIC KEY-----
...
-----END RSA PUBLIC KEY-----

サーバに NAT のために firewalld を入れます。

yum install -y firewalld
systemctl start firewalld
systemctl status firewalld
systemctl enable firewalld

サーバの I/F を external にして 655 ポートを許可します。 なお、external はデフォでマスカレードが有効なので VPN 経由でこのポートから出るときに NAT されるようになります。

firewall-cmd --zone=external --add-interface=eth0
firewall-cmd --zone=external --add-port=655/tcp
firewall-cmd --zone=external --add-port=655/udp
firewall-cmd --runtime-to-permanent

サーバで VPN が開始したときに VPN の I/F が trusted ゾーンになるようにします。

cat <<'EOS'> /etc/tinc/my_vpn/tinc-up
#!/bin/sh
ip link set $INTERFACE up
ip addr add 192.168.255.1/32 dev $INTERFACE
ip route add 192.168.255.0/24 dev $INTERFACE
firewall-cmd --zone=trusted --add-interface=$INTERFACE
EOS

cat <<'EOS'> /etc/tinc/my_vpn/tinc-down
#!/bin/sh
firewall-cmd --zone=trusted --remove-interface=$INTERFACE
ip route del 192.168.255.0/24 dev $INTERFACE
ip addr del 192.168.255.1/32 dev $INTERFACE
ip link set $INTERFACE down
EOS

chmod +x /etc/tinc/my_vpn/tinc-up
chmod +x /etc/tinc/my_vpn/tinc-down

次にクライアント側で VPN に接続したときにデフォルトゲートウェイが VPN を向くようにします。

cat <<'EOS'>> /etc/tinc/my_vpn/hosts/server-up
#!/bin/sh
ip route save match 0.0.0.0/0 > /etc/tinc/my_vpn/route.dump
ip route replace default via 192.168.255.1
EOS

cat <<'EOS'>> /etc/tinc/my_vpn/hosts/server-down
#!/bin/sh
ip route flush match 0.0.0.0/0
ip route restore < /etc/tinc/my_vpn/route.dump
EOS

chmod +x /etc/tinc/my_vpn/hosts/server-up
chmod +x /etc/tinc/my_vpn/hosts/server-down

クライアントとサーバの両方で tinc デーモンをリスタートします。

systemctl restart tinc@my_vpn.service

クライアントでルーティングが設定されていることを確認します。

ip route list match 0.0.0.0/0
#=> default via 192.168.255.1 dev my_vpn

サーバで firewlld が設定されていることを確認します。

firewall-cmd --get-active-zone
#=> external
#=>   interfaces: eth0
#=> trusted
#=>   interfaces: my_vpn

サーバで tcpdump で VPN を監視して

tcpdump -nn -i my_vpn

クライアントで適当に外部にアクセスして反応があれば OK です。

curl httpbin.org/get

あ、よく考えたら今回はクライアントとサーバが同じサブネットにあったのでガスっとデフォルトゲートウェイ変更しましたけど普通はそうじゃないのでクライアントのルーティングテーブルでサーバへの経路を確保しておく必要がありますね。

さいごに

OpenVPN と比べると ipfirewall-cmd で自前でネットワーク関係の構成を設定する必要があるぶん tinc 自体はシンプルですかね(OpenVPN はもっとサクッと NAT とかさせられた気がする? うろ覚え)

また、以下のように Windows でも動きそうです(未確認)(OpenVPN も Windows で動くだろうけど)

そのうち Windows でも試してみようと思います。

参考にしたサイトとか