把整个局域网放到 VPN 隧道后面是容易的部分;决定远端如何看待这些客户端则不是。伪装会把每个 LAN 源地址改写成网关的隧道地址,因此远端只看到并回复一个对端。策略路由保留原始地址,并要求远端知道如何到达它们。两者都不是默认做法;选择取决于远端归谁所有,以及是否在意每台设备的身份。

两种设计承诺的东西并不相同

伪装把 LAN 缩减为一个可见主机:网关。远端网络只回复这一个地址,网关再把回复翻译回发起该流的客户端。远端不需要通往你私有前缀的路由,正因如此,这套设计在拒绝承载它们的运营商那里仍然成立。网关也成为每条连接的源,因此远端无法分辨两个客户端。

策略路由让每个客户端的地址保持原样。远端网络看到真实源地址,可以记录它、按主机施加规则,或把回复直接送回该地址。这种可见性正是全部意义,同时也带来一项义务:远端必须有一条通往 LAN 前缀、经隧道返回的路由。在启用规则之前就要规划好这条路由,因为隧道无论如何都会建立,缺的只会是回程。

故障形态与承诺一样各异。缺少伪装规则时,数据包带着私有源地址离开,再也不会回来。缺少回程路由时,策略路由下会出现同样的症状,但修复的位置在远端路由器,而不在网关。

伪装:远端只看到一个对端

伪装路径有一个重要的顺序。必须先开启转发,任何 NAT 规则才有意义,过滤表必须允许两个方向的通行,然后翻译才会作用在隧道接口上。

sysctl -w net.ipv4.ip_forward=1
iptables -A FORWARD -i eth1 -o tun0 -j ACCEPT
iptables -A FORWARD -i tun0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE

客户端仍然需要一条通往网关的路。要么网关就是它们的默认路由器,要么它们带着一条通往远端前缀的专门路由;如果默认路由仍指向另一台路由器,那台路由器就需要一条通往隧道目的地的静态路由,否则数据包会在网关看到它之前走错下一跳。

DNS 是另一个决定。远端的解析器只有在回复能回到客户端时才会作答。在伪装之下,查询看起来来自网关,因此解析器的回程已经满足;在策略路由之下,解析器需要与其他任何远端服务相同的回程路由。

策略路由:保留源地址,补上回程

策略路由从一张表和一条选择它的规则开始。从 LAN 接口进入的流量被送往专用表,而那张表带有经隧道的默认路由。主表保持不变,因此网关自身产生的流量可以走另一条路径。

ip rule add iif eth1 table vpn
ip route add default dev tun0 table vpn

接着远端必须把 LAN 前缀经隧道送回。那是远端路由器上的配置,不在这台网关上。网关还必须接受从主表预料之外的接口到达的回复;当严格模式丢弃这些包时,把 rp_filter 设为 2 的宽松反向路径过滤是常见调整。

当两条隧道同时启用时,在流量进入时打标记,并让标记决定用哪张表。只看输入接口的规则会把两条隧道的流量送进同一张表,于是第二条隧道抢走第一条的流。用一条能扛过重连的连接去验证这个标记,混合策略往往在那时才暴露。

按归属以及远端记录什么来决定

如果隧道止于运营商,就用伪装。那位运营者不会为你的私有 LAN 段做路由,也没有理由区分你网关后面的单个客户端。只看到一个对端是它的策略唯一支持的模型,所以就这样给它,并把翻译留在你自己的边缘。运营商看到一个对端,这正是它的策略所期望的,而你的客户端继承了这一个身份。

如果两端都是你的,而远端服务依据源地址行事,策略路由就值得多出那条路由。防火墙规则、按用户访问和审计日志于是能区分客户端。如果远端只需要可达性,伪装更简单,还能消除一整类回程路由错误。

混合设计很常见:自己网络内部用策略路由,运营商边缘用伪装。要紧的是写清楚哪一跳做翻译、哪一跳做路由,因为为某个客户端误加的 NAT 规则会让同一条链上所有客户端的源地址都被隐藏。

先从一个客户端验证,再从第二个客户端验证

在流量起点做测试。检查主机使用的路由,观察数据包出现在隧道接口上,并查看 conntrack 里的翻译。这个顺序证明数据包走的是预期的路径,而不只是到了某个地方。

重连之后再做一次。写接口名的伪装规则能扛过新的隧道地址;写旧地址的规则则不能。然后再测第二台 LAN 主机,因为在一台机器上成立的设计,往往取决于那台机器自己的路由,而不是网关的策略。

把选定的模型、翻译或保留的源地址,以及回程路由的负责人写进网关笔记。否则下一个添加客户端或隧道的人只能猜,而猜测正是让一套能用的 LAN 到 VPN 方案变成半套的原因。

相关文章