WireGuard 站点到站点 VPN 实战:双网关互联与防火墙路由全解
两个办公室、机房和家里实验室、或者总部与分支机构之间要互访内网,最省事的方案之一是 WireGuard 站点到站点(site-to-site)隧道。它比 OpenVPN 轻、比 IPsec 好配,一个 UDP 端口、一对密钥就能把两个网段打通。我自己在两个固定地点之间跑了两年,踩过的坑集中在"路由和 NAT 搞混""MSS 没夹导致大包丢""开机没自启"。这篇文章就讲一套能直接复制的双网关配置,以及上线后怎么不让它悄悄退化。
1. 什么场景需要站点到站点 VPN
先分清两种用法,别一套配置到处套:
- 站点到站点(本文主角):两端都是网关,背后各挂一个内网,目标是让这两个内网互相路由。两端长期在线、端点基本固定。
- 远程接入(road warrior):一台笔记本或手机临时连回总部,端点会变、常在 NAT 后面。
站点互联的痛点不是"加密"本身,而是"怎么让对端网段的流量走隧道、又不影响本网点对互联网的出站"。下面这套方案就是围绕这个核心。
2. 架构与前置条件
典型拓扑是两个站点各有一台接公网的网关,网关后面是各自的内网:
站点 A 内网 10.10.10.0/24 ── 网关 A (公网 IP A, 隧道 IP 10.10.9.0/31)
│ 加密 UDP 隧道 (端口 51000)
站点 B 内网 10.10.11.0/24 ── 网关 B (公网 IP B, 隧道 IP 10.10.9.1/31)
前置条件:
- 两端网关都能出公网、能互访对方的公网 UDP 端口(建议固定一个端口,比如 51000)。
- 两个内网网段不能重叠。如果都是 192.168.1.0/24,隧道建起来路由会打架,先改掉其中一个。
- 隧道只用一个小网段给两个网关自己(/31 足够),内网主机不需要隧道 IP。
- 两端都装好 WireGuard(Ubuntu/Debian 用
apt install wireguard,内核模块或 wireguard-go 都行)。
3. 生成密钥对
每台网关各自生成自己的私钥和公钥,公钥要发给对端写进对端的 Peer 配置:
# 在网关 A 上
wg genkey | sudo tee /etc/wireguard/wgA.key
sudo cat /etc/wireguard/wgA.key | wg pubkey | sudo tee /etc/wireguard/wgA.pub
# 在网关 B 上
wg genkey | sudo tee /etc/wireguard/wgB.key
sudo cat /etc/wireguard/wgB.key | wg pubkey | sudo tee /etc/wireguard/wgB.pub
注意私钥文件权限要收好(chmod 600),公钥可以放心发给对端。
4. 写网关配置
站点互联的两端是对称的,区别只在 IP 和端点。下面给网关 A 的完整配置,网关 B 把地址和端点对调即可。
网关 A(/etc/wireguard/wgA.conf):
[Interface]
Address = 10.10.9.0/31
ListenPort = 51000
PrivateKey = <网关 A 的私钥>
PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -i %i -j ACCEPT
[Peer]
# 网关 B
PublicKey = <网关 B 的公钥>
Endpoint = <公网 IP B>:51000
AllowedIPs = 10.10.11.0/24, 10.10.9.0/31
# 如果 B 在 NAT 后,A 也加一行 PersistentKeepalive = 25
网关 B 把 Address 改成 10.10.9.1/31,AllowedIPs 改成 10.10.10.0/24, 10.10.9.0/31,Endpoint 指向 A 的公网 IP。关键点:AllowedIPs 写的是"对端内网 + 隧道小网段",它同时告诉 WireGuard 两件事——哪些目标地址的包要进隧道、以及哪些来源地址的包允许解密进来。
5. 防火墙与路由要点
这一节是站点到站点最容易翻车的地方,记住一句话:站点互联是路由,不是上网,绝对不要对隧道流量做 MASQUERADE。
wg-quick启动时会自动按 AllowedIPs 加路由,内网主机只要把网关当默认下一跳(通常已经是),就能访问对端网段,不用在主机上改任何东西。- 如果之前为了远程接入在 POSTROUTING 里写了
MASQUERADE针对 wg 接口,站点互联场景下要删掉,否则对端主机看到的源 IP 变成你的网关,回包回不来。 - 出于安全,建议在两端各加一条只放行对端公网端点访问 51000 的规则,而不是对全网开放:
- 需要限制跨站点能访问什么(比如只允许办公网访问机房的特定服务),在 FORWARD 链上加细粒度规则,例如禁止跨站点 SSH:
6. MTU 与 MSS 陷阱
WireGuard 在物理链路 MTU 之上又加了加密头,实际可用载荷比普通以太网少约 60-80 字节。如果不处理,TCP 大包会被悄无声息丢弃,现象很典型:能 ping 通、能开小网页,但传大文件、跑 HTTPS 或数据库同步就卡死或中断。这就是 MTU 黑洞。
最稳妥的修法是在 WireGuard 接口上把 TCP MSS 夹小:
[Interface]
MTU = 1380
# 或在 PostUp 里用 iptables 夹 MSS
PostUp = iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1380
经验值:物理 MTU 1500 时,WireGuard 接口 MTU 设 1420,MSS 设 1380 基本通吃。如果中间还套了 VLAN 或 PPPoE(MTU 1492 甚至更低),相应再减。别瞎设成 1400 以下,太小会多分片、降吞吐。
7. 启动与连通验证
两端都用 systemd 管理,保证重启后自动起来:
sudo systemctl enable --now wg-quick@wgA # 网关 A
sudo systemctl enable --now wg-quick@wgB # 网关 B
# 看隧道状态和握手
sudo wg show
# 期望看到 latest handshake 是最近的时间、transfer 有数据
# 从 A 内网主机 ping B 内网主机
ping 10.10.11.x
# 再看路由表确认对端网段走了 wg 接口
ip route | grep wg
wg show 里如果没有 handshake、transfer 一直是 0,说明端点不可达或密钥/AllowedIPs 不对,回到第 4 节核对。
8. 常见故障排查
- 握手成功但 ping 不通对端内网:九成是 AllowedIPs 没写对端内网段,或本端内网主机没把网关当默认路由。用
ip route确认对端网段下一跳是 wg 接口。 - 能 ping 小包、大文件必败:MTU/MSS,见第 6 节。
- 一端在 NAT 后收不到流量:加
PersistentKeepalive = 25打通 NAT 映射。 - 重启后隧道没起来:确认
systemd enable了,且配置路径是/etc/wireguard/wgA.conf与wg-quick@wgA对应。 - 跨站点某些服务连不上但网络通:多半是你在 FORWARD 或对方防火墙挡了对应端口,逐条排查链规则,别上来就怀疑 WireGuard。
9. 上线后的巡检与防退化纪律
站点互联最容易"上线好好的,三个月后悄悄坏",靠这几条纪律兜住:
- 监控握手与流量:把
wg show的 latest handshake 和 transfer 接进你的监控(Prometheus node-exporter 或黑盒探测都行),超过 N 分钟没握手就告警。别等用户投诉"机房连不上"才发现。 - 端点 IP 变动要同步:如果某一端公网 IP 是动态的,用动态域名而不是写死 IP;改了域名或 IP,两端 Endpoint 一起改,并重新握手验证。
- 禁 NAT 是铁律:任何人在网关上加 MASQUERADE 规则前,先确认它不是作用在 wg 接口上。建议把站点互联的 FORWARD 放行规则和 MSS 夹规则写进配置文件的 PostUp,随隧道一起加载,避免手动加完某天被清掉。
- 密钥定期轮换但有记录:WireGuard 没有证书体系,换密钥就是改 PrivateKey/PublicKey 并同步对端。轮换前先在维护窗口通知,改完用
wg show和跨站点 ping 双重确认,旧密钥在确认稳定后再删除。 - 配置进版本管理:
/etc/wireguard/*.conf抽成模板进 Git,密钥单独用 secrets 管理,谁改了哪台网关、AllowedIPs 改了什么,回滚就是git revert。不上这套,设备一多(多分支、多站点)随便改一个 AllowedIPs 就全乱。 - 不要为"图省事"把站点互联和远程接入混在同一接口:两者策略不同(一个禁 NAT、一个常 NAT),分开两个接口(wgA 站点到站点、wg0 远程接入)清晰得多,排错也快。