基于应用与基于目标地址的分流:该选哪一种
一条规则跟随进程,另一条规则跟随地址。先想清楚你必须控制什么,再用同一子网上的辅助进程和第二个应用去验证。
基于应用的分流与基于目标地址的分流并不是同一件事的两种设置。前者问的是哪个进程在发包,后者问的是这个包要去哪里。为第一个问题建立的规则,在流量来自辅助进程时会悄悄失效;为第二个问题建立的规则,在主机名解析到你从未列出的地址时也会失效。先选对与需求匹配的对象,再用两个测试包证明它:一个必须走隧道,另一个必须不走。
两条规则作用在不同层次
应用规则作用在网络层之上,针对的是操作系统分配给发送方的身份。在 Android 上就是包背后的 UID,VPN 服务按 UID 决定流量是否进入隧道。整个过程完全不看目标地址,因此被选中的应用可以到达任何地方,而其他应用无法到达内网。
目标地址规则作用在包本身。隧道接口拥有一组前缀,路由表把匹配的流量送入其中,发送进程是谁无关紧要。所以目标地址规则能覆盖某个服务、打印机或没人记得列出的批处理任务,也所以在客户端根本不看进程的平台上,它无法表达为就是这一个程序。
应用规则适合移动客户端和受管代理
Android 直接提供了这个选择:VpnService.Builder.addAllowedApplication() 和 addDisallowedApplication() 接收包名并按 UID 执行判断,Android 的 VPN 开发者指南对此有说明。iOS 也有等价机制,即按 bundle 标识符配置的单应用 VPN 规则,通常由 MDM 描述文件下发,而不是手工输入。
桌面客户端是薄弱环节。大多数 Windows 和 macOS 的 VPN 客户端按目标地址路由,根本不提供按进程选择的能力,因为隧道驱动看到的是包,不是进程。如果你的方案依赖在笔记本上只让一个应用走隧道,先确认客户端的真实能力再围绕它设计;你可能是在目标地址列表和再加一台机器之间做选择。
一个常被忽略的推论:在这些平台上无法用路由模仿应用规则。为应用的服务器加一条主机路由,会把所有人的流量都送进隧道去访问那台服务器,包括浏览器和更新程序。
目标地址规则适合子网清单和站点可达性
WireGuard 把模型说得很明确。AllowedIPs 是加密密钥路由:内部包的目标落在某个 peer 的列表内就加密发给它,其余一律按常规路径明文发出。没有任何进程需要点名,也没有任何包含范围的含糊。
OpenVPN 通过下发的路由表到达同一效果。route-nopull 阻止服务器指派路由,之后客户端的 route 10.20.0.0 255.255.0.0 vpn_gateway 正好覆盖办公网前缀。IPv6 在每一个这类客户端里都是单独的一条,忘记它正是绕过看似正确的 IPv4 分流而泄漏的最常见原因。
主机名不是路由。如果配置里有域名字段,把它当作客户端解析一次的名字,而不是永久的地址范围;内容分发网络和负载均衡明天就会给出不同的地址。
两种模型都会失效的地方:辅助进程、共享服务、解析器
辅助进程会破坏应用规则,表现却像是服务器故障。浏览器的更新程序、崩溃上报进程、以 SYSTEM 运行的同步服务、嵌在另一个包里的 WebView,都是各自独立的身份,所以把浏览器放进隧道,真正的请求可能仍留在隧道外。反过来同样令人困惑:被排除的应用如果在隧道内的进程里调用共享库,它的流量仍然会走隧道。
目标地址规则会在共享服务和名字上失效。文件共享、打印机、内网 DNS 和许可证服务器都落在地址上,于是单应用隧道给了浏览器访问权,映射的网络驱动器却还是死的。这种覆盖差异不是两种模型的缺陷,但正是单应用隧道经常得到 VPN 连着、共享没了这一结果的原因。
解析器横跨两者。如果客户端设置了内网 DNS 服务器,但操作系统通过 HTTPS 向公共解析器查询,答案就来自隧道之外,目标地址规则可能永远匹配不上。在断定规则有错之前,先查清究竟是哪个解析器给出了答案,并在客户端一侧确认它到底有没有安装隧道解析器。把 DNS 留在隧道外的分流,在路由表里看起来完美,却会在每个内网名字上失败。
操作流程:选择、实施、验证
从需求出发,不要从菜单出发。如果目标是只有这个应用能使用隧道,你需要应用规则和支持它的平台。如果目标是这些网络能通过隧道访问,你需要前缀。如果答案是两者都要,就把它实现为两套独立控制,并写清楚哪一套负责哪一类故障。
采用你能表达的最窄形式,然后向路由表求证,而不是看客户端的状态图标:
ip route get 10.20.30.40
ip -6 route get 2001:db8:10::40
wg show wg0 allowed-ips
Get-NetRoute -DestinationPrefix 10.20.0.0/16
Find-NetRoute -RemoteIPAddress 10.20.30.40
前两行指明真实目标会使用的接口;wg show 打印已配置的前缀列表;在 Windows 上,Find-NetRoute 回答的就是发包时协议栈会回答的同一个问题。如果接口是隧道,规则就匹配了。
然后再测一个应当被排除的包,从另一个应用发往同一目标。把客户端版本、规则类型、前缀或包名列表,以及两次观测结果都记进工单。正是这一组结果,告诉下一位工程师问题出在策略配错了,还是这些流量本来就不属于这条策略。