下游子网与桌面 VPN:排除的是中继流量,不是应用
按应用绕过只对本地出站流量分类。工作站后方的设备需要自己的入站、转发与回程策略。
一台机器,两种角色,一套防火墙:一条 Wi-Fi 上行链路,其后是一个小型以太网子网,还有一个桌面 VPN 客户端,它假定凡是从这台机器出去的流量都是本地的。在本地启动的应用走本地输出路径。工作站后方的设备从以太网口进来,需要一条转发决策。为第一条路径写下的排除规则永远看不到第二条路径,这就是为什么浏览器绕过了隧道,而那些设备没有绕过。
两条数据包路径,一套策略
| Origin | Path | Decision owner |
|---|---|---|
| Application on the workstation | OUTPUT then egress | The VPN app's routing policy |
| Device behind the workstation | PREROUTING then FORWARD | The workstation's own forwarding policy |
第一行是多数分离隧道指南描述的内容。第二行是一份恰好跑在同一台机器上的路由器配置。把两者合并成一个心智模型就是错误所在:它们共享内核,却不共享策略。修改任何规则之前先把两条路径都画出来,其余设计就取决于缺的是哪一条。
表格也解释了为什么同一台机器可能在一项测试中通过、在另一项中失败。工作站上的浏览器证明的是本地路径;插在以太网口的笔记本证明的是转发路径。每次改动都要用两者验证,否则无论刷新多少次浏览器,第二条路径始终未被测试。
应用排除规则究竟在分类什么
按应用排除通常的做法是:给某个进程或某个用户的包打上标记,再把带标记的流量路由到隧道之外。这是一种本地输出侧的构造。下游设备的包没有本地进程,因此无论选择器是什么,它都永远匹配不上。
无论选择器是什么,两份清单都不是同一份清单,这才是关键。Mullvad 的 Linux 指南描述的是其防火墙与路由策略所消费的标记,示例针对的是本地输出;它不是对下游路由器的受支持描述。照抄这些示例,会得到一台自以为有排除规则、实则把所有流量都送进隧道的机器。
写下中继策略契约
把下游源前缀和入站接口放在一起命名,然后在入站处限制该前缀。没有这条限制,任何能到达端口的设备都可以声称一个被排除的地址并从旁路出去。把它当作两个问题:这些主机究竟能不能用这条上行链路,以及它们能不能到达工作站或隧道的私有网段?后者是额外的授权,而不是选择上行链路的副作用。
先允许转发,再过滤转发。Linux 网关需要开启转发、需要两个方向都放行预期流量的规则,还需要一条上游回程路由或有意识限定范围的源地址转换。仅靠 NAT 既不会选择上行链路,也不等于通过过滤器;它只是在这些决策之后改写地址。
面向中继的分类必须发生在转发路由决策之前,也就是 prerouting 阶段而不是 output 阶段。放在决策之后的规则仍能修改数据包,却改变不了它已经被送往何处的事实。
标记属于拥有它的应用
标记值、表编号和链优先级属于 VPN 应用策略的一部分,而不是恒久不变的常量。一个依赖向他人重建的表里插入规则的持久网络,会在下次更新时出问题,而且通常发生在重连过程中,而不是在方便的时候。
新增一条带 accept 判决的基础链,并不必然覆盖后续链的 drop。基础链有钩子和优先级,顺序上第一个判决生效;一条链里的 accept 取消不了另一条链先到达的 drop。要看完整规则集和路由策略的顺序,而不是盯着那个刚好增长了的计数器。计数器只证明规则跑过,从不证明生效的就是它的判决。
集成,或者把子网挪走
两种设计的区别在于旁路归谁所有。集成式设计只保留一台机器和一套策略,代价是依赖 VPN 应用的表生命周期,以及一组其文档随时可能改动的标记。为下游网络专门配一台路由器则彻底去掉这份依赖:子网有自己的上行链路,工作站仍是客户端,不再承担转发角色。
当下游网络必须在 VPN 更新过程中继续工作,或者每次发版后没人能验证集成策略时,选第二种。只有当集成有文档、且有人对它负责时,才选第一种。
在四种状态下验证边界:已连接、重连中、被主动阻断、已停止。每种状态下都要检查域名解析和两个地址族,因为一条泄漏 DNS 的旁路,并不是你设计的那条排除规则。测试应当证明精确的边界,而不只是证明某个页面能打开。