megrxu

使用 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
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

更详细的说明可参阅文档