最新のモダンVPN WireGuardを検証 実践的な設定や運用について

# はじめに

実家のVPN環境を刷新したくてモダンなVPNソリューションを模索していました。
その中で、WireGuardこそが本命だと感じ構築検証してみたので、同じようにVPNのソリューションを探している方の参考になればと思いこの記事を掲載します。

# VPNソリューション色々

一口にVPNと言っても、数多くのソリューション（ソフトウェア）があります。
一例を上げると

* OpenVPN
* SoftEther
* L2TP/IPSec
* IKEv2/IPSec

等。
ここでは、下記を選択の優先度として上げておきます。

1. パフォーマンス（通信速度）や安定性
2. 設定や運用の簡単さ（サーバサイド）
3. クライアントからの接続の容易さ（クライアントサイド）
4. マルチプラットフォーム対応（Linux, MacOS, Windows, Android, iOS etc...)

各種VPNソフトウェアの通信速度については、色々なサイトで比較記事がありますが、その中でもよく言われているのが、OpenVPNは遅い、L2TP/IPSecは遅いという点です。
私は過去に両方使ってみたことがありますが、確かにどちらも遅くもう少し物理回線の速度に近づけないかなと思っていました。
IKEv2/IPSecは、通信速度も速く安定していると思いますが、構築するためにはStrongSwanを用いてかなり複雑な設定をしなければならないのと、クライアント側にVPNサーバの証明書をインポートする必要性があり、構築と運用両面から見て厳しいと考え断念しました。
SoftEtherについては、速度が速く取扱いも簡単な部類に入り非常に良いソリューションなので長く使い続けてきた経緯があります。
しかし、MacOSやAndroid端末から接続する際にL2TP/IPSecでの接続とせざるを得ず通信速度が落ちてしまう点と、サーバサイドの構築の煩雑さからもう少し良いソリューションはないものかと別のソリューションを探していました。
そこで見つけたのが `WireGuard` です。

# WireGuard

WireGuardは、現在まだ安定版が存在しない開発途上ではありますが、

* パフォーマンス（通信速度）が非常に速くて安定
* 設定や運用が簡単（サーバサイド）
* クライアントからの接続が簡単（クライアントサイド）
* マルチプラットフォーム対応（Linux, MacOS, Windows, Android, iOS etc...)

という状況で、まさに探していたVPNソリューションかなと思い検証しました。
結果的に非常に良く出来ていた為、実家のVPN環境として本番運用を開始しました。

# WireGuardの実際の使い勝手について

まず、私が検証した限りで出来なかった（未実装 or 私の調査不足）事を先に記載します。

1. mDNSやnmb lookup等のLAN内での名前解決は不可能（IPアドレスでアクセスしています）
2. ブリッジ接続は不可能（代わりにルーティングでアクセスしています）
3. DHCPでのIP割り当ては不可能（固定IPアドレスをクライアント毎に手動で振っています）

という感じで、このあたりもう少し機能追加されるといいなぁと思っていたりはするのですが、上記を差し引いても、高速かつ構築や運用が非常に楽です。

# WireGuardのインストール

https://www.wireguard.com/install/
こちらに従えばOKです。

# WireGuardの設定

本エントリーの本命はここ。かなり設定を試行錯誤してようやく自分のニーズに合うVPN構築が出来ました。
最初に設定を公開します。

# Linux VPNサーバ側

VPN空間のプライベートネットワークサブセグメントを適当に決めます。私は `10.10.0.1/24` にしました。
自由に決めて構いませんが、実際の接続元LAN、接続先LANそれぞれのセグメントと同じにはしないほうが良いと思います。
おそらくトラブります、というかやっちゃだめです。
今回の環境だと、サーバが属する接続先LANは `192.168.11.0/24` で（一般的なBuffaloの家庭用ルータのデフォルトセグメント）クライアント側は `192.168.14.0/24` と、接続元と先で異なるセグメントに設定してあります。
このあたりは同じセグメントだとVPNサーバ側のLANへの通信が通らなくなる（接続元LANと区別がつかずルーティングが上手く行かない）と思います。
実際の設定例です。

```ini:/etc/wireguard/wg0.conf
[Interface]
Address = 10.10.0.1/24
# SaveConfig = true
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
ListenPort = 51820
PrivateKey = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

[Peer]
# client01
PublicKey = yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
AllowedIPs = 10.10.0.2/32
PersistentKeepalive = 25

[Peer]
# client02
PublicKey = zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
AllowedIPs = 10.10.0.3/32
PersistentKeepalive = 25

[Peer]
# client03
PublicKey = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
AllowedIPs = 10.10.0.4/32
PersistentKeepalive = 25
```

## [Interface]セクション

* Address

VPNサーバ側の仮想IPを指定します。適当にスタートのIPにしました。

* SaveConfig

この設定 `true` にすると、通信中にWireGuardが認識した値を随時設定ファイルに反映してくれるようになります。ただし、自分でわかりやすく設定ファイルに `#` から始まるコメント等を記載していても、それらは上書きにより消失してしまうため、私はコメントアウトして `false` にしています（何も記載しなければデフォルトで `false`）

* PostUp, PostDown

WireGuardを起動する直前に実行するシェルコマンド及びWireGuardを終了した後実行するシェルコマンドを指定するようです。
iptablesにあまり詳しくなく、なんとなくしか設定値の意味を理解していないのですが、ここが肝でこの設定を入れなければVPNクライアントからVPNサーバ内の別のホストへアクセスすることが出来ません。この設定により仮想LAN -> 物理LANへのルーティングを設定しています。
注意事項として、 `-o eth0` の箇所を、実際の物理NICのデバイス名に置き換えてください。（例：enp4s0 等）

* ListenPort

特にポート番号は決まっていないみたいですが、 `man wg-quick` を実行した際に表示されるマニュアルには `51820` が例として記載されていた為そのまま用いています。
ここで指定したポート番号をルータの設定にてUDPでNATしてやる必要があります。

* PrivateKey

https://www.wireguard.com/quickstart/#key-generation

```shell
wg genkey | tee privatekey | wg pubkey > publickey
```
このようなコマンドで秘密鍵と公開鍵を生成し、privatekeyの内容をサーバ側の設定に記載します。

## [Peer]セクション

接続してくるクライアント毎に設定を追記していきます。 `#` から始まる行はコメント行として使えるため、分かりやすいようメモしておくと良いかもです。

* PublicKey

クライアントの公開鍵を指定します。

```shell
wg genkey | tee privatekey | wg pubkey > publickey
```
上記のコマンドをもう一度別のディレクトリ等で実行しクライアント毎にKey Pairを生成するか、クライアント側にて生成するかで対応します。

* AllowedIPs

このクライアントが使うIPアドレスの許可リストです。ここで許可したIPアドレス以外のIPアドレスをクライアントが申告してきた場合、その通信はシンプルにDropされて通信できません。

* PersistentKeepalive

KeepAliveの設定です。デフォルトだとOFFですが、私は25秒で一応設定しておきました。

# VPNクライアント側（Linux, MacOS, Windows 共通）

サーバ側の設定とかなり似ています。実は、便宜上サーバ側クライアント側と記載してきましたが、WireGuardにおいてはそれらの区別はなく、どちらも対等の扱いです。故にPeerと表記されています。

```ini
[Interface]
PrivateKey = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
Address = 10.10.0.2/24
DNS = 8.8.8.8

[Peer]
PublicKey = cccccccccccccccccccccccccccccccccccccccccccc
AllowedIPs = 10.10.0.0/24, 192.168.11.0/24
Endpoint = サーバのグローバルIPアドレス or ドメイン名:51820
```
## [Interface]セクション

* PrivateKey

クライアントの秘密鍵を指定します。サーバ側で予め生成しておいたものを用いるか、MacOSやWindowsのWireGuard GUIアプリケーションでは起動して空の接続設定を作成した段階で生成されるのでそれを用いることも出来ます。

* Address

クライアントが使用する仮想IPアドレスを指定します。サーバ側の `AllowedIPs` で許可されたIPアドレスしか使用できません。

## [Peer]セクション

* PublicKey

サーバ側の公開鍵を指定します。各クライアント毎にここは同一のサーバに繋ぐのであれば常に同じ鍵を指定することになります。

* AllowedIPs

ここが肝です。ここで指定するIPレンジに合致した通信のみがVPNトンネル内を流れる形となります。逆に、ここで指定したIPアドレス以外への通信はVPNを通さず直接WANへ流れます。
私の場合、VPNサーバ側LAN内のみへVPNを通して通信し、通常のWANアクセスについてはVPNサーバを通さず直接アクセスとしたかったので、仮想LANのセグメントとVPNサーバ側のセグメントの2つのセグメントを `,` 区切りで指定しています。
サーバ側のLAN内へ通信を通したい場合、サーバ側のセグメントを指定する必要があります。

~~`AllowedIPs = 0.0.0.0/0` 等と指定すると、全ての通信をVPN経由に出来ると思ったのですが、これをするとWANへのアクセスすら通らなくなりインターネットに接続できなくなりました。公衆Wi-Fi等で通信を安全にするために全ての通信をVPN経由としたい場合については、どのように設定すれば良いのか未だに分かっていません。もしご存じの方いらっしゃいましたらコメント欄にてご教示頂ければ嬉しいです。~~

```
AllowedIPs = 0.0.0.0
DNS = 8.8.8.8
```

と設定することにより、無事に全ての通信をVPNサーバ経由とすることが出来ました。
DNSの設定が無かったことにより名前解決出来ていなかった事が原因のようでした。
余談ですが、通常のVPNでは全ての通信をVPNサーバ経由で通し、上記のようなVPNサーバ側のセグメントのみ
VPNサーバ経由、それ以外の通信はVPNサーバを経由せず直接通信する通信の仕方を `Split Tunneling` と言います。

* Endpoint

接続先のVPNサーバのグローバルIPアドレスまたはドメイン名を指定します。DDNS等でサーバを識別できるように設定している場合はそのDDNS名を指定します。 `:` に続けてサーバ側の設定で `ListenPort` で指定した番号を指定します。

# 終わりに

以上でVPNサーバとVPNクライアントで接続が可能となります。
サーバ側の設定ファイルやクライアント側の設定ファイルが非常に短くシンプルで、OpenVPNやStrongSwanの設定と比較すると圧倒的に簡単に思えます。
MacOS, Windowsに関してはGUIのStrongSwanアプリを用いて接続、Linuxクライアントに関しては、 `/etc/wireguard/wg0.conf` にクライアント側の接続設定を記載して

```shell
sudo wg-quick up wg0
```
のように接続しています。MacOS, WindowsのGUIアプリでは、同時に複数のWireGuard VPNサーバへの接続は現時点では不可能のようです（同時に接続しようとすると既に接続しているコネクションがDeactivate（切断）される）。
Linuxのコマンドラインからの接続においては、 `/etc/wireguard/wg1.conf` のように別の設定ファイルを作成し、

```shell
sudo wg-quick up wg1
```
のようにすることで、複数のWireGuard VPNサーバへ同時接続が実現できています。
切断する時は

```shell
sudo wg-quick down wg0
```
のように実行します。
また、Linux OS起動時に自動的に接続するには

```shell
sudo systemctl enable wg-quick@wg0.service
```
等と設定することで、自動接続が可能です。
LinuxにおいてもNetwork-Managerに統合されたGUI接続が可能なのですが、 Ubuntu 18.04 LTSのNetwork-ManagerにおいてはまだWireGuardのサポートが追加されておらず、2020年4月頃リリース予定の Ubuntu 20.04 LTSでこれが可能になる見込みです。
そうなれば、コマンドラインだけでなくGUIからの接続も試してみたいなと思っています。

以上、快適なVPNライフをお過ごしください。

## 参考

https://pc.watch.impress.co.jp/docs/column/ubuntu/1606110.html

本記事のWireGuardを用いた簡単なSaaS「tailscale」
https://internet.watch.impress.co.jp/docs/column/shimizu/1303751.html