WireGuardで拠点間VPNを設定してみた

はじめに

WireGuardを使って、自宅とOracle Cloudの間で拠点間VPNを構築してみました。
Oracle Cloud上のVMから自宅サーバーのDBにアクセスする際などにセキュアな通信ができ、非常に便利です。
構築した際の手順やハマったポイントなどを記事に残しておこうと思います。

環境

  • 自宅(172.16.0.0/23)
  • Oracle Cloud(10.0.0.0/8)
    • VM2(10.0.0.2) amd64
    • VM3(10.0.0.3) arm64

VMのOSはいずれもUbuntu 20.04です。
今回はVM1とVM2の間でVPNを張り、PC1からVM2やVM3へアクセスできるようにしました。
もちろん、VM3からPC1へのアクセスもできます。

WireGuardの設定

インストール

WireGuardはaptでシュッとインストールできます。

$ sudo apt install wireguard

VM2側の設定

VM2はサーバー側です。
WireGuardの設定ファイルである /etc/wireguard/wg0.conf に以下のような設定を書きます。
基本的にはドキュメントのとおりに設定すればOKです。

[Interface]
Address = 192.168.1.1/24
MTU = 8920
SaveConfig = true
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE; iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE; iptables -t nat -D POSTROUTING -s 192.168.1.0/24 -j MASQUERADE
ListenPort = 51820
PrivateKey = <Your PrivateKey>

[Peer]
PublicKey = <Your PublicKey>
AllowedIPs = 192.168.1.2/32, 172.16.0.0/23
Endpoint = 125.30.20.110:43297

PeerのAllowedIPsには自宅側のネットワークのセグメントを書いてあげます。
また、MTUを8920に設定してあげます。
設定を記入したら $ sudo wg-quick up client でWireGuardの設定を反映させます。

VM1側の設定

VM1はクライアント側です。
/etc/wireguard/client.conf に以下のような設定を書きます。
こちらも基本的にはドキュメントのとおりに設定すればOKです。

[Interface]
PrivateKey = <Your PrivateKey>
Address = 192.168.1.2
DNS = 8.8.8.8
MTU = 1350

[Peer]
PublicKey = <Your PublicKey>
AllowedIPs = 10.0.0.0/8, 192.168.1.0/24
Endpoint = <VM2 Global IP Address>:51820
PersistentKeepalive = 10

AllowedIPsにはOracle Cloud側のネットワークのセグメントを書いてあげます。
また、DNSとMTUの設定も追記します。
設定を記入したら $ sudo wg-quick up wg0 でWireGuardの設定を反映させます。

ファイアウォール・NATの設定

VM2はOracle CloudのVMでデフォルトで使われているiptablesの設定を変更します。
以下の設定を追記し、WireGuardがlistenしているポートに対してのリクエストを許可します。

-A INPUT -p udp -m state --state NEW -m udp --dport 51820 -j ACCEPT

また、VPN経由でのpingに応答できるようにするため、以下の行をコメントアウトします。

- -A FORWARD -j REJECT --reject-with icmp-host-prohibited
+ # -A FORWARD -j REJECT --reject-with icmp-host-prohibited 

全て設定したら、 $ sudo systemctl restart iptables.service で設定を反映させます。
VM1の方は、ufwを使っていたので、以下のコマンドで設定を追加しました。

$ sudo ufw route allow in on eth0 out on client from 172.16.0.0/23

設定を追加したら、 $ sudo ufw reload で設定を反映させます。

Oracle Cloudのファイアウォールの設定

セキュリティ・リストに 0.0.0.0/0からの51820/UDPへのリクエストを許可する設定を追加します。
また、10.0.0.0/8からのICMPのリクエストも許可するようにします。

ルーターのスタティックルートの設定

Oracle Cloudではルート・ルールに、172.16.0.0/23宛の通信を全て10.0.0.2に向ける設定を追加します。
設定欄には、以下のように入力します。

  • ターゲット・タイプ: プライベートIP
  • 宛先タイプ: CIDRブロック
  • 宛先CIDRブロック: 172.16.0.0/23
  • ターゲット選択: 10.0.0.2 (VM2のプライベートIPアドレス)

また、

プライベートIPをターゲットとするルート・ルールの場合は、最初に、プライベートIPが割り当てられているVNICで「ソース/宛先チェックのスキップ」を有効にする必要があります。

とあるように、VM2のVNICの設定で「ソース/宛先チェックのスキップ」を有効に設定しておきます。
一方自宅側は、ルーターにスタティックルートの設定を追加します。
自宅側の各VMやPCに直接スタティックルートの設定をしてもいいですが、ルーターで一括で設定したほうが楽です。
私の家では、NECのIX2105を使っているので、それを例に説明します。
ルーターのコンフィグに以下の設定を追加します。

ip route 10.0.0.0/8 172.16.0.11 (VM1のプライベートIPアドレス)

これで、自宅側のネットワークから10.0.0.0/8宛にパケットを送信した際は、デフォルトゲートウェイであるルーターに到達し、ルーティング設定に基づいてVM1に転送されるようになります。
一通り設定を終えたら、相互にpingを送ったり、Oracle CloudのVMに対して、自宅のネットワークからプライベートアドレスでSSHを繋いだりできるようになっているはずです。
最終的なパケットの流れは、以下の図のようになります。
f:id:sublimer:20211128163658p:plain

ハマったポイント

MTUのサイズの設定

最初にWireGuard経由でSSHで接続しようとしたところ、なかなか接続できず、タイムアウトしてしまいました。
sshコマンド実行時に -v オプションを付けて接続したところ、 expecting SSH2_MSG_KEX_ECDH_REPLYというメッセージが出たところで止まっていました。
このメッセージで調べたところ、どうやらMTUのサイズが小さい場合に発生するということがわかりました。
また、WireGuardの設定にはMTUのサイズの設定もあることがわかりました。
WireGuardの設定ファイルにMTUサイズの設定も追加したところ、無事にSSHがつながるようになりました。

Oracle Cloudのセキュリティ・リストに関して

最初、セキュリティ・リストはインターネットから仮想クラウド・ネットワーク(VCN)に対してのリクエストに適用されるものだと思っていました。
今回はVPNさえつながってしまえばあとはVCN内の通信(プライベートアドレスでの通信)なので、ICMPのリクエストは通ると思っていましたが、設定を追加しないとリクエストが通りませんでした。
どうやら、セキュリティ・リストはVNIC共通のファイアウォールのルールのようなもので、インターネットからの通信だけでなく、VCN内の他のVMからの通信にも適用されるようです。

おわりに

設定をした際は、なかなかping応答が来なかったり、SSHがつながらなかったりとつまずくポイントがいくつかありました。
ただ、パケットの気持ちを考え、どこまでパケットが来てどこで止まっているのかを地道に追うことで無事に疎通するようになりました。
やはり、パケットの気持ちを考えるのは大切ですね。