客户端日志显示已推送的 DNS 服务器,隧道也连上了,内部域名却仍然解析不出任何结果。推送本身没有任何问题。选项确实送达了,只是操作系统的解析器从未使用它,因为在大多数 Unix 客户端上,OpenVPN 只负责把值交给系统,实际应用由平台决定。

症状:日志里有 DNS,域名却依然解析失败

OpenVPN 的 dhcp-option 会携带 DNS 和域信息,客户端会记录收到的内容。有些实现由客户端内部应用这些设置;有些实现只把它们导出,交给集成脚本或网络服务处理。日志那一行只描述了故事的前半段。后半段发生在客户端无法控制的地方,故障通常就住在那里。

这就是为什么已连接的隧道和解析失败的内部域名经常同时出现。值有了,使用它的环节却缺失或配置错误,解析器便悄悄继续使用连接之前的那台公共服务器。

收到并不等于应用

客户端可以记录被推送的 DNS 值,而系统解析器仍然归另一个服务所有。浏览器也可能有自己的加密 DNS 策略,完全绕过系统解析器,于是就会出现一个应用能解析内部域名、另一个却不能的难忘局面。

与其假设每个 Unix 客户端都会改写同一个文件,不如检查该实现支持的集成方式。正是这个假设,让人去改一份根本没人读取的解析器配置。

按平台找到解析器的归属方

在 Windows 上,名称解析策略表可以把特定后缀指向特定服务器,VPN 客户端也可能在那里安装规则。要检查策略,而不只是网卡的 DNS 列表。在 Linux 上,配置可能归 NetworkManager 或 systemd-resolved 所有,究竟归谁,决定了重新连接后会保留什么。

用真正会回答查询的工具读取实际状态:在 systemd-resolved 主机上运行 resolvectl query internal.example.net,或使用平台对应的方式。然后把这个回答与客户端日志声称已推送的 DNS 服务器做对比。

不要同时运行多个互相覆盖解析器设置、却毫无协调的服务。两个归属方会让结果看起来时好时坏,每次重连都像是随机修复或弄坏它。

脚本与 foreign_option

OpenVPN 通过名为 foreign_option_N 的变量把推送的选项导出给脚本,脚本可以把它们翻译成解析器命令。这种安排是受支持的集成,不是自动行为,而且必须在客户端上安装并调用。

如果依赖脚本,就记录它的执行情况和退出状态。脚本跑了却静默失败,症状和没有脚本一模一样,而且更难发现,因为配置看起来是完整的。要检查脚本的输出,而不是脚本文件。文件存在,不等于脚本跑过。

测试域名,而不是日志

连接后从客户端查询一个仅限内部的域名,再查询一个公共域名,然后检查是哪台服务器回答的。在正常配置里,两个回答来自不同的地方,而且都该与设计相符。

如果域名能解析但服务失败,就转到路由与应用访问,不要再去改 DNS。OpenVPN 手册记录了 dhcp-option 和脚本环境;在修改下一份解析器文件之前,用它确认客户端应当应用什么。

相关文章