お手軽に VPN 経由のノード間通信を実現する Tinc を使ってみる

ノード（コンピュータ）間の通信の機密性をプロトコルに寄らずに保持したい場面，ありますよね。
今回はそんなことを手軽に実現できる [Tinc](https://www.tinc-vpn.org/) という VPN デーモンを紹介します。

## きっかけ
サーバから NFS で NAS のディレクトリをマウントしてコンテンツの保存などを行うことを計画していました。
ところが NFS は暗号化などされないそうで，ArchWiki でも警告されています ([NFS - ArchWiki](https://wiki.archlinux.jp/index.php/NFS))。
同時に ArchWiki では Kerberos や Tinc などの暗号化プロトコルを使って NFS をトンネル化することを推奨しています。
そこで，今回は Tinc を用いてノード間の VPN 通信を実現します。

## 前提条件・環境
* MacOS (仮想マシンのホスト)
* Ubuntu Server 20.04 (仮想マシン) x 3

今回は Mac 上の仮想マシン間での通信を取り扱います。
NAT 越えなどは取り扱いません。

## 環境構築
まずは仮想マシンを用意します。
今回は以下の構成で用意しました。

<!-- TODO 画像 -->

仮想マシンは OS インストール時に OpenSSH をインストール済みです。
各仮想マシンに Avahi を導入しておきます:

```console
$ sudo apt install avahi-daemon
```

これにより，ホスト PC から各仮想マシンのホスト名でアクセスできるようになるので，SSH でログイン可能であることを確認してください:

```console
$ ssh user@tinc-a.local 
$ ssh user@tinc-b.local 
$ ssh user@tinc-c.local 
```

## Tinc のインストール
まずは各仮想マシンに Tinc をインストールします:

```console
$ sudo apt install tinc
```

## Tinc の設定
この 3 台の仮想マシン `tinc-a`, `tinc-b`, `tinc-c` の間に `vpnnet` という名前のネットワークを構築します。
なお，`tinc-a` はエントリポイントと呼ばれるすべてのマシンが接続するマシンとしてセットアップします。
他のマシンはエントリポイントであるマシンと接続を維持する必要があります。

### tinc-a の設定
まずはネットワークのためのディレクトリを作成します:

```console
$ sudo mkdir -p /etc/tinc/vpnnet
```

```conf:/etc/tinc/vpnnet/tinc.conf
Name = alpha
AddressFamily = ipv4
Interface = tun0
```

```bash:/etc/tinc/vpnnet/tinc-up
#!/bin/sh
ip link set $INTERFACE up
ip addr add  192.168.21.1 dev $INTERFACE
ip route add 192.168.21.0/24 dev $INTERFACE
```

```bash:/etc/tinc/vpnnet/tinc-down
#!/bin/sh
ip route del 192.168.21.0/24 dev $INTERFACE
ip addr del 192.168.21.1 dev $INTERFACE
ip link set $INTERFACE down
```

```console
$ sudo chmod +x /etc/tinc/vpnnet/tinc-down /etc/tinc/vpnnet/tinc-up
```

### tinc-b の設定
まずはネットワークのためのディレクトリを作成します:

```console
$ sudo mkdir -p /etc/tinc/vpnnet
```

```conf:/etc/tinc/vpnnet/tinc.conf
Name = beta
AddressFamily = ipv4
Interface = tun0
ConnectTo = alpha
```

```bash:/etc/tinc/vpnnet/tinc-up
#!/bin/sh
ip link set $INTERFACE up
ip addr add  192.168.21.2 dev $INTERFACE
ip route add 192.168.21.0/24 dev $INTERFACE
```

```bash:/etc/tinc/vpnnet/tinc-down
#!/bin/sh
ip route del 192.168.21.0/24 dev $INTERFACE
ip addr del 192.168.21.2 dev $INTERFACE
ip link set $INTERFACE down
```

```console
$ sudo chmod +x /etc/tinc/vpnnet/tinc-down /etc/tinc/vpnnet/tinc-up
```

### tinc-c の設定
まずはネットワークのためのディレクトリを作成します:

```console
$ sudo mkdir -p /etc/tinc/vpnnet
```

```conf:/etc/tinc/vpnnet/tinc.conf
Name = gamma
AddressFamily = ipv4
Interface = tun0
ConnectTo = alpha
```

```bash:/etc/tinc/vpnnet/tinc-up
#!/bin/sh
ip link set $INTERFACE up
ip addr add  192.168.21.3 dev $INTERFACE
ip route add 192.168.21.0/24 dev $INTERFACE
```

```bash:/etc/tinc/vpnnet/tinc-down
#!/bin/sh
ip route del 192.168.21.0/24 dev $INTERFACE
ip addr del 192.168.21.3 dev $INTERFACE
ip link set $INTERFACE down
```

```console
$ sudo chmod +x /etc/tinc/vpnnet/tinc-down /etc/tinc/vpnnet/tinc-up
```

### ノード間情報の設定
各ノードの `/etc/tinc/vpnnet/hosts/` にノード間の接続に必要な情報を記述します。

#### tinc-a
```conf:/etc/tinc/vpnnet/hosts/alpha
Address = 192.168.56.3
Port = 655
Subnet = 192.168.21.1/24
```

```console
$ sudo tincd -n vpnnet -K
Generating 2048 bits keys:
.......................................................................................+++++ p
...................................+++++ q
Done.
Please enter a file to save private RSA key to [/etc/tinc/vpnnet/rsa_key.priv]:
Invalid name for myself!
Please enter a file to save public RSA key to [/etc/tinc/vpnnet/hosts/alpha]:
```

公開鍵が追記された `/etc/tinc/vpnnet/hosts/alpha` を他のノードにも保存します。

#### tinc-b
```conf:/etc/tinc/vpnnet/hosts/bata
Address = 192.168.56.4
Port = 655
Subnet = 192.168.21.2/24
```

```console
$ sudo tincd -n vpnnet -K
Generating 2048 bits keys:
.......................................................................................+++++ p
...................................+++++ q
Done.
Please enter a file to save private RSA key to [/etc/tinc/vpnnet/rsa_key.priv]:
Invalid name for myself!
Please enter a file to save public RSA key to [/etc/tinc/vpnnet/hosts/beta]:
```

公開鍵が追記された `/etc/tinc/vpnnet/hosts/beta` を他のノードにも保存します。

#### tinc-c
```conf:/etc/tinc/vpnnet/hosts/gamma
Address = 192.168.56.5
Port = 655
Subnet = 192.168.21.3/24
```

```console
$ sudo tincd -n vpnnet -K
Generating 2048 bits keys:
.......................................................................................+++++ p
...................................+++++ q
Done.
Please enter a file to save private RSA key to [/etc/tinc/vpnnet/rsa_key.priv]:
Invalid name for myself!
Please enter a file to save public RSA key to [/etc/tinc/vpnnet/hosts/gamma]:
```

公開鍵が追記された `/etc/tinc/vpnnet/hosts/gamma` を他のノードにも保存します。

## Tinc を起動
設定が適切に行えたか，起動して確かめます。
各ノードで以下のコマンドを実行してください:

```console
$ tincd -n vpnname
```

問題なくコマンドが実行できたら，各ノード間で疎通確認を行いましょう:

```console
$ # tinc-a 上
$ ping -c 5 192.168.21.2
PING 192.168.21.2 (192.168.21.2) 56(84) bytes of data.
64 bytes from 192.168.21.2: icmp_seq=1 ttl=64 time=1.01 ms
64 bytes from 192.168.21.2: icmp_seq=2 ttl=64 time=1.01 ms
64 bytes from 192.168.21.2: icmp_seq=3 ttl=64 time=1.66 ms
64 bytes from 192.168.21.2: icmp_seq=4 ttl=64 time=1.50 ms
64 bytes from 192.168.21.2: icmp_seq=5 ttl=64 time=0.470 ms

--- 192.168.21.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4008ms
rtt min/avg/max/mdev = 0.470/1.127/1.659/0.419 ms
$ ping -c 5 192.168.21.3
PING 192.168.21.3 (192.168.21.3) 56(84) bytes of data.
64 bytes from 192.168.21.3: icmp_seq=1 ttl=64 time=0.726 ms
64 bytes from 192.168.21.3: icmp_seq=2 ttl=64 time=0.385 ms
64 bytes from 192.168.21.3: icmp_seq=3 ttl=64 time=1.55 ms
64 bytes from 192.168.21.3: icmp_seq=4 ttl=64 time=1.45 ms
64 bytes from 192.168.21.3: icmp_seq=5 ttl=64 time=1.27 ms

--- 192.168.21.3 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4035ms
rtt min/avg/max/mdev = 0.385/1.076/1.554/0.447 ms
```

```console
$ # tinc-b 上
$ ping -c 5 192.168.21.1
PING 192.168.21.1 (192.168.21.1) 56(84) bytes of data.
64 bytes from 192.168.21.1: icmp_seq=1 ttl=64 time=1.47 ms
64 bytes from 192.168.21.1: icmp_seq=2 ttl=64 time=1.56 ms
64 bytes from 192.168.21.1: icmp_seq=3 ttl=64 time=0.827 ms
64 bytes from 192.168.21.1: icmp_seq=4 ttl=64 time=1.36 ms
64 bytes from 192.168.21.1: icmp_seq=5 ttl=64 time=1.21 ms

--- 192.168.21.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4024ms
rtt min/avg/max/mdev = 0.827/1.285/1.563/0.257 ms
$ ping -c 5 192.168.21.3
PING 192.168.21.3 (192.168.21.3) 56(84) bytes of data.
64 bytes from 192.168.21.3: icmp_seq=1 ttl=64 time=1.53 ms
64 bytes from 192.168.21.3: icmp_seq=2 ttl=64 time=0.973 ms
64 bytes from 192.168.21.3: icmp_seq=3 ttl=64 time=5.83 ms
64 bytes from 192.168.21.3: icmp_seq=4 ttl=64 time=2.11 ms
64 bytes from 192.168.21.3: icmp_seq=5 ttl=64 time=0.817 ms

--- 192.168.21.3 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4013ms
rtt min/avg/max/mdev = 0.817/2.253/5.829/1.845 ms
```

```console
$ # tinc-c 上
$ ping -c 5 192.168.21.1
PING 192.168.21.1 (192.168.21.1) 56(84) bytes of data.
64 bytes from 192.168.21.1: icmp_seq=1 ttl=64 time=0.472 ms
64 bytes from 192.168.21.1: icmp_seq=2 ttl=64 time=1.39 ms
64 bytes from 192.168.21.1: icmp_seq=3 ttl=64 time=0.492 ms
64 bytes from 192.168.21.1: icmp_seq=4 ttl=64 time=1.55 ms
64 bytes from 192.168.21.1: icmp_seq=5 ttl=64 time=0.505 ms

--- 192.168.21.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4037ms
rtt min/avg/max/mdev = 0.472/0.882/1.549/0.483 ms
$ ping -c 5 192.168.21.2
PING 192.168.21.2 (192.168.21.2) 56(84) bytes of data.
64 bytes from 192.168.21.2: icmp_seq=1 ttl=64 time=0.859 ms
64 bytes from 192.168.21.2: icmp_seq=2 ttl=64 time=1.39 ms
64 bytes from 192.168.21.2: icmp_seq=3 ttl=64 time=1.46 ms
64 bytes from 192.168.21.2: icmp_seq=4 ttl=64 time=1.46 ms
64 bytes from 192.168.21.2: icmp_seq=5 ttl=64 time=0.702 ms

--- 192.168.21.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4009ms
rtt min/avg/max/mdev = 0.702/1.173/1.462/0.325 ms
```

ということで互いに通信できることが確認できました👏

## Tinc の自動起動設定
マシンの起動時に Tinc を自動的に起動するようにしましょう。

各マシンで以下のコマンドを実行します:

```console
$ sudo systemctl enable tinc  # Tinc 本体の自動起動を有効化
$ sudo systemctl enable tinc@vpnnet  # 自動起動するネットワークを指定する
```

実際に再起動してから ping コマンドを叩くと，自動起動されてつながっていることが確認できると思います。

## 参考
- [Tinc VPN](https://www.tinc-vpn.org/)
- [Tinc - ArchWiki](https://wiki.archlinux.jp/index.php/Tinc)
- [How To Install Tinc and Set Up a Basic VPN on Ubuntu 14.04 | DigitalOcean](https://www.digitalocean.com/community/tutorials/how-to-install-tinc-and-set-up-a-basic-vpn-on-ubuntu-14-04)

## 次にやること
今回は仮想マシン間の疎通確認までのため，本当に暗号化されているかの確認や NAT 越えの検証はできていません。
また，安定した稼働のためにはそれぞれのマシンの IP アドレスの固定が必要だと思われます。

なお，今回の設定は tinc-a と接続できる状態でないと tinn-b, tinc-c は Tinc を使った通信ができません。
この辺りも設定次第なようです。

## あとがき
これで平文のプロトコルでも安心して通信できるのでは🤔