一名 Windows VPN 用户应能访问某台服务器上的 HTTPS,却不能访问同一台服务器上的 SMB,而路由无法表达这一点。两个请求携带相同的目的地地址,因此把流量送入隧道的那个前缀,也一并携带了你想要排除的流。要把两者分开,需要一条检查比目的地更细的规则。

两种不同的意图要用两种不同的 Windows 机制来构建。把它们混在一起,会得到一份看似完整、却只执行了一半意图的策略。

路由无法在同一地址上区分两个服务

原生 VPN 路由基于前缀:它决定哪些目的地走隧道、哪些留在外面。要把一个子网放在隧道内、另一个放在隧道外,它正是合适的工具,而它在构造上对端口一无所知。VPN 配置文件上的流量筛选器增加了第二个维度:它把协议、远程地址和 远程端口 与允许穿越该接口的内容进行匹配。IPsec 规则在第三个位置起作用,表达哪些流需要保护、与谁进行身份验证。三者回答不同的问题,谁也不能替代谁。

先把你想要的那句话写下来。「只允许 HTTPS」是访问语句,而「为 HTTPS 要求 IPsec」是保护语句,两者通向不同的配置。

访问限制与保护要求走向不同

访问限制说明什么可以使用该接口,它的执行方式是允许一个集合、拒绝其余一切。保护要求说明哪些流必须加密并验证身份,它的执行方式是协商与之匹配的安全关联。前者在某项不在清单上时产生被阻止的请求,后者则产生要么携带所需保护、要么根本无法建立的连接。把两者混为一谈,策略最终就会什么都不拦、或什么都不保护。

实际后果是:筛选列表是一个封闭集合。配置文件上只要存在列表,该接口上不匹配任何条目的流量就会被拒绝,因此 DNS、身份验证交换以及应用所需的任何依赖都需要各自的条目。为让拒绝安静下来而加一条宽泛规则,会重新打开整个接口,并悄悄取消这项限制。列表应当短到要求允许的程度,同时完整到应用需要的程度。

把筛选列表构建成完整集合

VPNv2 配置文件将筛选列表与路由列表分开承载,其条目是配置文件属性而不是命令。以 HTTPS 为例,条目需要协议 6、远程地址和远程端口,并通过管理通道或针对所用配置文件类型的 ProfileXML 进行部署。Microsoft 的 VPNv2 流量筛选器定义 指出,同一条规则内的条件会合并,而不同规则标识符则作为替代方案起作用。

TrafficFilterList/0/Protocol            = 6
TrafficFilterList/0/RemoteAddressRanges = 10.60.4.20/32
TrafficFilterList/0/RemotePortRanges    = 443
RouteList/0/Address                     = 10.60.4.0/24

该列表中的端口值描述的是远端服务端口,本地端口应保持不受限制,这样客户端临时源端口才不会被过滤掉。把这件事做反,会得到一条什么都不匹配的规则,以及一个要耗掉一下午的谜题。

把路由和筛选器写成两个独立的决定。路由决定这台服务器经隧道到达,筛选器决定哪些服务可以使用这条路径。

用两个请求来证明两种结果

分别测试被允许的流和被拒绝的流,并去读失败原因,而不是缺页本身。很快到达的拒绝是策略决定,而超时更可能是缺少路由或服务未在监听。下面的命令会跑两个请求,并显示当前生效的规则。

Test-NetConnection 10.60.4.20 -Port 443
Test-NetConnection 10.60.4.20 -Port 445
Get-NetIPsecRule -DisplayName *Vpn*
Get-VpnConnection -Name Corp

SMB 测试被快速拒绝、HTTPS 测试成功,正是筛选器本应产生的结果。两个端口都被拒绝,说明列表缺少会话所需的某项内容;两者都超时,则指向路由而不是筛选。如果受保护的流在 IPsec 输出中显示 没有安全关联,那么即使连接成功,保护要求也没有满足。要把四份输出放在一起读,因为单独任何一份都能支撑另一种说法。

把生效的配置文件、规则集和对端策略与结果一并记录。关于较旧 Windows 客户端的历史兼容性说明,并不描述今天受管配置文件功能在特定网关上支持什么。与版本相关的行为属于记录,而不属于假设。

相关文章