无法配置的 NAT 之后的 VPN:什么仍然有效
没有端口转发,入站数据包无处可去。打洞、UPnP 和各自的中继改变这一点,但只在特定 NAT 行为下成立。
每个隧道端点都需要一个对端能够到达的地址和端口,而一台你无权管理的路由器,正是这一要求通常落空的原因。真正有意思的不是存在各种技巧,而是每一种技巧都取决于客户端看不到的映射表属性。这些属性中有两个几乎决定一切:映射如何建立,以及它是否接受来自内部从未联系过的地址的数据包。按这两个属性对环境分类,变通方案就不再是民间传说。
映射表决定一切
网络地址转换维护一张表,把内部地址和端口记录在公网地址和端口之下。与记录匹配的回复会被反向转换,而从外部到达、没有匹配记录的数据包根本没有任何目的地。正是这一行为解释了为什么路由器后面的监听者无法从互联网访问。它也解释了为什么每个方向的第一个包都必须由内部发起。
从路由器一侧看这张表,变通办法就很明显:要么有东西建立永久表项,要么建立临时表项并不断刷新,要么流量根本不走那条路。本文的其余部分按顺序讨论这三种情况。这里没有任何东西依赖厂商功能,超出的只是这张表所记录的内容。
会合服务能做什么,不能做什么
UDP 打洞把出站记录当作一个临时开口,而不是击败过滤器。双方都联系一项会合服务,该服务了解每个公网端点,并把对方的端点回告。随后双方互发,每个出站包都会创建或刷新让回复进入的记录。顺序是它奏效的原因,顺序也是它脆弱的原因:在对方建立记录之前就发送的一方会被直接丢弃,而稍后重试则会成功。服务本身不承载 VPN 流量,因此它唯一的杠杆是它所协调的时机。
映射行为决定其余一切,而 RFC 4787 给出了三种形态:与端点无关、与地址有关,以及与地址和端口都有关。在最后一种情况下,对端必须瞄准的端口随每个目的地变化,预测因此不再可靠。技术在这里走到尽头,因为运营商级 NAT 和严格的企业防火墙都会去掉方案所需的可预测性,再多的重试也换不回来。
Keepalive、重启与会移动的端点
打洞出来的路径是一条流而不是一个监听者,所以运行在其上的一切都必须经受变化。它必须用周期性流量让映射保持存活,察觉公网端点何时移动,并在路由器重启或地址重新分配后重新建立。网状 VPN 产品把这套逻辑放进自己的控制平面,而普通服务器则留给你。Keepalive 只在同一映射存活期间有用,而且会消耗移动客户端的电量,所以过于激进的间隔并非没有代价。
睡了一小时的笔记本电脑通常会换一个公网端点回来。凡是要重建路径的东西都必须再次从内部运行,这又把同样的要求放回客户端。
绕过缺失转发的三种办法
第一种办法请求路由器自行建立表项,通过 UPnP IGD、NAT-PMP 或 PCP,前提是功能存在且已开启。第二种把流量交给有公网地址的中继,这总是有效,也总是要付出带宽以及另一条信任边界的代价。第三种反转方向:内部主机向公网服务器开一条出站隧道,其余一切都坐在里面。
[Peer]
PublicKey = 6yGj3kQ2wVb8sTn1pLx4dRc7hZa9mKf2uYe5oNq0iXw=
Endpoint = relay.corp.internal:51820
PersistentKeepalive = 25
AllowedIPs = 10.44.0.0/24
中继端点让这笔交易在客户端配置中可见:端点是中继,而不是对端自己的地址。持久 keepalive 让映射保持温热,允许的前缀决定客户端能通过它发送什么。 从内部主机发起的反向隧道 是同一思路,只是所有者不同,而且完全不需要改动 NAT。三者都移除了入站要求,却没有击败按设计阻挡未经请求流量的防火墙,因为流量仍然从内部开始。
让变通方案匹配路由器的归属
如果你管理路由器,静态 UDP 转发比任何打洞方案都更容易审计,而且失败方式是你看得见的。如果不管理,就弄清楚厂商是否提供中继或托管控制平面,以及策略是否允许使用。如果都没有,剩下的选择就是反向隧道,或者接受该端点无法从外部到达。记录你用了哪一种,比听起来更重要,因为不同选项在谁能观察流量、第三方改动时会发生什么上并不相同。
注意你最终得到的路径是直连还是经过中继,因为这一事实同时预测延迟和故障行为。RFC 4787 仍是这一切之下映射行为的参考,读它的映射章节比依次测试三种变通方案更快。这张表并不在乎你选了哪个选项;它只记录下一个数据包被允许做什么。