跳转至

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 的规则,而不是对全网开放:
    iptables -A INPUT -p udp --dport 51000 -s <对端公网 IP> -j ACCEPT
    iptables -A INPUT -p udp --dport 51000 -j DROP
    
  • 需要限制跨站点能访问什么(比如只允许办公网访问机房的特定服务),在 FORWARD 链上加细粒度规则,例如禁止跨站点 SSH:
    iptables -A FORWARD -i wgA -p tcp --dport 22 -j REJECT
    

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 远程接入)清晰得多,排错也快。

FAQ

WireGuard 站点到站点和远程接入(road warrior)有什么不同? 站点互联两端都是网关、长期在线、通常用静态端点,加密的是两个内网之间的流量;远程接入是一台笔记本或一个手机连回家里/公司,端点会变、常走 NAT。配置上站点互联用更小更纯粹的路由网段(如 /31),且两端都要允许对方的私网段进 AllowedIPs。 为什么不能对站点互联流量做 MASQUERADE(NAT)? 站点互联的本质是"路由"不是"上网"。一旦在网关上做 MASQUERADE,对端看到的源地址会变成网关自己,对端内网的主机真实 IP 就丢了,回包也只对网关、不会回到原始主机。正确做法是纯路由:wg-quick 自动加对端网段路由,内网主机把网关当默认下一跳即可。 /31 网段能用吗?会不会没有可用 IP? 可以。RFC 3021 允许 /31 用于点对点链路,两个地址分别给两端网关。WireGuard 站点互联里网关本身才需要隧道 IP,内网主机不在隧道网段里,所以 /31 完全够用且最干净。如果某些老设备/系统不认 /31,退回 /30(两个可用主机地址)即可。 跨站点访问慢或某些应用连不上,多半是 MTU/MSS 问题吗? 大概率是的。WireGuard 在真实物理 MTU 上又套了一层加密头,可用载荷比普通网小约 60-80 字节。TCP 的 MSS 没跟着缩,就会出现大包被悄无声息丢弃(MTU 黑洞),表现是 ping 通、小文件能传、大文件或 HTTPS 卡死。在接口上把 TCP MSS 夹小 40 字节(如 1380)基本能解决。 一端在 NAT 后面收不到对端流量怎么办? 在 NAT 后的一端配置 PersistentKeepalive = 25,让它周期性发保活包打通 NAT 映射。站点互联两端最好都有静态端点(固定 IP 或域名);如果一端没有公网 IP,就只能让有公网 IP 的那端做被动接收方,NAT 后的一端主动维持连接。

相关文章