WireGuard で VPN サーバーを作り、OpenWRT から接続して拠点間 VPN を構成する
背景
先日引っ越しをした。引っ越し先のインターネット環境は一言で言えば残念で、好きな ISP と契約することができず、マンションが包括して契約している ISP 経由でしかインターネットに接続できない。悲しいことにこの ISP は各個にグローバル IP を割り当てないので、外に向けたサーバーの公開が制限されている [1]。
この問題を回避するため、外部の VPS を VPN サーバーとし、ルータとの間にVPN を張ることでサーバーを公開できるようにした。外部のユーザーは、VPS に割り当てられたグローバル IP を通して、筆者の自宅内のサーバーに接続することができる。
構成
表題の通り、VPN には WireGuard を使う。高速に動作し、設定もシンプルらしい。
図 1 のように、ルーターには ISP からプライベート IP アドレス、10.167.X.Y/24 が割り当てられている。グローバル IP 203.0.113.1 が割り当てられた VPS とルーター間で 10.10.0.0/24 のサブネットで VPN を構築し、VPS に 10.10.0.1 、ルーターに 10.10.0.2 を割り当てる。VPS と ルーターのそれぞれで NAT を行い、LAN内のサーバーから公開するポートに外部から接続できるようにする。LAN 内では、ルーターに 192.168.1.1、公開するサーバーには 192.168.1.2 が割り当てられているものとする。
VPS の OS には Debian (bullseye) を使う。ルーターには OpenWRT (18.06.4) を使う。
なお、WireGuard はピアツーピア型の VPN であるため、本来どちらがサーバーであるという概念は存在しないが、便宜上 VPS 側をサーバー側と呼ぶことにする。
VPN サーバー (VPS) 側の設定
WireGuardのインストール
WireGuard をインストールする。
$ sudo apt install wireguard
鍵生成
サーバーの秘密鍵を生成する。
$ sudo wg genkey > server.key
秘密鍵から公開鍵を生成する。
$ cat server.key | sudo wg pubkey > server.pub
便宜上秘密鍵を server.key(.pub)
として書き出したが、値をファイルから読むことはないので、値を控えしだいファイルを消してしまっても構わない。
同様にして、ルーター側の鍵ペアも生成する。
$ sudo wg genkey > client.key
$ cat client.key | sudo wg pubkey > client.pub
IP転送の許可
サーバー内でのIP転送を許可する。/etc/sysctl.conf
を編集し、
net.ipv4.ip_forward=1
をアンコメントする。
$ sudo sysctl -p
として設定を反映させる。
設定
/etc/wireguard/[インターフェース名].conf
として設定ファイルを作成する。今回は wg0
とする。内容は以下の通り。
[Interface]
ListenPort = 51820
Address = 10.10.0.1/32
PrivateKey = [生成したサーバー側の秘密鍵]
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o [WAN向きのインターフェース名] -j MASQUERADE;
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o [WAN向きのインターフェース名] -j MASQUERADE;
SaveConfig = true
[Peer]
PublicKey = [生成したルーター側の公開鍵]
AllowedIPs = 10.10.0.0/24
PersistentKeepalive = 25
各項目の意味は以下の通り。
[Interface]
サーバー側の設定。
ListenPort: WireGuard が待ち受けるポート
Address: 作成するインターフェース (wg0) に割り当てるアドレス。/32
以外にすると通信できないので注意。
PrivateKey: サーバーが利用する秘密鍵
PostUp/PostDown: インターフェースの作成後/破棄後に実行させるコマンド。IPマスカレードを行い、VPN 内から外向きの通信を WAN 側インターフェースを経由して行えるようにしている。
SaveConfig: インターフェースを破棄した際に現在の WireGuard の設定をこのファイルに書き出す。true
にしていると、インターフェースを作成してから設定を変更しても、インターフェースを落とした際に設定が上書きされるので注意したい。[2]
[Peer]
各クライアントに対する設定。複数のクライアントを設定するには、Peer セクションを複数書けば良い。
PublicKey: 接続を許可するクライアントの公開鍵
AllowedIPs: クライアントに許可する IP レンジ。0.0.0.0/0 には設定しないことを強く勧める。デフォルトゲートウェイが変更され、(SSH等でも)サーバーに接続できなくなる可能性がある。 → 参照
PersistentKeepalive: 無通信でも Keep-Alive パケットを送る間隔(秒)。NAT 越えの場合、無通信で切断されないように 25 に設定することが推奨されているようだ [3]。今回は NAT 越えではないが、念のために入れておいた。
ルーター側の設定
Web インターフェースにログインし、以下を行う。
必要なプラグインのインストール
上部メニューの System -> Software でソフトウェア管理ページを開き、Available Packages の中から luci-app-wireguard
を探しインストールする。同様に luci-proto-wireguard
もインストールする。
インターフェースの追加
Network -> Interfaces から、Add new interface... ボタンをクリックしてインターフェースを追加する。以下の通り設定する。
- Name of the new interface: 追加するインターフェース名(
wg0
とする)を入力する。 - Protocol of the new interface: WireGuard VPN
インターフェースの設定画面が開く。以下の通り設定する。
- Private Key: 前のステップで生成したクライアント秘密鍵
- Listen Port: 51820
- IP Address: このルーターに割り当てる VPN 内の IP、10.10.0.2 を入力する。
Peers セクションの Add ボタンをクリックして、ピアを追加する。これはクライアントから見たピアなので、つまりサーバーのこと。
- Public Key: 前のステップで生成したサーバー公開鍵
- Allowed IPs: サーバー側に許可される IP レンジを入力する。すなわち 10.10.0.1/24
- Route Allowed IPs: 有効にする
- Endpoint Host / Port: サーバーの WAN 側 IP とポート番号を入力する。203.0.113.1 および 51820 とする。
- Persistent Keep Alive: 有効にする。
こちらから接続に行くのに、接続に行った先が許可される IP レンジを指定するというのも妙な話だが、上でも書いたようにピアツーピア型の VPN であるためだ。
上のタブから Firewall Settings を開く。
- Create / Assign firewall-zone: wan にする。[4]
Save & Apply ボタンを押して反映する。
接続の確認
ここまでで VPS とルータ間で接続が確立できているはずだ。Network -> Interface で、追加した wg0 がアクティブになったことや(図2)、Status -> Wireguard Status で Peer が表示され、Latest Handshake が直近の日次になっていることを確認する(図3)。また、VPS と ルーター、互いに VPN 内 IP で ping が通ることを確認する。
インターフェース一覧で wg0 にエラーが表示されている場合、Restart ボタンでインターフェースを再起動したり、ルーター本体を再起動すると直るかもしれない。
VPS 側でも
sudo wg
とすることで状態を確認できる。
ポートフォワードの設定
本記事の目的は、外部のユーザーが LAN 内のサーバーに接続できるようにすることである。つまり、ポートフォワードを設定しなければならない。今回の構成では、ルーター側で行う通常のポートフォワードに加え、最初に接続を受ける VPS 側でもポートフォワードを設定する必要がある。
VPS 側でのポートフォワードの設定
iptables で淡々とルールを追加する。例えば、TCP 80 に来た通信を LAN 内のサーバーで受け取れるようにするには、
$ sudo iptables -t nat -A PREROUTING -i [WAN向きのインターフェース名] -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.10.0.2;
$ sudo iptables -t nat -A POSTROUTING -m tcp -p tcp --dst 10.10.0.2 --dport 80 -j SNAT --to-source 10.10.0.1;
とする。上記のルールが VPN 生成/破棄時に自動で設定/削除されるように、wg0.conf
のPostUp
/ PostDown
に追加しておくと良い。直接書くと見づらいので、以下のような内容で /etc/wireguard/wg0-up.sh
と /etc/wireguard/wg0-down.sh
というファイルを作り、
wg0-up.sh
#!/bin/bash
iptables -t nat -A PREROUTING -i [WAN向きのインターフェース名] -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.10.0.2;
iptables -t nat -A POSTROUTING -m tcp -p tcp --dst 10.10.0.2 --dport 80 -j SNAT --to-source 10.10.0.1;
wg0-up.sh
#!/bin/bash
iptables -t nat -D PREROUTING -i [WAN向きのインターフェース名] -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.10.0.2;
iptables -t nat -D POSTROUTING -m tcp -p tcp --dst 10.10.0.2 --dport 80 -j SNAT --to-source 10.10.0.1;
PostUp
/ PostDown
で実行されるようにした。
wg0.conf
[Interface]
...
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o [WAN向きのインターフェース名] -j MASQUERADE; /etc/wireguard/wg0-up.sh
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o [WAN向きのインターフェース名] -j MASQUERADE; /etc/wireguard/wg0-down.sh
...
ルーター側でのポートフォワードの設定
Network -> Firewall 内の Port Forwards タブから設定できる。上記の HTTP を転送する例であれば、
- Protocol: TCP
- External Zone: wan
- External Port: 80
- Internal Zone: lan
- Internal IP Address: 192.168.1.2
- Internal Port: 80
上記のようなフォワードルールを作成する。図4. に作成した後の画面を示す。
まとめ
本記事では、Wireguard を使って VPS と OpenWRT 間で拠点間 VPN を構築し、グローバル IP が与えられない環境下で外部向けにサーバーを公開する方法を説明した。Wireguard は初めて使ったが、設定ファイルがとても簡素で分かりやすく、またハマりそうな要素も少なく、必要最低限の VPN を構築するには便利だと感じた。