前言
本文是对上一篇文章:WireGuard:局域网跨城通信 的补充。将围绕远程办公的开发者,即不在办公网内的开发者,如何基于 WireGuard® 跨城访问办公网中的服务。
场景
团队提倡远程办公,开发者位于全国各地。公司在某个城市设有办公地点,办公地点的局域网内自建了虚拟化,并部署了开发相关的一系列服务(GitLab,CI,K8s)。
- 不同城市的开发者需访问办公地点局域网中的多个服务。
- 不同城市的开发者之间需互相访问。
挑战
- 开发者如何稳定的跨公网访问局域网内所有设备(如同在办公地点的局域网内)。
- 局域网内的 WireGuard 节点位于运营商 NAT 设备之后,不支持 NAT 穿透且无公网 IP。
- 局域网内只运行一个 WireGuard 节点(而不是将所有服务加入 WireGuard 网络)。
- 如何解决局域网网关不支持静态路由规则的问题。
- 如何避免路由冲突。
架构
思路
假设有两个局域网 LAN1,LAN2 分别是公司在深圳(LAN1)和北京(LAN2)的办公网。
有三台服务器,z(IP 前的字母)是公网服务器(有公网 IP),a 和 c 是内网服务器(无公网 IP)。公网服务器(z)和内网服务器(a 和 c)组建了一个 WireGuard 网络,且三台服务器开启封包转发(IP Forward)。
e 和 f 是两个远程办公的开发者,其中 e 在广州,f 在上海,每个开发者是一个 WireGuard Client,通过各自的 WireGuard 客户端配置加入 WireGuard 网络。
开发者访问服务
假设广州的开发者 e(10.70.0.4)要访问北京办公网(LAN2)的中的设备 d(192.168.200.244)。
e 通过 WireGuard 客户端接入 WireGuard 网络。客户端与 WireGuard Server 建立连接,并向系统路由表写入相关路由规则(192.168.200.0/24 由 wg0 负责接收)。
e 的内核通过系统路由表匹配 192.168.200.0/24 由 wg0 负责接收,内核将数据包发给 wg0。WireGuard 收到数据包后,根据目的地址和内部路由表1,匹配 192.168.200.0/24 的目标 Peer 是 z,WireGuard 将数据包转发给 z。
公网服务器(z)收到请求后,z 的内核通过系统路由表匹配 192.168.200.0/24 由 wg0 负责接收,内核将数据包发给 wg0。WireGuard 收到数据包后,根据目的地址和内部路由表,匹配 192.168.200.0/24 的目标 Peer 是 c,WireGuard 将数据包转发给 c。
内网服务器(c)收到请求后,c 的内核通过系统路由表发现 192.168.200.244/32 不是自己(192.168.200.248),而是局域网中设备 d。因为开启了封包转发,c 将数据包转发给设备 d。
设备 d 收到数据包并处理,生成响应 e(10.70.0.4)的数据包。设备 d 通过默认网关(192.168.200.1)确认数据包的下一跳,默认网关中配置了静态路由规则(指定 10.70.0.0/16 的下一跳是 c),因此默认网关将数据包发给 c。
内网服务器(c)收到请求后,c 的内核通过系统路由表匹配 10.70.0.0/16 由 wg0 负责接收,内核将数据包发给 wg0。WireGuard 收到数据包后,根据目的地址和内部路由表实现,匹配 10.70.0.0/16 的目标 Peer 是 z,WireGuard 将数据包发给 z。
公网服务器(z)收到请求后,z 的内核通过系统路由表匹配 10.70.0.0/16 由 wg0 负责接收,内核将数据包发给 wg0。WireGuard 收到数据包后,根据目的地址和内部路由表实现,匹配 10.70.0.4/32 的目标 Peer 是 e,WireGuard 将数据包发给 e。
设备 e 收到请求后,发现目标 IP 就是自己,e 接收并处理来自 d 的数据包。
开发者访问开发者
假设广州的开发者的设备 e(10.70.0.4)要访问上海的开发者的设备 f(10.70.0.5)。
设备 e 的内核通过系统路由表匹配 10.70.0.0/16 由接口 wg0 负责接收,内核将数据包发给 wg0。WireGuard 收到数据包后,根据目的地址和内部路由表实现,匹配 10.70.0.0/16 的目标 Peer 是 z,WireGuard 将数据包发给 z。
公网服务器(z)收到请求后,z 的内核通过系统路由表匹配 10.70.0.0/16 由 wg0 负责接收,内核将数据包发给 wg0。WireGuard 收到数据包后,根据目的地址和内部路由表实现,匹配 10.70.0.5/32 的目标 Peer 是 f,WireGuard 将数据包转给 f。
设备 f 收到请求后,发现目标 IP 是自己,f 接收并处理来自 e 的数据包。
操作
部署 WireGuard 网络
因为 e 和 f 是开发者设备(MacOS/Windows),所以这里 e 和 f 的配置是 wg-quick 的 conf 文件,通过 WireGuard 的 GUI 客户端导入配置即可。
生成密钥对的命令如下(在开发本地执行如下命令即可创建出私钥、公钥、预共享密钥):
prikey="$(wg genkey)"; pubkey="$(wg pubkey <<< ${prikey})"; pskkey="$(wg genpsk)"
echo -e "PrivateKey: ${prikey}\nPublicKey: ${pubkey}\nPresharedKey: ${pskkey}"
需要注意的是,公网服务器只用到了公钥和私钥,PresharedKey 需跟客户端保持一致。比如客户端 e 的 PresharedKey 是 KK...GE=
,
那么在公网服务器上 e 对应的 Peer 内的 PresharedKey 就是 KK...GE=
。
e 的配置如下:
[Interface]
PrivateKey = WMWozvjIgpA2h75juoku2btWxbJ54i4Yt0A0RhpW7V8=
ListenPort = 51820
Address = 10.70.0.4/16
MTU = 1280
[Peer]
PublicKey = CmmeC0yqofMMZhEGHuK5dd2Mxyxe7tA8wSniDWiI5V0=
PresharedKey = KKQGN01IkZ1kJD2fAxtDZ6k5VFAI2fMca2q+SV7OrGE=
Endpoint = 39.101.166.124:51820
AllowedIPs = 10.70.0.0/16
AllowedIPs = 192.168.200.0/24
AllowedIPs = 192.168.201.0/24
PersistentKeepalive = 15
e 的密钥信息如下:
PrivateKey: WMWozvjIgpA2h75juoku2btWxbJ54i4Yt0A0RhpW7V8=
PublicKey: 0VooFEp8ZdpZp3P7BPa2W3piw+eu/6cyUGpSUWZp4HA=
PresharedKey: KKQGN01IkZ1kJD2fAxtDZ6k5VFAI2fMca2q+SV7OrGE=
f 的配置如下:
[Interface]
PrivateKey = mOUA5x3hfa4D3zwC1HpivQYH2f/z6Ltdpm4YeEQSFXg=
ListenPort = 51820
Address = 10.70.0.5/16
MTU = 1280
[Peer]
PublicKey = CmmeC0yqofMMZhEGHuK5dd2Mxyxe7tA8wSniDWiI5V0=
PresharedKey = pXt2C8EeBPF+y7izRv40GwYpEtaF625PRlp9kcTSzIM=
Endpoint = 39.101.166.124:51820
AllowedIPs = 10.70.0.0/16
AllowedIPs = 192.168.200.0/24
AllowedIPs = 192.168.201.0/24
PersistentKeepalive = 15
f 的密钥信息如下:
PrivateKey: mOUA5x3hfa4D3zwC1HpivQYH2f/z6Ltdpm4YeEQSFXg=
PublicKey: MyeHCD9sVXMXPgyEY79Yif8QskCjqm+SoJqmLXHN1RM=
PresharedKey: pXt2C8EeBPF+y7izRv40GwYpEtaF625PRlp9kcTSzIM=
公网服务器(z)配置:
文件:/etc/systemd/network/wg0.netdev
(WireGuard 内部路由表规则通过这份文件中的 AllowedIPs 来指定)
[NetDev]
Name=wg0
Kind=wireguard
MTUBytes=1280
[WireGuard]
ListenPort=51820
PrivateKey=qJwN1zu5alr89c6k18U4SUwZXnUgVhr/tvSgLM7rmHQ=
# LAN1 WireGuard Client
[WireGuardPeer]
PublicKey=u4vkZywF9hZcy5QUhcbOCjD2EZ7GL2yXgoAH+IoIbHE=
PresharedKey=IBAsO/DwbautQjq/Gksv3lFJZwnUXvkMR3GyxeelS4o=
AllowedIPs=10.70.0.2/32
AllowedIPs=192.168.201.0/24
PersistentKeepalive=15
# LAN2 WireGuard Client
[WireGuardPeer]
PublicKey=/d8UR69JrdjxC2Fo9hvfZaoSC35LSGNLJ5mqjEyWjRo=
PresharedKey=YdpxayZdKMeoHzo9fVvBSWoDTn1c++r5awhm28Is9Ic=
AllowedIPs=10.70.0.3/32
AllowedIPs=192.168.200.0/24
PersistentKeepalive=15
# Remote Developer e
[WireGuardPeer]
PublicKey=0VooFEp8ZdpZp3P7BPa2W3piw+eu/6cyUGpSUWZp4HA=
# 公网服务器上的 PresharedKey 需要跟客户端保持一致
PresharedKey=KKQGN01IkZ1kJD2fAxtDZ6k5VFAI2fMca2q+SV7OrGE=
AllowedIPs=10.70.0.4/32
PersistentKeepalive=15
# Remote Developer f
[WireGuardPeer]
PublicKey=MyeHCD9sVXMXPgyEY79Yif8QskCjqm+SoJqmLXHN1RM=
# 公网服务器上的 PresharedKey 需要跟客户端保持一致
PresharedKey=pXt2C8EeBPF+y7izRv40GwYpEtaF625PRlp9kcTSzIM=
AllowedIPs=10.70.0.5/32
PersistentKeepalive=15
文件:/etc/systemd/network/wg0.network
(系统级的路由表规则通过这份文件指定)
[Match]
Name=wg0
[Network]
Address=10.70.0.1/16
[Route]
Destination=192.168.200.0/24
[Route]
Destination=192.168.201.0/24
添加好配置请执行:systemctl restart systemd-networkd
重启 systemd-networkd。
z 的密钥信息如下:
PrivateKey: qJwN1zu5alr89c6k18U4SUwZXnUgVhr/tvSgLM7rmHQ=
PublicKey: CmmeC0yqofMMZhEGHuK5dd2Mxyxe7tA8wSniDWiI5V0=
PresharedKey: <不需要>
内网服务器(a)配置:
文件:/etc/systemd/network/wg0.netdev
[NetDev]
Name=wg0
Kind=wireguard
MTUBytes=1280
[WireGuard]
ListenPort=51820
PrivateKey=gNCkDUdQ3yBynzEST9XOd31NsPluF3Q7aeFu82QsV04=
[WireGuardPeer]
PublicKey=CmmeC0yqofMMZhEGHuK5dd2Mxyxe7tA8wSniDWiI5V0=
PresharedKey=IBAsO/DwbautQjq/Gksv3lFJZwnUXvkMR3GyxeelS4o=
Endpoint=39.101.166.124:51820
AllowedIPs=10.70.0.0/16
PersistentKeepalive=15
文件:/etc/systemd/network/wg0.network
[Match]
Name=wg0
[Network]
Address=10.70.0.2/16
添加好配置请执行:systemctl restart systemd-networkd
重启 systemd-networkd。
a 的密钥信息如下:
PrivateKey: gNCkDUdQ3yBynzEST9XOd31NsPluF3Q7aeFu82QsV04=
PublicKey: u4vkZywF9hZcy5QUhcbOCjD2EZ7GL2yXgoAH+IoIbHE=
PresharedKey: IBAsO/DwbautQjq/Gksv3lFJZwnUXvkMR3GyxeelS4o=
内网服务器(c)配置:
文件:/etc/systemd/network/wg0.netdev
[NetDev]
Name=wg0
Kind=wireguard
MTUBytes=1280
[WireGuard]
ListenPort=51820
PrivateKey=SF6RqNRo1c9vwFVteFonRwpbU1Ee1zsKp6v9c8pXwnA=
[WireGuardPeer]
PublicKey=CmmeC0yqofMMZhEGHuK5dd2Mxyxe7tA8wSniDWiI5V0=
PresharedKey=YdpxayZdKMeoHzo9fVvBSWoDTn1c++r5awhm28Is9Ic=
Endpoint=39.101.166.124:51820
AllowedIPs=10.70.0.0/16
PersistentKeepalive=15
文件:/etc/systemd/network/wg0.network
[Match]
Name=wg0
[Network]
Address=10.70.0.3/16
添加好配置请执行:systemctl restart systemd-networkd
重启 systemd-networkd。
c 的密钥信息如下:
PrivateKey: SF6RqNRo1c9vwFVteFonRwpbU1Ee1zsKp6v9c8pXwnA=
PublicKey: /d8UR69JrdjxC2Fo9hvfZaoSC35LSGNLJ5mqjEyWjRo=
PresharedKey: YdpxayZdKMeoHzo9fVvBSWoDTn1c++r5awhm28Is9Ic=
封包转发
为了实现局域网级别的通信,我们需要开启三台服务器 a/c/z(WireGuard 节点)的封包转发,在服务器上分别执行一遍如下命令:
# sysctl 配置文件名开头的数字会影响优先级
# 为了确保配置项的确有效,请在最后执行:systemd-analyze cat-config sysctl.d 进行验证
echo -e 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/10-ip-forward.conf
sysctl -p /etc/sysctl.d/10-ip-forward.conf
添加静态路由规则
为了能让 e 访问到 d,我们需要在 d 所在局域网(LAN2)的默认网关上,添加如下静态路由规则(我的测试环境的网关不支持静态路由规则,所以我直接在 d 上添加路由规则):
ip route add 10.70.0.0/16 via 192.168.200.248
同理,在 LAN1 的默认网关上,添加如下静态路由规则:
ip route add 10.70.0.0/16 via 192.168.201.247
e 和 f 就可以跟 LAN1/LAN2 中的所有设备互通了。
测试
d 的 IP 地址: 192.168.200.244,在 e 和 f 上测试能否访问 d:
ping 192.168.200.244
效果如下:
安全加固
由于连上 WireGuard 网络即可以访问 192.168.200.0/24(192.168.201.0/24)中的所有设备,这对于局域网是种风险。这里可以通过在内网服务器(a 和 c)上添加防火墙规则,限制公网服务器主动访问局域网(下面两种安全加固的方式二选一即可)。
添加了这些防火墙规则后,可以防止 WireGuard Server 节点(以及其他不在白名单内的节点)主动访问局域网。
iptables 安全加固
# 只允许 10.70.0.3 和 10.70.0.4 访问局域网,你可以按照实际情况,添加更多的 WireGuard Client's VPN IP 进来
iptables -t filter -I FORWARD 1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -t filter -I FORWARD 2 -s 10.70.0.3/32 -m conntrack --ctstate NEW -j ACCEPT
iptables -t filter -I FORWARD 2 -s 10.70.0.4/32 -m conntrack --ctstate NEW -j ACCEPT
iptables -t filter -I FORWARD 10 -j DROP
firewalld 安全加固
保存到:/etc/firewalld/direct.xml
内,然后执行:firewall-cmd --reload
即可。
<?xml version="1.0" encoding="utf-8"?>
<direct>
<rule ipv="ipv4" table="filter" chain="FORWARD" priority="1">-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT</rule>
<rule ipv="ipv4" table="filter" chain="FORWARD" priority="2">-s 10.70.0.3/32 -m conntrack --ctstate NEW -j ACCEPT</rule>
<rule ipv="ipv4" table="filter" chain="FORWARD" priority="2">-s 10.70.0.4/32 -m conntrack --ctstate NEW -j ACCEPT</rule>
<rule ipv="ipv4" table="filter" chain="FORWARD" priority="10">-j DROP</rule>
</direct>
常见问题
网关不支持静态路由规则
当局域网的网关不支持静态路由规则时,可以在局域网 WireGuard 节点(架构图中的 a 和 c)上开启 MASQUERADE,在转发时修改数据包的来源地址,从而实现静态路由规则的效果(下面两种配置 MASQUERADE 的方式二选一即可)。
下面的配置以 LAN2 中的内网服务器(c)作为例子,你需要根据你自己的实际情况进行调整(比如局域网网段,物理网卡名字等)。
iptables 配置 MASQUERADE
iptables -t nat -A POSTROUTING -s 10.70.0.0/16 -d 192.168.200.0/24 -o eth2 -j MASQUERADE
firewalld 配置 MASQUERADE
开启 public Zone 和 wireguard Zone 的 Forward 功能,然后将物理网卡(eth2) 和 wg0 放在一个 Zone 内(假设叫 wireguard):
wireguard (active)
...
interfaces: eth2 wg0
forward: yes
masquerade: no
...
在 public Zone 添加 rich rule:
public (default, active)
...
sources: 192.168.200.0/24
services: dhcpv6-client ssh wireguard
forward: yes
masquerade: no
...
rich rules:
rule family="ipv4" source address="10.66.0.0/16" destination address="192.168.200.0/24" masquerade
即可通过 firewalld 实现 MASQUERADE。
测试 MASQUERADE 效果
在远程开发者 e 的设备上测试 WireGuard 网络是否正常。确保 e 可以通过 VPN IP 访问公网服务器 z 和内网服务器 c:
# 访问公网服务器 z 的 VPN IP:
ping 10.70.0.1 -c 2
# PING 10.70.0.1 (10.70.0.1) 56(84) bytes of data.
# 64 bytes from 10.70.0.1: icmp_seq=1 ttl=64 time=48.9 ms
# 64 bytes from 10.70.0.1: icmp_seq=2 ttl=64 time=48.2 ms
# 访问内网服务器 c 的 VPN IP:
ping 10.70.0.3 -c 2
# PING 10.70.0.3 (10.70.0.3) 56(84) bytes of data.
# 64 bytes from 10.70.0.3: icmp_seq=1 ttl=63 time=96.8 ms
# 64 bytes from 10.70.0.3: icmp_seq=2 ttl=63 time=96.4 ms
确保内网服务器 c 开启了 MASQUERADE(假设通过 iptables 方式配置 MASQUERADE):
iptables-save -t nat
# *nat
# :PREROUTING ACCEPT [1189:501372]
# :INPUT ACCEPT [5:1627]
# :OUTPUT ACCEPT [4:732]
# :POSTROUTING ACCEPT [5:816]
# -A POSTROUTING -s 10.70.0.0/16 -d 192.168.200.0/24 -o eth2 -j MASQUERADE
# COMMIT
在 e 上执行测试命令的同时在 d 上抓包:
# 在 e 上测试:
ping 192.168.200.244 -c 5
# PING 192.168.200.244 (192.168.200.244) 56(84) bytes of data.
# 64 bytes from 192.168.200.244: icmp_seq=1 ttl=62 time=98.3 ms
# 64 bytes from 192.168.200.244: icmp_seq=2 ttl=62 time=97.1 ms
# 在 d 上抓包:
tcpdump -i any -n icmp
# 04:38:02.622391 eth2 In IP 192.168.200.248 > 192.168.200.244: ICMP echo request, id 16, seq 1, length 64
# 04:38:02.622428 eth2 Out IP 192.168.200.244 > 192.168.200.248: ICMP echo reply, id 16, seq 1, length 64
# 04:38:03.621300 eth2 In IP 192.168.200.248 > 192.168.200.244: ICMP echo request, id 16, seq 2, length 64
# 04:38:03.621325 eth2 Out IP 192.168.200.244 > 192.168.200.248: ICMP echo reply, id 16, seq 2, length 64
从 d 上抓的包可以看到,来源地址是内网服务器 c 的内网 IP 地址(192.168.200.248)而不是远程开发者 e 的 VPN IP(10.70.0.4)。因此 MASQUERADE 确认生效了。
路由冲突
路由冲突发生在远程办公的开发者来到办公地点时。此时远程办公开发者的设备内,会出现重复的路由规则。导致局域网流量绕行公网。
首先模拟一下远程办公的开发者 e 通过 WireGuard 访问办公网设备 d 的场景,这里通过 iperf3 测试一下线路的速度,结果如下:
# 在 e 上:
iperf3 -c 192.168.200.244 -i 5 -n 1G
# [ 5] local 10.70.0.4 port 46172 connected to 192.168.200.244 port 5201
# [ ID] Interval Transfer Bitrate Retr Cwnd
# [ 5] 0.00-5.01 sec 21.0 MBytes 35.2 Mbits/sec 7561 333 KBytes
# [ 5] 5.01-10.01 sec 15.4 MBytes 25.8 Mbits/sec 17 277 KBytes
# [ 5] 10.01-15.00 sec 13.8 MBytes 23.1 Mbits/sec 0 319 KBytes
# [ 5] 15.00-20.00 sec 19.1 MBytes 32.1 Mbits/sec 229 289 KBytes
# [ 5] 20.00-25.00 sec 15.2 MBytes 25.6 Mbits/sec 0 323 KBytes
# [ 5] 25.00-30.01 sec 15.5 MBytes 26.0 Mbits/sec 9 323 KBytes
# 在 d 上:
iperf3 -s -i 5
# Accepted connection from 192.168.200.248, port 46160
# [ 5] local 192.168.200.244 port 5201 connected to 192.168.200.248 port 46172
# [ ID] Interval Transfer Bitrate
# [ 5] 0.00-5.01 sec 18.4 MBytes 30.8 Mbits/sec
# [ 5] 5.01-10.01 sec 14.2 MBytes 23.9 Mbits/sec
# [ 5] 10.01-15.01 sec 14.6 MBytes 24.5 Mbits/sec
# [ 5] 15.01-20.01 sec 18.5 MBytes 31.0 Mbits/sec
# [ 5] 20.01-25.01 sec 16.0 MBytes 26.8 Mbits/sec
# [ 5] 25.01-30.01 sec 15.1 MBytes 25.4 Mbits/sec
可以看到我的测试环境 WireGuard 网络的传输速度大概是 25 Mbits/sec。
下面模拟远程办公开发者来到办公地点时的场景,此时 e 接入办公网并通过 DHCP 直接获得一个 192.168.200.0/24 的 IP:
# 在 e 上:
ip a s wlan0
# wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
# link/ether 52:54:00:0d:b4:90 brd ff:ff:ff:ff:ff:ff
# altname enp10s0
# inet 192.168.200.27/24 metric 1024 brd 192.168.200.255 scope global dynamic wlan0
# valid_lft 3590sec preferred_lft 3590sec
出现路由冲突。路由表内有两条 192.168.200.0/24 的路由规则:
ip r | grep 200
# 192.168.200.0/24 dev wg0 scope link
# 192.168.200.0/24 dev eth2 proto kernel scope link src 192.168.200.27 metric 1024
# 192.168.200.1 dev eth2 proto dhcp scope link src 192.168.200.27 metric 1024
iperf3 测试同一个局域网内的 d,因为路由冲突,192.168.200.0/24 的局域网流量被转发到公网,实际上也只有 25 Mbits/sec,结果如下:
# 在 e 上:
iperf3 -c 192.168.200.244 -i 5 -n 1G
# Connecting to host 192.168.200.244, port 5201
# [ 5] local 10.70.0.4 port 43220 connected to 192.168.200.244 port 5201
# [ ID] Interval Transfer Bitrate Retr Cwnd
# [ 5] 0.00-5.01 sec 22.8 MBytes 38.1 Mbits/sec 6558 329 KBytes
# [ 5] 5.01-10.01 sec 13.6 MBytes 22.9 Mbits/sec 10 279 KBytes
# [ 5] 10.01-15.00 sec 13.9 MBytes 23.3 Mbits/sec 0 342 KBytes
# [ 5] 15.00-20.01 sec 18.1 MBytes 30.4 Mbits/sec 231 302 KBytes
# [ 5] 20.01-25.01 sec 16.4 MBytes 27.5 Mbits/sec 0 323 KBytes
# [ 5] 25.01-30.01 sec 15.2 MBytes 25.6 Mbits/sec 10 324 KBytes
# 在 d 上:
iperf3 -s -i 5
# Accepted connection from 192.168.200.248, port 46160
# [ 5] local 192.168.200.244 port 5201 connected to 192.168.200.248 port 43220
# [ ID] Interval Transfer Bitrate
# [ 5] 0.00-5.00 sec 18.9 MBytes 31.7 Mbits/sec
# [ 5] 5.00-10.01 sec 13.8 MBytes 23.0 Mbits/sec
# [ 5] 10.01-15.01 sec 14.9 MBytes 25.0 Mbits/sec
# [ 5] 15.01-20.00 sec 18.2 MBytes 30.6 Mbits/sec
# [ 5] 20.00-25.01 sec 16.2 MBytes 27.3 Mbits/sec
# [ 5] 25.01-30.01 sec 14.9 MBytes 25.0 Mbits/sec
路由冲突解决办法
路由表实际上是二叉前缀树的实现,我们可以添加一个优先级更低的路由规则作为目标路由规则的父节点(二叉前缀树的概念)从而降低 WireGuard 添加的路由规则的优先级。
WireGuard GUI 客户端会根据 AllowedIPs 配置项添加对应的系统级路由规则,这里我们将远程开发者 e 客户端配置中的 192.168.200.0/24 改为 192.168.0.0/16 即可。
--- /etc/wireguard/wg0.conf.old 2024-01-22 13:30:54.793166541 +0000
+++ /etc/wireguard/wg0.conf 2024-01-28 05:15:34.232343033 +0000
@@ -9,6 +9,5 @@
PresharedKey = KKQGN01IkZ1kJD2fAxtDZ6k5VFAI2fMca2q+SV7OrGE=
Endpoint = 39.101.166.124:51820
AllowedIPs = 10.70.0.0/16
-AllowedIPs = 192.168.201.0/24
-AllowedIPs = 192.168.200.0/24
+AllowedIPs = 192.168.0.0/16
PersistentKeepalive = 15
然后我们再通过 iperf3 重新测试一下:
# 在 e 上:
iperf3 -c 192.168.200.244 -i 5 -n 1G
# Connecting to host 192.168.200.244, port 5201
# [ 5] local 10.70.0.4 port 43220 connected to 192.168.200.244 port 5201
# [ ID] Interval Transfer Bitrate
# [ 5] 0.00-5.01 sec 8.39 GBytes 14.4 Gbits/sec 0 2.49 MBytes
# [ 5] 5.01-10.01 sec 8.11 GBytes 13.9 Gbits/sec 0 2.62 MBytes
# [ 5] 10.01-15.00 sec 8.34 GBytes 14.3 Gbits/sec 0 2.75 MBytes
# [ 5] 15.00-20.01 sec 8.24 GBytes 14.1 Gbits/sec 0 2.75 MBytes
# [ 5] 20.01-25.01 sec 7.99 GBytes 13.7 Gbits/sec 0 4.28 MBytes
# [ 5] 25.01-30.00 sec 7.63 GBytes 13.1 Gbits/sec 0 4.28 MBytes
# 在 d 上:
iperf3 -s -i 5
# Accepted connection from 192.168.200.248, port 46160
# [ 5] local 192.168.200.244 port 5201 connected to 192.168.200.248 port 43220
# [ ID] Interval Transfer Bitrate
# [ 5] 0.00-5.00 sec 8.39 GBytes 14.4 Gbits/sec
# [ 5] 5.00-10.00 sec 8.12 GBytes 14.0 Gbits/sec
# [ 5] 10.00-15.00 sec 8.36 GBytes 14.4 Gbits/sec
# [ 5] 15.00-20.00 sec 8.25 GBytes 14.2 Gbits/sec
# [ 5] 20.00-25.00 sec 7.99 GBytes 13.7 Gbits/sec
# [ 5] 25.00-30.00 sec 7.73 GBytes 13.3 Gbits/sec
解决冲突后,局域网速度来到了 14Gbits/sec。
这个方法在 Linux/MacOS/iOS 上有效,Android 上未知(没有 Android 设备,无法测试),Windows 上无效(Windows 的路由表不是完全基于二叉前缀树,路由策略还受接口优先级和路由规则优先级的影响)。
为什么 systemd 而不是 /etc/wireguard/
WireGuard 其实是内核的一部分(内核模块),Linux 服务器可以直接通过添加 systemd-networkd 方式配置 WireGuard 网络,无需安装 wireguard-tools 包(只是辅助用的管理工具,不包含核心逻辑)。
我所有服务器的网络配置都是通过 systemd-networkd 来管理,所以通过 systemd-networkd 来管理 WireGuard 的配置。 这对于追求最小化改动,提高集群运维一致性是有意义的。
向 /etc/wireguard/*
中添加的配置,实际上是 wireguard-tools 提供的另外一种管理 WireGuard 配置的方案。本质上是通过 Bash 脚本来调用对应的命令行工具,比如 ip
、wg
来管理系统路由和 WireGuard 配置等。
wireguard-tools 包提供了 wg
命令行工具,这个工具类似 ip
这个工具(今后 wg
的逻辑可能被直接合并到 ip
中),让使用者可以通过命令行查看和管理 WireGuard 设备。
wg
命令也用于创建密钥对。密钥对实际上是一对 32 Bytes 的字节数组,我们完全可以在非生产环境通过 wg
命令创建密钥对,准备 WireGuard 配置后,通过 systemd-networkd 来配置 WireGuard 设备即可。在生产服务器安装 wireguard-tools 包的操作不是必须的。
开机自启动
我的服务器集群主要是 Arch Linux,如果你的网络配置不是通过 systemd-networkd 进行管理的(比如 Debian、Ubuntu 是用 netplan),你可能需要启动并设置 systemd-networkd 为开机自启动,比如:
systemctl enable --now systemd-networkd
总结
局域网互通解决的是两个办公网内的用户如何互相访问的问题。本文是对该场景的补充,旨在解决:不在办公网内的开发者如何访问任意办公网设备的问题。
如果你发现本文中存在任何语法错误,欢迎通过 主页 中 Email 联系我,另外你也可以在 关于 里面找到我的微信二维码。
参考
-
WireGuard 内部路由表。WireGuard 内部有自己的路由表实现(区别于 Linux 系统路由表),基于二叉前缀树,通过 AllowedIPs 配置项指定前缀树中的节点,树节点跟 Peer 建立映射。 ↩︎