在 VPN 网关两侧同时抓包,同一条流可能显示四个不同地址。源 NAT 改写数据包自称来自何处,目的 NAT 改写它去往何处。Masquerade 不是第三个方向,而是一条从出口接口取地址、而非使用固定值的源 NAT 规则。把三者中真正需要的那一条说清楚,多数“VPN 坏了”的工单都会归结为回程路径的问题。

每条规则改写了什么

源 NAT 通常写作 SNAT,在数据流离开路由器或网关时改写它的源地址。接收方服务把应答发往被翻译后的地址,执行翻译的盒子再用它为这条连接保存的状态把改动还原。在 Linux 上这份状态存放在 conntrack 里,所以 conntrack -L 会把原始元组与应答元组并排打印出来。

目的 NAT 即 DNAT 改写的则是目的地址。一个发往某公网地址 UDP 51820 的数据包,可以被改写到内网的 WireGuard 服务器 10.8.0.2:51820。家用路由器或云端负载均衡器就是这样发布 VPN 监听端口的,而这并不说明隧道此后允许承载什么。

Masquerade 属于 SNAT 家族。这条规则不点名任何地址,而是使用数据包离开时出口接口所拥有的地址。Linux 为它提供了单独的目标,在通过 DHCP 或 PPPoE 取得地址的 WAN 接口上这是常见选择。

映射存放的位置决定了故障形态

在每一道边界都记录元组:隧道之前、隧道之内以及公网接口上。隧道内的抓包与外部的抓包本就应当不同,不知道翻译发生在哪里就去比对两者的人,只会追错主机。

回程流量必须经过持有映射的那台设备。如果应答走了另一条路由、第二条上行链路、策略路由,或者走了一个从未听说过你私有网段的运营商,带状态的逆向翻译就永远不会发生,客户端看到的便是单向流量。再加源 NAT 很少能修好缺失的回程路由,它可能把症状藏在一条流上,同时破坏其他所有地方的基于地址的策略。

另一个常见错误是指望隧道凭空造出可路由性。一个数据包要发往互联网的私有客户端,需要一套有效的出站安排:默认路由、会转发数据的网关,通常还需要把源地址翻译成公网地址。仅仅加密并不会让私有地址变得可达。

Masquerade 还是显式 SNAT

比较两者要看它们承诺了什么。Masquerade 选择出口地址,显式 SNAT 把它固定下来。在公网地址稳定的网关上,固定地址让日志和合作方白名单只有一个可预期的值。在动态 WAN 链路上,写死的 SNAT 地址会在租约变化的那一刻失效,而 Masquerade 正是为这种情形而设。

作用范围与选择同样重要。一条匹配所有离开网关的数据包、没有源或目的限制的 Masquerade 规则,也会翻译你本想保持原样的流量,包括发往其他私有网络的流量。请把规则限制在客户端网段、预期目的地址和接口上。

在基于策略的 IPsec 中这一点咬得最狠。strongSwan 的转发指南讲到开启 IP 转发并为客户端添加源 NAT 规则,同一份文档也涵盖了如何排除必须保留原始地址的流量。如果在安全策略匹配之前就翻译了源地址,隧道可能已经建立,但流量依旧失败,因为数据包看起来已不再是协商好的选择器。

还要决定远端应当看到什么。经过翻译之后,服务看到的是网关,而不是单个客户端。这简化了回程路由,同时也抹平了日志,并让按客户端的白名单失效。如果保留客户端地址很重要,就规划一条带路由的回程路径,而不是为了通过测试把它们藏起来。

在 Linux 网关上读规则

一台客户端位于 10.8.0.0/24、WAN 接口名为 eth0 的远程访问网关,必须先开启转发并有一条放行该流的过滤策略,任何 NAT 规则才可能生效。iptables 版本把两个方向都放在 nat 表里:

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 51820 \
  -j DNAT --to-destination 10.8.0.2:51820

nftables 写法使用同样两个钩子,配上具名接口和显式链:

nft add rule ip nat postrouting ip saddr 10.8.0.0/24 oifname "eth0" masquerade
nft add rule ip nat prerouting iifname "eth0" udp dport 51820 dnat to 10.8.0.2:51820

两段示例都假定过滤链已经接受被转发的流量。NAT 并不是放行规则,一条翻译正确的数据包仍可能在下一张表里被丢弃。先用 conntrack -L 确认现存的映射,再用 nft list ruleset 或 iptables-save -t nat 确认当前加载的内容,最后在两侧接口上都抓包,确认地址确实发生了改变。

看起来像规则写错的几种故障

发卡流量是经典情形。内部客户端要通过服务器的公网地址访问内部服务器,就需要在去程做 DNAT,并在回程做出站源地址翻译,否则服务器会直接回给客户端,应答随即被丢弃。正确的做法是为这种“内部访问公网地址”的情形再加一条更窄的规则,而不是把 Masquerade 写得更宽。

双重翻译是安静的那种。一个已经处于运营商级 NAT 之后的客户端,带着你无法回程路由的地址来到你的网关,于是你又翻译了一次。它能用,但日志里从此多出两跳私有地址,任何排查都需要两份映射。基于策略的 IPsec 还会再添一层扭曲:在策略匹配前翻译,你会得到一条已建立、却没有流量通过的 SA,看起来像防火墙的毛病而不是 NAT 的毛病。

还有一种测试能过、上线却坏的情形。手工敲一遍 NAT 命令只证明了机制,而不是配置。运行时临时添加的规则,会在重启或下一次接口事件时消失;而一条绑在会重新编号的接口上的 Masquerade 规则,可能悄悄改变远端看到的地址。请把规则写进持有它的防火墙配置,并在重新连接之后再测一遍。

保存规则前的检查清单

先命名方向:源、目的,还是两者都要。然后确定谁必须持有状态——网关、上层路由器还是 IPsec 对端——并确认回程确实从它经过。把规则限制在你真正想要的客户端网段、目的地址和接口上,并事先决定远端应当看到网关还是客户端的原始地址。

要从客户端测,而不是从网关测。网关自己的浏览器可以完全绕过客户端翻译规则,所以在那儿能打开的页面证明不了隧道的事。发一个由客户端发起的请求,观察计数器和 conntrack 表,如果条件允许,在隧道重连和 WAN 地址变化之后再复测一遍。

相关文章