你的 VPN 加密的是 DNS,还是只加密下一跳?
VPN 服务器上的加密 DNS 保护的是与客户端 DoT 不同的那一跳。启用之前,先梳理每条查询到底由谁接收。
在 VPN 服务器上启用加密 DNS 常被说成堵住泄漏,而这一说法掩盖了究竟哪一段连接发生了变化。从客户端到解析器的这一跳,与从解析器到上游的这一跳,是分属不同所有者的两条连接。只加密其中一条,另一条仍然原封不动。
在改动配置文件之前,先写清楚每个环节由谁接收查询。
两种安排,在设置页面上看起来一样
第一种安排下,设备自己运行 DNS over TLS,隧道只是把这条连接送到你选定的解析器。TLS 会话终止于解析器,也就是说解析器看得到查询,而 VPN 出口看到的只是它读不懂的密文。第二种安排下,设备把普通 DNS 经隧道发给 VPN 主机上的转发器,由该转发器自己去建立到上游解析器的加密连接。在功能列表里两者都像“配合 VPN 的加密 DNS”,但它们把可读的查询放在了不同的楼里。
差别是可测量的,而不是哲学问题。到客户端上找 TLS 会话,或者到服务器上找,你就知道自己实际是哪一种。
改动之前,先画出解析器链路
数清路径上有几个解析器,并说出每个的角色。转发组件把请求交给另一个解析器;递归解析器负责找出答案,还可能直接与权威服务器对话。这些角色之所以被分开定义,正是出于这个原因,而RFC 9076 的解析器定义值得在假定只有一个上游目标之前先读一遍。自己运行递归解析器的主机,根本没有唯一的“上游”可以加密,整个计划也就作废了。
然后问:客户端被配置为使用哪个组件。VPN 配置文件下发的 DNS 地址只说明查询发往何处,并不说明由哪个进程作答。
也要注意失败时的行为。加密的上游一旦不可达,转发器是失败关闭、退回明文,还是改用另一个解析器?这个决定应当写进记录,因为在隧道最不健康的那一刻,正是它在改变隐私保证。
确认是哪一跳被加密
先在客户端确认它是否有到 853 端口的 TLS 会话。一条经 53 端口正确解析的查询,并不能证明任何加密。
resolvectl status | grep -E "DNS Servers|DNS over TLS"
kdig +tls +tls-ca +tls-hostname=dns.example.net @198.51.100.53 example.com A
再到 VPN 主机上,找转发器打开的出站加密连接。socket 表能告诉你上游 TLS 会话是否存在,以及由哪个进程持有:
ss -tunp '( dport = :853 )'
dig @127.0.0.1 example.com A +short
如果客户端有 TLS 会话、服务器没有,那你属于第一种安排,服务器在 IP 层转发的查询它读不到。如果服务器有 TLS 会话、客户端在隧道里只是普通的 53 端口流量,那你属于第二种安排:后一段被保护了,但转发器仍然读得到设备请求的每一个名字。第二种结果不算故障,但把它称为端到端加密是错的。
记录所有者与失败行为
所有权决定你可以承诺什么。设备所有者掌握客户端一侧的加密连接,而 VPN 管理员掌握其转发器创建的下一段连接。两样都不运营的人,只能描述这条路径。
启用任何东西之前,先检查内部命名。把组织内部名称发给公共解析器并不是等价设计,而只存在于内网的 split-horizon 区域在上游根本解析不了。改动之后测试一个内部名称和一个公开名称,并把结果与配置改动放在一起。如果加密路径破坏了内部主机的解析,修法是限定范围的解析器规则,而不是把隧道开得更宽。
最后为每一跳写一句话:谁接收了查询、这一跳有没有加密、谁能读到这个名字。当关于 VPN 主机的那句话仍然写着它能读到查询,文档就应当照实写,因为一份反着说的图,总会被从没跑过这些命令的人信以为真。