上周,dig +short 为你的 AWS Client VPN 配置文件里的端点名称返回了一个地址,隧道也连上了。这周同一个配置文件在你的笔记本上停在“Connecting”,而同事那份没动过的副本仍然可用。没有任何东西被封锁,证书也没有过期。有人把端点主机名换成了那个解析出来的地址,服务就不在那里应答了。导出时拿到的名称,正是为了扛住这种变化而设计的部分。

配置文件里的名称未必是被查询的那个名称

从 Client VPN 导出的配置文件通常带有 remote-random-hostname。打开该选项后,客户端会构造一个一次性标签,用点号把它和端点名称拼在一起,再解析这个组合名。端点所在区域的通配符记录会回应任意标签,所以凭空编出的前缀照样能解析,也正是这个原因,当你手动复现查询时,这一手法看起来毫无痕迹。

因此,当你把端点名称粘贴到终端并得到答案时,你解析的是另一个名字,而非客户端连接时使用的那个名字。这一不一致并不是配置文件里需要修正的缺陷。标签是有意重新生成的:该选项让缓存无法保留过期答案,而且它永远不会标识你的机器或会话。

我遇到过客户端完全忽略该选项的兼容性情况。AWS 记录了变通做法:保留真正的端点名称,并在前面加上一串随机字符和一个点,把它当作命名模式,而不是一个可以粘贴的值。把这一做法用在旧客户端上是合理的;把带前缀的字符串硬编码为端点则不合理。

为什么保存下来的答案无法一直正确

Client VPN 端点地址由 AWS 管理,会随时间变化,最佳实践文档对此说得很直白。抓到的答案只是一次解析的快照。把答案写死,服务一迁移,配置就指向了已下线的机器,而故障看起来很少像 DNS:你会得到连接超时或卡住的握手,于是人们去追路由和证书。

同一个答案写进 /etc/hosts 也会继承同样的失效,还多一个问题。hosts 条目能扛过重启、重新导出和“重装客户端”,所以地址一变,故障就回来。我见过一条记录几周里毫无影响,随后让所有带着它的机器集体失联,而全新安装的机器却正常连接。同一账号里一组故障、另一组正常,这种分化是你能拿到的最便宜的线索。

随机化的范围也要弄对。它发生在每次连接尝试。已建立的隧道不会重新解析端点,不会在会话中途切换服务器,单个应用请求也不会各自挑选新的 VPN 主机。只有下一次尝试才会重新解析,所以一个下午都正常的会话,说明不了明天早上会怎样。

隧道建立时服务会检查什么

Client VPN 要求你连接的地址与端点 DNS 名称解析出的某个地址相匹配。这一条要求解释了几种看似无关的故障:指向别处的自定义 DNS 记录、把流量转给真实端点地址的自定义前端,以及被改写成裸地址的配置文件。AWS 把“自定义记录加转发”这一组合记录为在较新 AWS 客户端上的故障。这项检查很窄;它并不是说所有别名都被禁止。

它也不是关于 VPN 协议的通用规则。数字形式的 remote 在普通 OpenVPN 部署里完全合法,很多部署就是这么跑的。约束来自受管的架构,而不是底层协议。

也正因如此,放宽证书校验是错误的反射动作。如果地址更换后配置文件失败,关掉服务器校验只是把错误挪个位置,既没有修好解析,也没有修好地址匹配要求,还留下一个什么都信的客户端。导出拿到的 CA、服务器校验和双向认证设置,请原样保留。

允许列表、出口与私有 DNS 是三个独立问题

笔记本连接之前必须先放行目的地的防火墙规则,针对的是外层监听。把端点名称、传输方式和配置端口交给网络负责人,再问他们的管控能否跟随服务的 DNS 行为。只记住一个抓来的地址的规则,是一张没写有效期的快照。如果管控只接受不可变的目的地,就在上线前把这个缺口定下来,而不是悄悄写死一个答案。

需要在隧道建立之后发出的请求拥有固定源地址的伙伴,诉求不同:那是出口。网站看到什么,取决于应用路由和拆分隧道策略,而不是客户端连到了哪个地址。走拆分隧道的流量可能根本碰不到 AWS。

隧道内部的私有 DNS 是第三层。下发给客户端的解析器在连接之后回答内部名称;隧道建立之前,它无法解析公网端点。端点解析失败与内部应用解析失败发生在不同阶段,混在一起就会把你引到错误的日志。

地址族也属于同一类清单。IPv4 的答案说明不了 IPv6 或双栈端点允许走哪条路,所以在一个地址族里拍下的快照,不能当作另一个地址族的证据。

在不固定任何东西的前提下检查一次连接

从导出原样的配置文件开始,读它实际要求什么:

grep -nE '^remote|remote-random-hostname' profile.ovpn
dig +short cvpn-endpoint-ID.prod.clientvpn.REGION.amazonaws.com
ss -tnp | grep -E ':(443|1194)'

然后比较三样东西:配置文件里的端点名称、客户端为一次新尝试报告的地址,以及这个名字现在解析成什么。解析成功只证明记录有应答;传输与认证是分开的,端点也不必回应 ping。中间那一项只有客户端自己的日志能提供,所以要在尝试过程中抓,而不是事后补。

What you seeUsual causeNext check
One machine fails, others connectPinned address or stale hosts entry on that machineDiff its profile and hosts file against a working copy
Everyone fails at once, no config changeThe endpoint address rotatedConfirm the current endpoint configuration with the account owner
Tunnel up, internal names failClient DNS policy, not endpoint resolutionInspect the resolver pushed after connection

今天能应答的地址只说明今天;名称才是服务持续维护有效的东西。把观察记下来,发给端点的主人,让配置文件继续指向名称。

要丢掉两个习惯:为了绕过卡住的握手而去关证书检查,以及抱着“总有一个能粘住”的心态在端点名称前堆标签。标签要么通过通配符记录解析成功,要么问题就在别处。配置正确的配置文件仍然失败时,请提供准确的查询和连接事件,而不是一个更长的主机名。

收尾之前:配置文件仍然带着导出时的端点名称,没有任何 hosts 文件固定该地址,客户端日志里的 remote 与当前解析一致,防火墙规则点名的是端点而不是地址。这也是要交给下一个人的状态。

相关文章