隧道上的客户端能到达网关,网关也能到达内部解析器,所以 DNS 本应正常工作。它却没有,因为这是两种不同的权限,而一个可用的 DNS 服务是第三件事。监听器必须存在于客户端能到达的地址上,防火墙必须允许发往该地址的查询,而客户端必须被告知去使用它。把隧道双向打开只是碰巧解决了前两件事,第三件仍然没有着落。

监听器是具有两个方向的本地服务

入站方向,客户端把查询发往网关隧道地址的 53 端口。出站方向,守护进程把该查询转发给它配置的上游。两半都必须工作,而且它们以不同方式失败。

客户端报告的超时和网关上看到的超时并不是同一个故障。

绑定隧道地址,而不是所有地址

这个用途的 dnsmasq 专用实例需要一个明确的监听地址、一个能容忍隧道在启动后才出现的绑定模式,以及一个清晰的上游。这是我用的最小版本:

listen-address=127.0.0.1,10.90.0.1
bind-dynamic
no-resolv
no-hosts
server=192.168.50.53

bind-dynamic 绑定各个地址,并在 tun0 稍后出现时也能应对,这在 VPN 服务会重启的主机上很重要。列出 loopback 可以让本地工具继续工作,而不必添加公开监听器。no-resolv 阻止守护进程从主机的解析器配置继承上游,no-hosts 则阻止这个实例发布网关的 hosts 文件。这些选项都不会授予任何人对该服务发起查询的权限。

在启动第二个守护进程之前,检查是否已有53 端口上现成的监听器,并使用发行版的配置所有者,而不是错误目录里的散落文件。

允许 DNS 入站,而不是隧道的转发

让这一切成立的防火墙规则是本地入站规则,范围限于隧道接口、客户端地址池、网关地址和 53 端口。每个协议一条、共两条规则就够了:

sudo ufw allow in on tun0 proto udp from 10.90.0.0/24 to 10.90.0.1 port 53
sudo ufw allow in on tun0 proto tcp from 10.90.0.0/24 to 10.90.0.1 port 53

DNS 既走 UDP 也走 TCP,所以只针对 UDP 的规则会让大响应以看起来时断时续的方式失败。这些并不是ufw route 的权限,也不能变成对tun0 的广泛放行。顺序和内容同样重要:前面那条丢弃来自tun0 的全部入站的规则依然会赢,而在最终丢弃之下追加放行不会产生任何效果。

如果网关 DNS 现在已成为设计的一部分,请有意识地修改那项更早的策略,并记录原因。

上游这一半是一条单独的路径

查询以网关自己的地址离开网关、发往上游解析器,所以网关到该解析器的路由和出站策略必须允许这次交换。这与让客户端到达监听器的那条规则不同,失败时的症状也不同。

不要为了让应答工作而添加不受限制的隧道出站;被跟踪的应答不需要它。

推送该选项,然后检查交接

服务器可以通过推送选项把解析器通告给客户端。那是一个选项,而不是操作系统必须服从的指令:

push "dhcp-option DNS 10.90.0.1"

Linux 命令行客户端可能需要其连接管理器的解析器集成,受管设备可能需要更新管理配置文件。连接之后请确认客户端上生效的解析器,而不是假定推送已经生效。当两套机制同时试图设置 DNS 时,就会出现一种表现为解析时好时坏的竞争。

目标是让客户端解析器配置只有一个所有者,而只有当客户端平台同意时,推送通常才是那个所有者。

测试你实际构建的东西

直接以隧道地址查询一个私有完全限定名和一个被允许的外部名称,UDP 和 TCP 都要测。然后通过应用的常规查找路径再重复一次。

如果直接查询成功而应用仍然失败,故障就在交接而不在服务器:

  • 客户端从未应用被推送的选项。
  • 后来设置了另一个解析器并赢得了竞争。
  • 应用使用自己的解析器库或缓存的应答。

最后确认:经批准的 VPN 路径之外的来源完全无法使用该监听器。成功的解析并不授权它返回的地址,所以要把这两个问题分开。

相关文章