VPN 服务器在 TCP 10443 上应答,Windows 内置客户端的凭据也没问题,但连接仍会在一次看起来像 DNS 超时的停顿后断开。原因通常是地址栏:在「服务器名或地址」里输入 vpn.example.com:10443,Windows 会把整个字符串当成主机名来读取,因为这个框并不是通用的 host:port 输入。端口能否写在这里,取决于配置文件使用的隧道类型,四种类型中只有一种会把它当作可解析的值。

地址栏为什么不是 host:port 输入框

这个栏位背后的配置文件属性是 ServerAddress,每种隧道类型各自决定如何处理该字符串。其中三种根本没有端口参数可填,于是冒号和数字只是一个永远解析不出的名字里的字符。第四种走 HTTPS,端口本就是 URL 的一部分,所以必须写成能被当作 URL 解析的形式。

这就是为什么 故障看起来像名称解析失败,而不是连接被拒绝。没有「端口错误」可报,只有一个不存在的主机名,而错误在解析器放弃之后才出现。动服务器之前,先确认配置文件声称使用哪种隧道类型。

端口固定的三种类型

PPTP 连接 TCP 1723,随后用 GRE(IP 协议 47)承载隧道。L2TP/IPsec 用 UDP 1701 承载隧道,用 UDP 500 和 4500 承载 IPsec,其中 4500 是 NAT 穿透端口。IKEv2 同样使用 UDP 500 和 4500。这些配置文件的界面里都没有端口字段,也都不会协商非标准端口。

因此,在自定义端口上监听的服务器对这几类配置文件来说不可达,地址怎么写都一样。实际可行的答案很窄:在服务器上发布标准端口,或者在它前面的路由器上把标准端口转换为自定义端口。两种做法都不用改动客户端配置文件,而当配置文件是通过策略下发而非手工输入时,这一点很有价值。

SSTP 要的是 URL,不是名字

SSTP 是例外,因为它跑在 TLS 会话里。这个栏位必须能按 URL 解析:先是 https:// 方案,然后是主机,然后是冒号和端口。没有方案时,Windows 仍把该值当作普通名称,连接在任何 TLS 交换发生之前就失败。

让人意外的是,标准端口下裸主机名是可以接受的。从不需要自定义端口的配置文件不会教这条规则,而第一个添加端口的人只能靠失败来发现它。保存之后,不要相信对话框,而要把存储的值读回来:

Get-VpnConnection -Name "Corporate SSTP" | Select-Object Name, ServerAddress, TunnelType

ServerAddress 显示的正是存进去的内容。如果读到的是一个带冒号的裸主机名,说明配置文件从未收到 URL,重试多少次都修不好。

会被粘进同一个框的厂商语法

有些网关会为这个栏位记录额外约定,比如端口之后追加的 realm,或者改变证书处理方式的查询参数。这些只有该厂商的服务器懂,部分版本里它自己的客户端也懂。它们不属于 Windows 配置文件的格式,粘进内置配置文件通常还是同样的静默名称失败。

把关闭证书校验的参数当作警告而不是修复。如果某个配置文件只有带上这个参数才能连接,说明证书与所用名称不匹配,正确的修复在网关侧,不在客户端。端口之后的 realm 也一样:只有对端服务器确实期待它时才起作用。

先证明端口,再读失败阶段

在责怪配置文件之前,先从客户端所在网络确认该端口上确实有东西接受 TCP:

Test-NetConnection vpn.example.com -Port 10443

成功只证明有监听者,不证明会讲 SSTP。Web 服务器、反向代理或负载均衡器可以完成 TCP 握手却仍然不说 SSTP,所以测试是绿的而 VPN 是红的,这种组合真实存在。把证书主体与配置文件里的名称对比,并在自带 RasClient 日志的版本里查看日志,因为它记录的是失败阶段,而不是笼统的超时。

然后考虑那个能直接消除问题而不是记录问题的选项:把外部的 TCP 443 转发到 VPN 服务器的 10443。这样客户端使用的默认配置文件完全不含端口,对受管设备群通常是最干净的做法,自定义端口则成为内部细节。

相关文章