两种 Windows 配置在“设置”里看起来一样,行为却完全不同。一种是复制过来的 VPN 配置文件加上应用触发器,因此启动某个可执行文件就会拉起隧道。另一种是拆分隧道配置文件,其路由列表恰好覆盖了该应用所通信的目标。第一种回答的是“隧道何时存在”。第二种回答的是“哪些数据包进入隧道”,而且只有第二种能让机器其余部分继续走物理链路、同时应用保持连接。

触发器是生命周期规则,不是路由规则

Add-VpnConnectionTriggerApplication 把可执行文件路径绑定到配置文件名称。该进程启动时,远程访问连接管理器会拨号连接该配置文件,并且 当触发条件结束时,连接可能会断开。这一交换中没有任何环节检查套接字。该 cmdlet 需要提升权限的 shell、二进制文件的完整路径,以及在 -Force 替换现有条目时还需要它。用以下命令确认关联是否保留: Get-VpnConnectionTrigger 而不是相信提示;Microsoft 的 Add-VpnConnectionTriggerApplication 参考文档 列出了参数和示例。

两个限制解释了大多数“触发器毫无作用”的反馈。触发器围绕所有用户配置文件构建,因此配置文件作用域是第一件要确定的事;应用路径必须匹配真正运行的二进制文件,而不是快捷方式,也不是会派生第二个进程的启动器。另一个限制是设计使然:在 Windows 判定为可信的网络上,触发器保持空闲,因为该配置文件是为了在公司之外使用而设计的。

Add-VpnConnectionTriggerApplication -Name "APP_VPN" -ApplicationID "C:\Program Files\Vendor\app.exe" -Force
Get-VpnConnectionTrigger -Name "APP_VPN" | Format-List *
Get-VpnConnection -Name "APP_VPN" | Format-List Name,ConnectionStatus,SplitTunneling,AllUserConnection

拆分隧道决定什么穿越隧道

关闭拆分隧道时,Windows 会在 VPN 接口上安装一条低跃点的基于类的默认路由。每个套接字都会走它,包括浏览器和更新服务,因此这种配置下的触发器会在配置文件连接期间移动整台机器。触发器仍然只是一条生命周期规则;影响范围来自路由。

开启拆分隧道时,配置文件只携带网关推送的或你手动添加到 VPN 接口的前缀。其余一切继续使用物理默认路由。这正是人们通常说的“让一个应用走单独出口”,而且从头到尾都是路由决策。

Set-VpnConnection -Name "APP_VPN" -SplitTunneling $true
Get-VpnConnection -Name "APP_VPN" | Select-Object SplitTunneling
Get-NetRoute -InterfaceAlias "APP_VPN" | Sort-Object DestinationPrefix

带路由的拆分在哪里不再隔离你的应用

常见的故障是一个目标从未出现在隧道的路由列表里。一个先登录某个地址、然后从 CDN 拉流,或解析十几个轮换端点的应用,会把部分连接放在 VPN 接口上,其余留在 Wi-Fi 上。IPv6 是另一张表,因此只推送 IPv4 前缀的配置文件看起来可能没问题,而应用的 IPv6 流量仍留在本地。

两个更小的影响同样重要。最长前缀匹配优先,因此一条更具体的 on-link 路由、为管理主机推送的 /32,或第二个适配器的子网,都可能在你关心的那个地址上压过隧道。名称解析也发生在路由之前:除非配置文件的 DNS 服务器或 NRPT 规则覆盖该名称,否则应用可能通过物理适配器解析,然后拨号一个从未在隧道路由前缀内的地址。

三种形态,以及各自换来什么

这张表是我会贴在墙上的部分,因为决定关乎影响范围而非功能。如果应用的目标是可以列出的稳定前缀,带路由的拆分就能以最小足迹实现隔离。如果不能,那么在虚拟机或第二台设备后面放一个默认路由配置文件,可以诚实地保持隔离,代价是移动不止一个进程。

ShapeWhat starts the tunnelWhat enters itPick it when
Trigger plus routed splitProcess startOnly pushed or added prefixesThe app's endpoints are stable prefixes and the host should stay direct
Trigger plus default routeProcess startEverything, machine-wideYou need one exit for all traffic and accept the wider scope
Separate VM or deviceGuest boot or loginEverything inside that guestThe app's destinations rotate, or the host must keep its own public address

这些方案都不提供主机上的套接字级绑定。经典 Windows VPN 客户端没有“此进程必须使用该适配器”这样的规则;触发器和路由过滤器处在不同层,而选择接口的那一层是路由表。如果某篇指南承诺仅凭一个复制的配置文件就能实现按应用强制,那就问清楚是哪条路由或哪个过滤器在起作用。

一次同时验证两者

按这个顺序分别测试生命周期和路由。关闭应用,确认配置文件报告 Disconnected,然后启动应用并检查状态是否改变。只有隧道已经拉起,才应该去问某个具体目标走向哪里;并且应该使用应用真正拨号的地址,而不是猜测。

$app = Get-Process app | Select-Object -First 1
Get-VpnConnection -Name "APP_VPN" | Select-Object ConnectionStatus
Get-NetTCPConnection -OwningProcess $app.Id -State Established | Select-Object RemoteAddress,RemotePort
Find-NetRoute -RemoteIPAddress 203.0.113.10

Find-NetRoute 会指出承载该目标的接口和下一跳。那里出现物理别名,就意味着隧道虽然拉起,但对这条流无关:先检查推送的前缀,再检查更具体的路由,然后是 IPv6。如果状态根本不变,说明触发器没有触发,那就去看可执行文件路径、配置文件作用域和 VPN 客户端操作日志这三个地方。

干净地撤销,让下一次测试诚实

按构建的相反顺序移除:先移除应用触发器,再移除配置文件。在触发器仍存在时删除配置文件会留下悬空关联,让下一次尝试变得混乱;而且 Remove-VpnConnection -Force 并不总能清除在 cmdlet 之外创建的 phonebook 条目。

每用户条目位于用户的 rasphone.pbk (在 Roaming 配置文件下);所有用户条目位于 ProgramData 下。检查与配置文件作用域匹配的存储,重新连接保留的配置文件,并确认其路由和 DNS 仍然正常,这样后续测试测量的是你所做的改动,而不是残留状态。

  • 隧道在进程启动时建立,而不是在登录时。
  • 只有预期的前缀出现在 VPN 接口上。
  • 应用真正的远程地址解析到 VPN 别名。
  • 移除配置文件后不会留下悬空的触发器。

相关文章