跳转至

DoH 配置实战:在服务器、路由与内网解析器上落地 DNS over HTTPS

传统 DNS 查询走 UDP 53 端口,全程明文。你家的路由器、公司的出口交换机、咖啡馆的 Wi-Fi、还有沿途每一个运营商节点,都能清清楚楚看见你在查哪个域名。这不是危言耸听,DNS 明文是互联网上最容易被大规模采集的元数据之一。DNS over HTTPS(DoH,RFC 8484)把 DNS 查询包进 HTTPS 请求,用 443 端口和 TLS 加密,让旁观者只看到“这台机器在和某个 DoH 服务商通信”,却看不到具体查了什么。

但“知道 DoH 是什么”和“把它落到生产环境”是两回事。本文不重复讲原理,只讲运维真正要动手的部分:怎么在单台服务器上接上 DoH、怎么让整张网统一走加密解析、怎么自建内网 DoH 服务、以及怎么验证它真的生效。目标读者是中掌握了基本网络运维、需要在真实环境里把明文 DNS 替换掉的工程师。

1. 先想清楚:你要加密的是哪一段

动手前先定位痛点,不同位置的做法完全不同:

  • 单台 Linux 服务器:只想让这一台机器的 DNS 不被旁人看见,改本机解析器即可,影响范围最小。
  • 整张办公网 / 家庭网:希望所有终端(手机、笔记本、IoT)统一走加密 DNS,该在路由器或网关上做,向 DHCP 下发的就是加密解析器地址。
  • 对外暴露的服务 / 公网出口:需要从被封锁或受限的网络里把 DNS 透出去,用 cloudflared 这类 DoH 代理最稳。
  • 内网有私有域名:必须分层解析,否则内网服务全断。

先画清楚你的边界在哪,再决定用下面哪一段配置。混用很正常,关键是别让私有域被错误地发到公网 DoH 上。

2. 单台服务器:cloudflared 本地 DoH 代理

这是最通用、对系统侵入最小的方案。cloudflared 在本地起一个 DNS 监听(默认 53),把收到的查询通过 HTTPS 转发到上游 DoH 端点,本机其他进程只要把解析器指向 127.0.0.1 即可。

安装(Debian/Ubuntu):

# 添加 Cloudflare 仓库并安装
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared

配置文件 /etc/cloudflared/config.yml:

proxy-dns: true
proxy-dns-port: 53
proxy-dns-upstream:
  - https://1.1.1.1/dns-query
  - https://1.0.0.1/dns-query

然后以系统服务方式常驻:

sudo cloudflared service install
sudo systemctl enable --now cloudflared

最后把本机解析器指向本地代理。编辑 /etc/resolv.conf 或 systemd-resolved 配置,把 nameserver 改成 127.0.0.1,并保留一个非加密备用(仅当代理挂掉时兜底,注意这会退化成明文,是取舍不是最佳)。验证:

dig @127.0.0.1 example.com

这套方案的好处是:应用层几乎无感,所有进程自动受益;缺点是只覆盖这一台机器,且 cloudflared 本身成了新的单点,需要监控其存活。

3. 系统级:systemd-resolved 的 DoT 与 DoH 误区

很多教程让你在 systemd-resolved 里写 DNS=1.1.1.1#cloudflare-dns.com 加 DNSOverTLS=yes,并号称这是 DoH。严格说,systemd-resolved 的 DNSOverTLS 实现的是 DoT(DNS over TLS),不是 DoH。两者都是加密,但端口和封装不同。这不算错,只是命名要准确,避免向上汇报时口径混乱。

如果你确实要在这层用 DoH,更现实的做法是:把 systemd-resolved 的上游设为上一步的本地 cloudflared(即 DNS=127.0.0.1),由 cloudflared 负责 DoH 封装。这样职责清晰:系统解析器管缓存和本机策略,cloudflared 管加密出口。

4. 整网统一:在路由器上向终端下发加密解析

只改服务器不够,手机和笔记本还是明文。真正的“全站加密”要在网关做。两种思路:

思路 A:网关自己跑 DoH 代理。 在 OpenWrt/爱快/软路由上装 cloudflared 或 dnscrypt-proxy,网关的 DNS 监听对外服务,DHCP 下发给终端的就是网关自己的地址。终端无感知,全部流量在网关汇聚后走 DoH 出去。

思路 B:DHCP 直接下发公共 DoH 端点。 部分现代路由器支持下发“加密 DNS”选项(DoH/DoT 的 bootstrap 域名),终端系统(新版 Android、Windows 11、iOS)能识别并直连。但这要求终端系统支持,老设备会忽略,覆盖不全。

推荐思路 A,因为它不依赖终端能力,且你能在网关统一加过滤和日志。注意:下发后,之前依赖网关 DNS 做广告拦截、家长控制的能力要重新接回网关的解析层,否则这些策略会随明文 DNS 一起消失。

5. 内网自建:用 CoreDNS 提供 DoH 服务

对有一定规模的团队,把解析器全部指向第三方 DoH 不总是合适——你把查询元数据交给了别人。更可控的是自建递归 + DoH 出口:用 CoreDNS 在 443 端口提供 DoH 端点,上游再转发到你信任的递归或公共 DoH。

Corefile 骨架:

https://.:443 {
    tls /etc/coredns/cert.pem /etc/coredns/key.pem
    forward . 1.1.1.1 8.8.8.8 {
        tls_servername cloudflare-dns.com
    }
    cache 300
    log
}

这里有两个工程要点。第一,私有域分流:在 forward 之前加 file 或 kubernetes 插件处理内部 zone,确保 *.internal 不外流。第二,证书:DoH 的 443 端口需要合法 TLS 证书,用 ACME 自动续期,证书过期是这类服务最常见的“悄悄挂掉”原因。

6. 验证:用抓包证明明文真的没了

配置完最忌“配完即上线”。DoH 是否生效,靠看配置是不行的,要靠证伪。

用 curl 打 DoH 的 JSON API 确认解析正常:

curl -s -H 'Accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=example.com&type=A' | jq

用 tcpdump 抓明文 DNS,确认没有外泄:

sudo tcpdump -i any port 53

在 DoH 正确生效时,这条命令应当长时间静默——你不该看到任何发往外网 53 端口的查询。如果还在刷 8.8.8.8.53 的包,说明有进程绕过了:可能是某个容器硬编码了 8.8.8.8,可能是 /etc/resolv.conf 没改干净,也可能是应用直接用了自家 DNS 库。逐个排查,直到抓包静默为止。这一步是 DoH 上线的“质量门”,过不了就不算完成。

7. 选提供商的隐私口径

DoH 加密了路径,但没消除“谁在看”。主流提供商的口径差别很大,选型时应对齐合规要求:

  • Cloudflare(1.1.1.1):承诺 24 小时内删除查询日志,每年由 KPMG 审计,速度通常最快。
  • Google(8.8.8.8):临时记录完整源 IP,24-48 小时内匿名化,稳定性和覆盖好。
  • Quad9(9.9.9.9):总部瑞士,默认不记录 IP,内置威胁情报拦截恶意域名。
  • NextDNS:可定制过滤规则,适合做团队级策略,但属于商业服务。

若数据合规要求严格(如国内数据出境考量),优先自建递归或选管辖权友好的提供商,并保留 DNSSEC 校验。

8. 上线后的巡检与防退化纪律

DoH 配置是典型的“会悄悄退化”项:一次错误的回滚、一次证书过期、一个应用硬编码了明文 DNS,加密就破了,而且表面一切正常。建立纪律:

  • 每日:监控 cloudflared/CoreDNS 进程存活与上游延迟,进程挂了终端会无声退化到明文兜底。
  • 每周:跑一次 tcpdump 抽查 port 53,确认仍有终端在走明文(新接入的设备最容易漏)。
  • 每月:检查 DoH 服务端 TLS 证书剩余有效期,证书过期会导致整个 DoH 端点不可用。
  • 每次变更前:在测试机改解析器验证,再推全量;变更后立刻 dig + tcpdump 双重确认。
  • 红线:禁止在生产里长期保留明文兜底 DNS 而不监控;禁止把内网私有域错误地转发到公网 DoH。

FAQ

DoH 和 DoT 到底该选哪个,站点统一用一种不行吗? 可以分场景混用。DoH 走 443 端口,和网页流量长得一样,最难被网络层识别和封锁,适合需要穿透严格防火墙或运营商侧限制的场景;DoT 走专用 853 端口,协议更轻、实现更简单,适合你在自己掌控的服务器和路由器上做系统级加密解析。很多团队的做法是:内网服务器用 systemd-resolved 的 DoT(或 CoreDNS 转发),需要对外穿透的公网出口用 cloudflared 的 DoH。两者解决的是同一件事——把明文 DNS 加密,只是端口和封装不同,不必二选一。 部署了 DoH 之后,内网的按域名分流、内网私有域名解析会不会全坏? 会坏,这是最常见的踩坑。DoH 把查询发到外部解析器,它根本不知道你内网的 *.corp.local 或路由器下发的私有域名。正确做法是分层:本地先查 hosts 和本机解析器缓存,私有域和内部 zone 走内部权威(或 split-horizon),只有真正的公网域名才转发到 DoH 代理。在 CoreDNS 里用 forward 配合 file 或 kubernetes 插件做 zone 分流,在 cloudflared 上游前先判断域名后缀,避免出现“能上外网却找不到打印机”的尴尬。 怎么证明 DoH 真的生效了,而不是只是配了个寂寞? 两步验证。第一,用 curl 直接打 DoH 接口的 JSON API,确认能返回正确的 A 记录;第二,开 tcpdump 抓 53 端口,在 DoH 生效状态下应当看不到任何发往外网的明文 DNS 包。如果 tcpdump 还能看到发往 8.8.8.8:53 的流量,说明还有某个进程绕过了解析器(常见于容器、硬编码 DNS 的应用、或没改完的 /etc/resolv.conf)。这个“抓包证伪”比看日志更可靠,是上线前的必做项。 把 DNS 全交给 Cloudflare 或 Google,隐私就彻底安全了吗? 没有彻底安全这一说。DoH 只解决“网络路径上的人看不见你查了什么域名”,但 DoH 提供商自己能看到你的查询。所以选择提供商是隐私口径的取舍:Cloudflare 承诺 24 小时删除日志并年度审计,Google 会临时记录完整 IP 后匿名化,Quad9 总部在瑞士且默认不记录 IP。更稳妥的是自建递归解析器(CoreDNS + 上游转发)或同时启用 DNSSEC 校验响应真实性。DoH 是纵深防御的一环,不是终点。 公司网络里开了 DoH,原来的 DNS 过滤、上网行为管理会不会失效? 会,这是安全团队最该提前知道的副作用。基于 53 端口的 DNS 过滤、家长控制、威胁域名拦截,在终端直连外部 DoH 后全部失效,因为流量被加密且绕过了本地 DNS。对策有两条:要么在边界自建 DoH 代理并保留过滤逻辑(CoreDNS 接策略插件),要么通过设备策略锁定只允许走公司指定的 DoH/DoT 端点、禁止随意自选。千万不能“员工自己开了 DoH,安全策略还以为是原来的 DNS 在管事”。

相关文章