如何搭建多节点间路由的WireGuard

参考的文章已经讲的特别清晰了, 本篇主要是把原文里简述的部分稍微展开描述.

Easier Setup

本文中提到的Chain功能, 可通过wg-ops工具更简单的实现. 使用该工具可避免一些常见的容易犯错的点.

Step-by-Step Setup

1. 搭建Client

peer为NodeA, allowed-ips设为0.0.0.0/0, 即全部流量. 假设网卡名称为wg0. 如果不想转发本地流量, 请设置allowed-ips为0.0.0.0/1, 128.0.0.0/1

注意当allowed-ips设为全部流量转发的时候, 可能需要添加DNS选项才能正确解析. DNS解析请求会走隧道.

2. 搭建Node A

先与client建立连接, allowed-ips设为client的ip即可 (按需设置). 假设网卡名称为wg1

再与NodeB建立连接, allowed-ips设为NodeA的ip + Client的ip. (按需设置). 假设网卡名称为wg2

3. 搭建Node B

与NodeA建立连接, allowed-ips设为NodeA的ip. (按需设置). 假设网卡名称为wg3

4. 在NodeA上配置转发

1
2
3
echo "1 middleman" >> /etc/iproute2/rt_tables
ip route add 0.0.0.0/0 dev wg2 table middleman
ip rule add from <NodeA对Client的allowed-ips> lookup middleman

注意此处ip rulefrom要配置为NodeA原本对Client的allowed-ips, 从而让Client来的流量可以按照middleman表来进行路由.

1
wg set wg2 peer <NodeB的公钥> allowed-ips 0.0.0.0/0

这句话主要是将面向NodeB的wg2接口的WireGuard路由调整为0.0.0.0/0. 注意, 此处并没有调整系统路由表. 所以如果用ip route命令查看的时候, 当前系统里原本面向NodeB的路由还是原来的路由, 因此NodeA上的其他流量不会被全部转发到NodeB.

1
sysctl net.ipv4.ip_forward=1

启动IP转发.

5. 在NodeB上配置转发

1
sysctl net.ipv4.ip_forward=1

启动IP转发.

1
2
iptables -A FORWARD -i wg3 -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

调整iptables规则, 允许来自wg3的流量转发. 第二句指定系统将eth0网卡作为转发流量的出口. 如果物理网卡名字不同需要调整为目标网卡名称. (否则不会报错, 但是没有效果)

完成以上配置后即可达到流量转发的效果!

可以通过traceroute命令来验证.

选择性转发配置

如果想让来自Client的流量只经过NodeA转发, 但是某些想经过NodeB转发, 那么在step-by-step里第4步, ip route命令改为:

1
ip route add <想经过NodeB路由的IP> dev wg2 table middleman

除此之外还要在NodeA上调整iptables从而允许流量转发至互联网:

1
2
iptables -A FORWARD -i wg2 -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

更多/更少节点时如何配置

更多节点: 在靠近Client端的节点按照NodeA配置, 在靠近互联网的节点按照NodeB配置, 中间的节点只需要按照NodeA配置即可.

更少节点: 如果Client只经过一跳就到达互联网, 则不需要NodeA的ip ruleip route配置, 只需要在NodeA上配置允许转发到互联网的iptables规则即可.

配置样例

Client

1
2
3
4
5
6
7
8
9
[Interface]
PrivateKey = ...
Address = 10.100.0.2/32

[Peer]
PublicKey = ...
AllowedIPs = 0.0.0.0/1, 128.0.0.0/1
Endpoint = ...
PersistentKeepalive = 30

NodeA

wg0 (面向客户端)

1
2
3
4
5
6
7
8
[Interface]
PrivateKey = ...
Address = 10.100.0.1
ListenPort = 51820

[Peer]
PublicKey = ...
AllowedIPs = 10.100.0.2

wg1 (面向下一个节点)

1
2
3
4
5
6
7
8
9
10
[Interface]
PrivateKey = ...
Address = 10.200.0.2
ListenPort = 51821

[Peer]
PublicKey = ...
AllowedIPs = 10.200.0.1
Endpoint = ...
PersistentKeepalive = 30

NodeB

1
2
3
4
5
6
7
8
[Interface]
PrivateKey = ...
Address = 10.200.0.1
ListenPort = 51820

[Peer]
PublicKey = ...
AllowedIPs = 10.200.0.0/24, 10.100.0.0/24

这里要注意, 因为我们没有在NodeA上配置iptables规则让来自10.100.0.0/24网段的IP包以10.200.0.2为源地址发出, 所以我们要在NodeB上配置允许IP来自10.100.0.0/24网段, 尽管在NodeB与NodeA之间使用的是100.200.0.0/24网段.

参考

Wireguard VPN: Chained Setup - The poetry of (in)security ——这篇写的真的太棒了

Wireguard VPN: Typical Setup - The poetry of (in)security

iamckn/chained-wireguard-ansible - GitHub

nealfennimore/wireguard.conf - GitHubGist

traceroute(8) — Linux manual page

ip-route(8) — Linux manual page

ip-rule(8) — Linux manual page