Windows 按应用 VPN 触发器与拆分隧道:哪一个真正隔离
应用触发的 Windows VPN 配置文件只会拉起一条隧道。带路由的拆分隧道决定什么进入隧道,而两者对单个应用失败的方式不同。
两种 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 规则覆盖该名称,否则应用可能通过物理适配器解析,然后拨号一个从未在隧道路由前缀内的地址。
三种形态,以及各自换来什么
这张表是我会贴在墙上的部分,因为决定关乎影响范围而非功能。如果应用的目标是可以列出的稳定前缀,带路由的拆分就能以最小足迹实现隔离。如果不能,那么在虚拟机或第二台设备后面放一个默认路由配置文件,可以诚实地保持隔离,代价是移动不止一个进程。
| Shape | What starts the tunnel | What enters it | Pick it when |
|---|---|---|---|
| Trigger plus routed split | Process start | Only pushed or added prefixes | The app's endpoints are stable prefixes and the host should stay direct |
| Trigger plus default route | Process start | Everything, machine-wide | You need one exit for all traffic and accept the wider scope |
| Separate VM or device | Guest boot or login | Everything inside that guest | The 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 别名。
- 移除配置文件后不会留下悬空的触发器。