DNS查询的明文传输,是互联网最底层也最容易被忽视的安全短板。每次你通过域名访问网站,系统发出的DNS请求就像一张没有信封的明信片,途经的任何一个网络节点都能看清你要去哪里。运营商劫持、中间人篡改、公共WiFi的恶意重定向,本质上都是利用了这个弱点。DNS over TLS(DoT)通过在传输层加密DNS流量,让这张明信片装进了保险箱,从根源上切断篡改的可能。
Ubuntu 20.04及之后的版本已经内置了systemd-resolved服务,它原生支持DoT,不需要安装额外软件包。但很多人配置失败,是因为没有理解systemd-resolved的工作机制,或者混淆了传统resolv.conf的配置方式。下面直接进入实操环节,每一步都拆解清楚。
确认当前DNS解析架构在动手之前,先搞清楚你的系统现在是谁在管DNS。运行以下命令查看resolv.conf的实际指向:
ls -l /etc/resolv.conf
如果输出显示该文件指向/run/systemd/resolve/stub-resolv.conf或/usr/lib/systemd/resolv.conf,说明systemd-resolved已经在管理DNS。如果是其他情况,需要先启用systemd-resolved:
sudo systemctl enable --now systemd-resolved sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
这一步确保后续所有配置都通过systemd-resolved生效,而不是被NetworkManager或其他网络管理工具覆盖。
修改resolved.conf启用DoT核心配置文件是/etc/systemd/resolved.conf。用编辑器打开它:
sudo nano /etc/systemd/resolved.conf
找到[Resolve]段落,需要修改或添加以下三个关键参数。这里直接给出生产环境可用的配置,并解释每个参数的意义:
[Resolve] DNS=1.1.1.1 9.9.9.9 223.5.5.5 FallbackDNS=1.0.0.1 149.112.112.112 223.6.6.6 DNSOverTLS=yes DNSSEC=allow-downgrade Cache=yes DNSStubListener=yes
DNS参数指定主用DoT服务器。这里选了三组:Cloudflare的1.1.1.1、Quad9的9.9.9.9、阿里云的223.5.5.5。systemd-resolved会优先向第一个服务器发起DoT查询,如果失败则依次尝试后面的。三组不同运营商的服务器提供了充分的冗余,避免单点故障。注意这里写的是IP地址而非域名,因为DoT握手时本身就需要DNS解析,用IP可以避免循环依赖。
FallbackDNS参数是兜底服务器,当主DNS全部不可用时才会启用。这里配置了对应服务商的备用IP。如果主DNS和FallbackDNS全部失效,systemd-resolved会回退到传统明文DNS,但这需要把DNSOverTLS设为opportunistic模式,出于安全考虑我们使用yes模式,所以FallbackDNS也会尝试DoT连接。
DNSOverTLS=yes是强制模式,意味着所有DNS查询必须通过TLS加密。如果DoT服务器不可达,查询会直接失败而不是降级为明文。这是防止降级攻击的关键设置。如果你需要一定的容错性,可以设为opportunistic,它会尝试DoT但允许回退明文,不过这样就留下了劫持窗口。
DNSSEC=allow-downgrade开启DNSSEC验证,对返回的DNS记录进行签名校验,防止缓存投毒。allow-downgrade模式会在服务器支持DNSSEC时进行验证,不支持时放行,兼顾安全性和兼容性。
验证DoT是否生效保存配置文件后重启服务:
sudo systemctl restart systemd-resolved
然后通过resolvectl命令查看当前状态:
resolvectl status
输出中重点关注Global部分的DNS服务器列表,以及每个网络接口的DNS配置。如果看到DNS服务器前面标注了“DNS Servers”,并且协议显示为“DNS-over-TLS”,说明配置成功。更直接的验证方法是抓包观察:
sudo tcpdump -i any port 853 -n
端口853是DoT的标准端口。打开另一个终端执行一次DNS查询:
resolvectl query ubuntu.com
如果tcpdump捕获到了与1.1.1.1或9.9.9.9等服务器在853端口的加密通信,DoT就已经在工作了。你看到的不再是明文的UDP 53端口数据包,而是TLS加密的TCP连接。
处理多网卡和VPN接口的DNS泄漏很多人在配置完resolved.conf后发现,连接公司网络或某些虚拟网络后,DNS又变成了运营商下发的服务器。这是因为systemd-resolved支持按网络接口独立配置DNS,而DHCP客户端或NetworkManager会为每个接口推送各自的DNS服务器,覆盖全局设置。
查看每个接口的DNS配置:
resolvectl dns
你会看到类似eth0、wlan0等接口各自有DNS服务器列表。要让所有接口都走全局DoT配置,需要修改NetworkManager的DNS处理策略。编辑/etc/NetworkManager/conf.d/dns.conf文件(如果不存在就新建):
sudo mkdir -p /etc/NetworkManager/conf.d sudo nano /etc/NetworkManager/conf.d/dns.conf
写入以下内容:
[main] dns=systemd-resolved systemd-resolved=false
这里dns=systemd-resolved告诉NetworkManager将DNS管理权完全交给systemd-resolved,而systemd-resolved=false是让NetworkManager不要尝试接管resolv.conf。然后重启NetworkManager:
sudo systemctl restart NetworkManager
再次运行resolvectl dns,确认每个接口的DNS服务器都变成了你在resolved.conf中设置的全局服务器。如果某个接口仍然显示运营商DNS,检查/etc/netplan/下的网络配置文件,确保没有在接口级别硬编码nameserver。
进阶:自建DoT代理实现内网统一防护如果管理多台Ubuntu服务器或家庭网络中有多台设备,逐台配置DoT效率太低。可以在内网部署一台DoT代理,比如用AdGuard Home或dnscrypt-proxy搭建本地DNS服务,上游走DoT,下游对内网设备提供传统DNS服务。这样手机、智能电视等不支持DoT的设备也能间接获得加密DNS的保护。
以dnscrypt-proxy为例,安装后配置其监听内网IP,上游指定DoT服务器,然后在Ubuntu的resolved.conf中将DNS指向这台内网代理的IP即可。这种方式还能叠加广告过滤、恶意域名拦截等功能,把DNS安全从单点防护升级为网关级防护。
故障排查清单DoT配置失败通常集中在几个环节。如果resolvectl status显示DNS服务器但协议不是DoT,检查防火墙是否放行了853端口出站流量。有些企业网络或云服务商的安全组默认只允许80和443端口,853端口被阻断会导致DoT回退失败。
如果DNS解析完全失败,检查DNSOverTLS是否误设为yes但指定的DNS服务器不支持DoT。并非所有公共DNS都支持DoT,比如常见的114.114.114.114就不支持。务必使用确认支持DoT的服务器,前文推荐的三组都经过验证。
如果配置被覆盖,检查/etc/resolv.conf的符号链接是否被其他进程修改。某些VPN客户端或网络管理脚本会强行替换这个文件,导致systemd-resolved的配置被绕过。可以用chattr命令锁定resolv.conf的不可变属性,但更根本的解决是找出并停用那个捣乱的进程。
DNS over TLS配置完成后,你的Ubuntu系统发起的每一次域名查询都在TLS隧道中完成,运营商和中间人看到的只是你和DNS服务器之间加密的流量,无法知道你在访问什么网站,更无法篡改解析结果。结合HTTPS,从DNS到应用层形成完整的加密链路,域名劫持这个古老的攻击手法在你这里就彻底失效了。
