前言
此前在外需要访问局域网时,笔者一直通过在网关上部署类似Shadowsocks等代理服务,配合公网IP和DDNS实现。相比传统VPN,该方案更轻量、稳定,而且没在安全性上作出妥协。对于客户端,Apple生态下的代理应用近年来也越来越完善与智能。
就此一直良好运行了七年,直到需要访问的局域网从一个变成多个,如果继续按照原来的方法,需要给每个独立的局域网配置一个代理服务,一个DDNS,对每个局域网编写一套分流规则。更麻烦的是,当有多台局域网内设备需要互相访问,而且这些设备有的是Windows PC、Linux Server,Android TV时,在客户端部署代理软件的复杂程度就会指数式上升,变成一个运维地狱。
新的需求简述如下:
- 有多个局域网,下文简化为两个(简称为CAN和SEA方便描述),均使用OpenWrt的网关连接到互联网。
- 各地网关均拥有动态公网IP,而且配置了
xxx.mydomain.io
的DDNS服务指向它的公网IP。 - 这些局域网的网段都不重叠,每台设备均拥有全局唯一的局域网IP地址。
期望:
- 跨局域网内的设备均可以互相访问,支持TCP、UDP与ICMP,在日后运维中能通过ping命令检查连通性。
- 除了网关外不需要对其他设备进行额外配置。
- 在网络意外中断或者停电等场景,设备恢复后网络需要具备自愈能力。
- 在任意局域网内能通过类似
device-name.location
的域名,例如是mac-mini.can
的方式访问任意其他设备。 - 被访问的设备需要看到真实的来源IP,便于后续配置访问控制规则。
- 在将来增加更多区域时不需再作过多改动。
寻找一番,发现Tailscale是一个十分优秀的企业级解决方案能完全满足笔者需求。Tailscale在官网中提到,Tailscale是基于WireGuard之上搭建的。这些企业功能对个人或小型团队来说并非刚需,因此选择更加轻量化且灵活的WireGuard。
Location | Gateway | WireGuard |
---|---|---|
CAN | 7.0.0.1/8 | 192.168.10.2/24 |
SEA | 10.0.0.1/24 | 192.168.10.1/24 |
部署过程
在网关上安装WireGuard
opkg update
opkg install wireguard-tools kmod-wireguard luci-proto-wireguard
Note
可能存在安装成功后无法直接添加类型为WireGuard的接口的情况,可以通过重启OpenWrt系统解决。
在网关上新增WireGuard接口
在每个网关的/etc/config/network
中新增
config interface 'wg0'
option proto 'wireguard'
option listen_port '51820'
option private_key 'your-private-key'
list addresses '192.168.10.1/24'
其中:
- 51820是WireGuard协议的默认端口,建议更改,避免被无意义的扫描和防火墙拦截。
private_key
可以使用wg genkey
或在LuCI界面中生成,每个网关需要使用不同的公私钥对。- 每一个网关的addresses应该是不相同的,但网段范围应该是一样的。
此外,还需要在防火墙放行来自公网的WireGuard流量,在/etc/config/firewall
中新增
config rule
option name 'Allow-WireGuard'
list proto 'udp'
option src 'wan'
option dest_port '51820'
option target 'ACCEPT'
注意dest_port
应该和上述的listen_port
对应。
为WireGuard接口添加peer
在每个网关中将其它区域网关的WireGuard添加为peer,值得注意的是,WireGuard与大多数基于非对称加密的软件类似,使用公钥进行加密,私钥进行解密。换言之,在添加peer后,双方都有彼此的公钥,而自己持有私钥。
例如在SEA区域的网关,将CAN添加为peer,则需要在/etc/config/network
中新增
config wireguard_wg0
option description 'CAN'
option public_key 'can-pub-key'
option endpoint_host 'can.mydomain.io'
option persistent_keepalive '25'
list allowed_ips '192.168.10.2/32'
list allowed_ips '7.0.0.0/8'
option route_allowed_ips '1'
option endpoint_port '51820'
allowed_ips
需要加上CAN区域的WireGuard地址和CAN区域的局域网网段,这样当SEA网段下的设备访问CAN区域才会允许通过此peer路由。route_allowed_ips
表示自动添加路由表,告诉网关在路由allowed_ips
的时候,不通过默认的互联网接口,而是通过此WireGuard接口。endpoint_port
需要和上述的listen_port
对应。
Note
如果peer其中一方没有公网IP,可以不填写endpoint_host
和endpoint_port
。
添加成功后重启WireGuard接口,通过wg show
命令可以检查peer是否已经成功添加并连接。
root@OpenWrt:~# wg show
interface: wg0
public key: sea-pub-key
private key: (hidden)
listening port: 11820
peer: can-pub-key
endpoint: can-ip-addr:51820
allowed ips: 192.168.10.2/32, 7.0.0.0/8
latest handshake: 1 second ago
transfer: 12.01 GiB received, 120.71 MiB sent
persistent keepalive: every 25 seconds
上面输出展示了成功连接的情况,并包括传输数据和握手时间的信息。
在firewall中添加zone
在/etc/config/firewall
中新增
config zone
option name 'wg'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'ACCEPT'
list network 'wg0'
config forwarding
option src 'lan'
option dest 'wg'
config forwarding
option src 'wg'
option dest 'lan'
这个配置允许局域网(lan)与WireGuard区域(wg)的双向互通,且不启用NAT。
至此,各区域的局域网已经通过WireGuard连接到一起,网内的设备均可以通过局域网IP直接访问,网关会根据网段自动选择WireGuard隧道。
➜ ~ traceroute 7.0.0.66
traceroute to 7.0.0.66 (7.0.0.66), 64 hops max, 40 byte packets
1 openwrt (10.0.0.1) 4.395 ms 3.196 ms 2.089 ms
2 192.168.10.2 (192.168.10.2) 192.990 ms 194.596 ms 193.510 ms
3 7.0.0.66 (7.0.0.66) 194.141 ms 180.263 ms 183.067 ms
通过IP访问异地的NAS时,来源IP会显示实际IP,而不会是代理服务器或网关IP(没有进行NAT)。

性能优化
WireGuard使用ChaCha20-Poly1305实现加密,在Linux中支持在内核态完成加解密,因此对CPU占用较低,即使在低端开发版上也不会因CPU性能遇到瓶颈。
在协议设计上,WireGuard也不包含流量控制的逻辑,它通过UDP封装并传输IP数据包,而IP数据包的发送是「尽力而为」的,即可能存在丢包、重复和乱序。如果希望尽量利用带宽,可以尝试在更上层的协议中优化流量控制算法,例如对使用TCP的内核开启BBR算法等。
此外,观察到部份ISP对UDP包有限流和干扰,此类策略针对的是UDP协议而非WireGuard,可以尝试通过DNAT等方法进行「端口跳跃」。