Await 做些相當庸俗的夢

使用 WireGuard 配置一個 IPv6 Tunnel

WireGuard 可以用來建立安全的點對點的連線。INRIA 的研究人員於 2019 五月發表了對於該協議的一份機器證明

We contribute proofs for correctness, message secrecy, forward secrecy, mutual authentication, session uniqueness, and resistance against key compromise impersonation, identity mis-binding, and replay attacks.

更換了實驗室(地點)以後,喪失了連線到教育網 IPv6 的能力,只能在寢室或者是使用跳板伺服器訪問,非常不方便。於是想透過 WireGuard 建立一個從實驗室電腦到機房伺服器的隧道,實現在實驗室電腦上也能訪問公網 IPv6。

安裝

實驗室電腦是 Arch Linux,核心用的是官方核心,因此裝好 wireguard-toolswireguard-arch 即可。(更新:如果使用較新的核心, wireguard 模組預設已被啟用,則不再需要安裝 DKMS 核心模組,但如果是使用的其他版本的核心,則還可能需要安裝對應版本的軟體包)

在 Ubuntu 上則需要自己新增相應的源再安裝:

1
2
sudo add-apt-repository ppa:wireguard/wireguard
sudo apt install wireguard

配置

配置的方法網路上已經有很多教程,比如 WireGuard 的官方教程,Linode 的教程和 Arch Linux Wiki 上的教程,都比較靠譜,並且大同小異。

另外,如果沒有裝 linux-headers 的話,在 ip link add dev wg0 type wireguard 時會報 Error: Unknown device type. 錯誤,務必裝上自己核心版本對應的 headers 。 (更新:接前文更新,如果不再需要安裝 DKMS 的 wireguard 核心模組,則安裝核心標頭檔案也是不必須的)

原生配置檔案中的欄位副作用

一個教訓是:Peers 塊中的 AllowedIPs 不能隨意設定

  • 如果是 Server 端的配置,需要允許擁有對應公鑰的裝置 IP ,並且設定好掩碼。
  • 如果是 Client 端的配置,看情況配置:
    • 只是為了異地組網,把不同機器連起來,則只需要填入該子網的 IP 段
    • 想要訪問內(外)網路,Server 在內(外)網,則需要將要訪問的對應的內(外)網 IP 段填入。
      • 經典的配置就是 0.0.0.0/0 , ::/0,本機上所有流量都被轉發給伺服器了。(伺服器作為一個 VPN Server)
      • 如果想在外地連入校內網,則可以寫 10.0.0.0/8

AllowIPs 這個欄位通常彷彿都是人畜無害的(只是來個流量再判斷一下 IP 決定是否 drop 嘛)。但是在 WireGuard 上是有副作用的:它會新增路由,以便能夠使用 WireGuard 建立的那個網路。

所以,想象一下,如果在 Server 上設定了 AllowedIPs = 0.0.0.0, ::/0,會發生什麼?

答案
  • 如果沒有別的網絡卡,伺服器上的幾乎所有區域網 IPv4 流量都會透過 wg。
  • 而此時,大機率已經無法連線到該伺服器了。

    您好!我想借用一下機房的鑰匙。

最終配置檔案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Server
[Interface]
ListenPort = [PORT]
PrivateKey = <PrivateKey>
Address = 192.168.0.2/24, fd23:23:23::2/64
# 用 iptables 設定包轉發,注意將 <dev-name> 替換為 Server 上對應的網絡卡裝置名字
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o <dev-name> -j MASQUERADE; ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o <dev-name> -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o <dev-name> -j MASQUERADE; ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o <dev-name> -j MASQUERADE

[Peer]
PublicKey = <PublicKey>
# 只允許一個 IP
AllowedIPs = 192.168.0.3/32, fd23:23:23::3/128
Endpoint = <IP>:[PORT]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Client
[Interface]
ListenPort = [PORT]
PrivateKey = <PrivateKey>
Address = 192.168.0.3/24, fd23:23:23::3/64

[Peer]
PublicKey = <PublicKey>
# IPv4 正常用就好了,目的只是 enable IPv6。
# 這裡我偷了一下懶,把所有 IPv6 流量都指向伺服器了,這樣一來客戶端本地鏈路上的 IPv6 就無法正常訪問了(不過也通常沒什麼用)。
AllowedIPs = 192.168.0.2/32, ::/0
Endpoint = <IP>:[PORT]

最後終於:

WG IPv6

Bonus: 遷移到 Systemd

後來我逐漸將自己的網路配置完全遷移到 systemd-networkd,根據 Arch Linux 優秀的文件,需要兩個檔案來配置 wireguard 連線。 其中 .netdev 檔案用於設定網路裝置,.network 用於配置路由等(此時 .netdev 中的 AllowedIPs 欄位終於幾乎對本機網路沒有副作用了,很合理)。摘錄如下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# /etc/systemd/network/99-<name>.netdev
[NetDev]
Name=<name>
Kind=wireguard
Description=WireGuard tunnel for Container Network

[WireGuard]
ListenPort=<port>
PrivateKey=<private-key>

[WireGuardPeer]
PublicKey=<public-key>
AllowedIPs=<ips>
Endpoint=<endpoint>

# /etc/systemd/network/99-<name>.network
[Match]
Name=<name>

[Network]
Address=<address>

[Route]
Destination=<destination>
Scope=link

更詳細的說明可參閱文件