只让一个本地 Web 服务按端口绕开 VPN
判断一条源端口规则能否把主机上的某个服务经 WAN 送出去、而主机其余部分继续使用 VPN,以及这样做要付出什么代价。
主机上的某一个服务必须通过物理上行链路回应互联网,而机器上的其他一切继续使用隧道。策略路由能否承担这个任务由三个条件决定,而每一条在写规则之前都很容易检查。服务必须终止在本机上,而不是在 DNAT 或端口转发之后;VPN 自身的规则必须允许这一例外;而且你必须匹配回包而不是请求。三者缺一,下面的命令照常干净加载,却什么用也没有。
写规则之前先检查这三个条件
通过 DNAT 发布端口的容器,或位于端口转发之后的服务器,产生的数据包并不源自本机。这些包会被转发,所以 以主机本地输入为条件的规则永远看不到它们 ,流量会继续走默认路由。继续之前先确认服务实际运行在哪里,因为这正是人们最常搞错的条件。
第二个条件是策略,而不是机制。任何绕开隧道的路由都是对隧道用途的例外,而在受管主机上,这个决定属于策略的所有者,而不属于正在排查超时的人。
匹配回包,而不是客户端的目标端口
监听 TCP 443 的服务器回包以源端口 443 离开本机。同一台主机上的浏览器连接远程 HTTPS 站点时,发出的是临时源端口与目标端口 443。两者都是 443 端口流量,只有其中一个该走 WAN 表,所以按目标端口匹配会抓错那一半。
能区分的是回包方向。匹配源端口,把规则限定为本地产生的数据包,规则便覆盖服务自身的回包,而不碰浏览器的请求。
先建好完整的返回表,再让规则指向它
空表意味着没有路由,看起来像服务坏了而不是策略坏了。先填好它,用一个别的组件没有占用的表号和优先级:
ip -4 route add table 100 192.0.2.0/24 dev eth0
ip -4 route add table 100 default via 192.0.2.1 dev eth0
ip -4 rule add priority 1000 iif lo ipproto tcp sport 443 lookup 100
从正在运行的主机上读取数字,而不是照抄。ip -4 rule show 会告诉你哪些优先级已被占用;把规则加到已经存在的优先级上,会悄悄改变两条单看都没问题的规则的顺序。
真正起作用的是两个选择器。iif lo 是内核报告本机自发数据包的方式,因此规则不会误抓从外部进来的转发流量。ipproto tcp sport 443 把它收窄到单一传输协议和单一端口,已是要求允许的最紧范围。去掉协议选择器,规则也会抓到 UDP 443,那是另一种行为的另一种服务。
让私有侧回包留在隧道内
如果同一个监听器还要回应私有侧的客户端,表 100 里全收的 WAN 默认路由就会试图把这些回包从上行链路送出去。把不应走 WAN 默认的私有前缀加进去,或者让它们落到下一条策略规则:
ip -4 route add table 100 throw 10.90.0.0/24
throw 路由会结束在这张表里的查找,并把数据包交还给规则列表,于是随后的 VPN 策略得到机会。它本身并不把任何流量路由进隧道;如果后面没有规则有匹配的路由,数据包仍然失败,所以要验证下一条策略,而不是假定 throw 就是修复。
反向路径过滤和主机防火墙规则仍然作用于到达的请求。放宽校验之前先看它们真正的判定结果,因为路由例外并不会让 rp_filter 接受它认为不可能的返回路径。
先测 TCP,再测 QUIC,最后测重启
从被授权的外部客户端开一条连接,再从私有侧开一条,然后用真实地址和端口检查回包查找。如果外部回包走了表 100 而私有回包没走,说明策略正是你要的。
HTTP/3 通过 UDP 提供,TCP 规则匹配不到,所以 QUIC 监听器需要自己的选择器,否则 HTTPS 正常时它仍会走隧道。把你定下的规则保存到拥有该接口的组件里,并在重新连接或 DHCP 续租后确认。若重启后有竞争的管理器删除了运行时路由,那是要在源头解决的归属问题,不是在上面再加更宽的 NAT 规则的理由。