WireGuard は VPN を構成するための一連の実装と通信プロトコル。 実装のコードベースが小さく、他の VPN ソフトウェアと比べて設定方法がシンプルという特徴がある。 今回は、その WireGuard を Linux の Network Namespace と一緒に試してみる。
使った環境は次のとおり。
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=22.04 DISTRIB_CODENAME=jammy DISTRIB_DESCRIPTION="Ubuntu 22.04.2 LTS" $ uname -srm Linux 5.15.0-69-generic x86_64
もくじ
下準備
あらかじめ必要なパッケージをインストールしておく。
WireGuard に直接関係するのは wireguard-tools
だけ。
$ sudo apt-get install wireguard-tools iproute2 tcpdump
WireGuard のカーネルモジュールがロードされていることを確認しておく。
$ lsmod | grep -i wireguard wireguard 94208 0 curve25519_x86_64 36864 1 wireguard libchacha20poly1305 16384 1 wireguard ip6_udp_tunnel 16384 1 wireguard udp_tunnel 20480 1 wireguard libcurve25519_generic 49152 2 curve25519_x86_64,wireguard
もしロードされていなければ modprobe する。
$ sudo modprobe wireguard
ネットワークを用意する
まずは Network Namespace を使ってネットワークを用意する。 なお、この工程ではまだ VPN とか暗号化は関係がない。 作るネットワークはセグメントが 1 つしかないシンプルなもの。 そこにホストとして Network Namespace が 2 つ繋がる。
はじめに peera
と peerb
という名前で 2 つの Network Namespace を作る。
$ sudo ip netns add peera $ sudo ip netns add peerb
2 つの Network Namespace の間を結線する Virtual Ethernet デバイスのインターフェイスを用意する。
$ sudo ip link add peera-veth0 type veth peer name peerb-veth0
インターフェイスの両端を Network Namespace に移動してリンクアップさせる。
$ sudo ip link set peera-veth0 netns peera $ sudo ip link set peerb-veth0 netns peerb $ sudo ip netns exec peera ip link set peera-veth0 up $ sudo ip netns exec peerb ip link set peerb-veth0 up
インターフェイスに IPv4 アドレスを付与しておく。 一応 MAC アドレスも変更しているが必須ではない。
$ sudo ip netns exec peera ip address add 192.0.2.1/24 dev peera-veth0 $ sudo ip netns exec peerb ip address add 192.0.2.2/24 dev peerb-veth0 $ sudo ip netns exec peera ip link set dev peera-veth0 address 00:00:5E:00:53:01 $ sudo ip netns exec peerb ip link set dev peerb-veth0 address 00:00:5E:00:53:02
これで Network Namespace 同士が L3 で疎通した。
$ sudo ip netns exec peera ping -c 3 192.0.2.2 -I 192.0.2.1 PING 192.0.2.2 (192.0.2.2) from 192.0.2.1 : 56(84) bytes of data. 64 bytes from 192.0.2.2: icmp_seq=1 ttl=64 time=0.079 ms 64 bytes from 192.0.2.2: icmp_seq=2 ttl=64 time=0.058 ms 64 bytes from 192.0.2.2: icmp_seq=3 ttl=64 time=0.057 ms --- 192.0.2.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2041ms rtt min/avg/max/mdev = 0.057/0.064/0.079/0.010 ms
WireGuard で VPN を構成する
ここからは、作成したネットワーク上に WireGuard で VPN を構成していく。
WireGuard が動作するには、各ノードごとに公開鍵ペアが必要になる。 まずは wg genkey コマンドを使って秘密鍵を生成する。
$ umask 077 $ wg genkey > peer-a-privatekey $ wg genkey > peer-b-privatekey
次に wg pubkey コマンドを使って秘密鍵から公開鍵を生成する。
$ wg pubkey < peer-a-privatekey > peer-a-publickey $ wg pubkey < peer-b-privatekey > peer-b-publickey
生成された鍵はいずれもテキストファイルになっている。 たとえば公開鍵ならこんな感じ。
$ cat peer-a-publickey FSofL/Slt+l1ga1hLaOq4yd4tJgNDWXX2dF1Q8Z4uRs= $ cat peer-b-publickey uhLVCdXY/dpVjYMCLAVHg5ye/afRrN5exDsFjBGO3C8=
公開鍵ペアができたら、次に WireGuard デバイスのインターフェイスを設定していく。
まずは Network Namespace の peera
に wg0
という名前で WireGuard インターフェイスを追加する。
$ sudo ip netns exec peera ip link add dev wg0 type wireguard
追加した WireGuard インターフェイスに IPv4 アドレスを付与する。 これが VPN トンネルの端点のアドレスになる。
$ sudo ip netns exec peera ip addr add 198.51.100.1/24 dev wg0
次に wg set コマンドを使って WireGuard インターフェイスに VPN 通信の受信用ポートと秘密鍵が格納されたファイルを設定する。 使うポートには特に決まりがないようだ。
$ sudo ip netns exec peera wg set wg0 listen-port 37564 private-key ./peer-a-privatekey
続いて VPN トンネルを張るピアに関する情報を設定する。 ここではピアの公開鍵とエンドポイント、送信元になる端点のアドレスなどを指定する。
$ sudo ip netns exec peera wg set wg0 peer $(cat peer-b-publickey) persistent-keepalive 25 allowed-ips 198.51.100.2/32 endpoint 192.0.2.2:37564
あとは WireGuard インターフェイスをリンクアップさせる。
$ sudo ip netns exec peera ip link set wg0 up
wg コマンドで設定されている内容を確認できる。
$ sudo ip netns exec peera wg interface: wg0 public key: FSofL/Slt+l1ga1hLaOq4yd4tJgNDWXX2dF1Q8Z4uRs= private key: (hidden) listening port: 37564 peer: uhLVCdXY/dpVjYMCLAVHg5ye/afRrN5exDsFjBGO3C8= endpoint: 192.0.2.2:37564 allowed ips: 198.51.100.2/32 latest handshake: 25 seconds ago transfer: 564 B received, 688 B sent persistent keepalive: every 25 seconds
peera
と対になる設定を peerb
にも設定する。
$ sudo ip netns exec peerb ip link add dev wg0 type wireguard $ sudo ip netns exec peerb ip addr add 198.51.100.2/24 dev wg0 $ sudo ip netns exec peerb wg set wg0 listen-port 37564 private-key ./peer-b-privatekey $ sudo ip netns exec peerb wg set wg0 peer $(cat peer-a-publickey) persistent-keepalive 25 allowed-ips 198.51.100.1/32 endpoint 192.0.2.1:37564 $ sudo ip netns exec peerb ip link set wg0 up $ sudo ip netns exec peerb wg interface: wg0 public key: uhLVCdXY/dpVjYMCLAVHg5ye/afRrN5exDsFjBGO3C8= private key: (hidden) listening port: 37564 peer: FSofL/Slt+l1ga1hLaOq4yd4tJgNDWXX2dF1Q8Z4uRs= endpoint: 192.0.2.1:37564 allowed ips: 198.51.100.1/32 latest handshake: 49 seconds ago transfer: 572 B received, 596 B sent persistent keepalive: every 25 seconds
これで VPN トンネルが張られた。
動作を確認する
それでは、実際にトンネルの端点間で ping を打ってみよう。
$ sudo ip netns exec peera ping -c 3 198.51.100.2 -I 198.51.100.1 PING 198.51.100.2 (198.51.100.2) from 198.51.100.1 : 56(84) bytes of data. 64 bytes from 198.51.100.2: icmp_seq=1 ttl=64 time=0.371 ms 64 bytes from 198.51.100.2: icmp_seq=2 ttl=64 time=0.740 ms 64 bytes from 198.51.100.2: icmp_seq=3 ttl=64 time=1.09 ms --- 198.51.100.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2007ms rtt min/avg/max/mdev = 0.371/0.732/1.086/0.291 ms
ちゃんと疎通があるようだ。
パケットキャプチャもしてみよう。
ping を打ちっぱなしにした状態で peerb
の wg0
インターフェイスをキャプチャする。
すると、次のように平文の ICMP パケットが流れている。
$ sudo ip netns exec peerb tcpdump -tnl -i wg0 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on wg0, link-type RAW (Raw IP), snapshot length 262144 bytes IP 198.51.100.1 > 198.51.100.2: ICMP echo request, id 39464, seq 1, length 64 IP 198.51.100.2 > 198.51.100.1: ICMP echo reply, id 39464, seq 1, length 64 IP 198.51.100.1 > 198.51.100.2: ICMP echo request, id 39464, seq 2, length 64 IP 198.51.100.2 > 198.51.100.1: ICMP echo reply, id 39464, seq 2, length 64 IP 198.51.100.1 > 198.51.100.2: ICMP echo request, id 39464, seq 3, length 64 IP 198.51.100.2 > 198.51.100.1: ICMP echo reply, id 39464, seq 3, length 64
一方で peerb-veth0
インターフェイスをキャプチャすると UDP:37564 ポートで通信がやり取りされている。
これは、WireGuard がトランスポート層のプロトコルに UDP を使うため。
つまり、これが VPN でやり取りされる暗号化されたパケットということになる。
$ sudo ip netns exec peerb tcpdump -tnl -i peerb-veth0 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on peerb-veth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes IP 192.0.2.1.37564 > 192.0.2.2.37564: UDP, length 128 IP 192.0.2.2.37564 > 192.0.2.1.37564: UDP, length 128 IP 192.0.2.2.37564 > 192.0.2.1.37564: UDP, length 148 IP 192.0.2.1.37564 > 192.0.2.2.37564: UDP, length 92 IP 192.0.2.2.37564 > 192.0.2.1.37564: UDP, length 32 IP 192.0.2.1.37564 > 192.0.2.2.37564: UDP, length 128 IP 192.0.2.2.37564 > 192.0.2.1.37564: UDP, length 128 IP 192.0.2.1.37564 > 192.0.2.2.37564: UDP, length 128 IP 192.0.2.2.37564 > 192.0.2.1.37564: UDP, length 128
いじょう。