我的 ISP 在劫持我的 DNS

homelab 里的一台容器连不上某个网站,DNS 应答却指向了 ISP 自己。作者复盘了证据,再澄清 Unbound、DNSSEC 和 DoH 到底能挡住什么、挡不住什么。

中文
复制
终端里执行 curl ipinfo.io 查询一个 IP,返回结果显示归属 org 为 AS24309 Atria Convergence Technologies,即印度宽带运营商 ACT Fibernet,机房城市为班加罗尔

一个无聊的周六下午,我打算修一修 homelab 里的几个问题。其中一个问题是,我的 Podman 容器要访问的一个外部站点就是连不上。

几乎立刻就弹出“Site cannot be reached”。我怀疑是 DNS 配置出了问题,于是对这个域名做了一次 dig。我换了几家不同的服务商,Google DNS、Cloudflare,基本上都试遍了。几乎所有的都返回同一个 IP 地址。可我一试 Cloudflare 的 DoH 端点,拿到的 IP 地址完全不同。

“嗯?”就是我当时的原话。

我拿第一次 DNS 解析得到的 IP 去查归属,结果你猜怎么着,是我那该死的 ISP。我这才意识到,ISP 在劫持我的 DNS 查询,返回一个被篡改过的 IP 地址。

一次明文 DNS 查询,返回的是我 ISP 的 IP 地址

同样的查询走 Cloudflare 的 DoH 端点,返回的是真实 IP 地址

IP 归属查询显示,被篡改的地址属于我的 ISP

显而易见的解法是 DoT(DNS over TLS)或 DoH(DNS over HTTPS)。我为此发了条推文,不知怎么就火了。

但翻看回复时,我发现不少误解和疑问,我没法一条条回复。

于是我想借这篇文章回应其中一些问题,顺便澄清几个误解。

我自己搭了 Unbound,我很安全

可能有人还不知道,Unbound 本质上是一个 DNS 解析器,类似 Cloudflare 在 1.1.1.1 上跑的那种。区别在于,你可以在本地运行它。它的工作方式是逐级查询 DNS 服务器来确定一个域名的 IP 地址,从根域名服务器开始,一路查到该域名的权威域名服务器。

问题就在这里。DNS 解析并不是在一条连接上完成的。你的解析器可能得跟多台域名服务器通信才能确定一个 IP 地址,而这些查询传统上走的是明文 DNS,通常是 UDP。也就是说,你猜对了,你的 ISP 可以窥探甚至篡改你的 DNS 流量。

不过,你也可以在 Unbound 里配置 forward-zone。这样 Unbound 就不再自己解析 DNS 查询,而是把它们转发给另一个 DNS 解析器。如果那个解析器支持 DoT,与它之间的连接就是加密的,你的 ISP 也就无法在传输途中随意窥探或篡改你的 DNS 查询。

直接用 DNSSEC 就行了

有人会以为在这里保护自己靠的是 DNSSEC,这可以理解。毕竟名字里就写着 DNS 和 Security。从某种意义上说确实如此,但它靠的是验证,不是加密。

DNSSEC 并不会在把 DNS 流量发给解析器之前先加密。它用密码学签名来验证 DNS 数据是真实的、没有被篡改。但问题依旧:你的 DNS 流量在传输途中仍然没有加密。也就是说,ISP 照样能窥探,或者对响应动手脚。

而且要让 DNSSEC 验证生效,你查询的域名必须正确配置了 DNSSEC,有有效的签名和一条信任链。这里还有另一个问题:根据 2026 年 10 月的 Defaults.Exposed 普查,只有 7.03% 的受评域名拥有有效且能完整验证的 DNSSEC 链。

如果被篡改的响应通不过 DNSSEC 验证,你的解析器会返回 SERVFAIL,而不是把那个伪造的 IP 地址交给你。

DoH 和 DoT 并不能防篡改

没错,确实如此。DoT 和 DoH 的前提是你信任自己把查询发过去的那个 DNS 解析器。它们加密了连接,所以 ISP 无法在传输途中随意窥探或篡改你的 DNS 流量。但这并不意味着解析器本身不会对你的响应做手脚。你仍然在信任那台 DNS 服务器的运营者。

无论如何,ISP 都能窥探你访问的网站

确实。虽然你的常规互联网流量由 TLS 加密,但 ISP 能在 TLS 握手时看到主机名(SNI)。有时他们仅凭目标 IP 就能判断你在访问哪个网站。

当然,除非你把流量通过 VPN 隧道转发。我用的是 Tailscale 的 exit node 功能跑 Mullvad。

当然,这不过是把一部分信任转移给了你的 VPN 服务商。不过那就是另一个话题了。

那我的配置是什么样的?

我用 Pi-hole/AdGuard 已经三年多了。说起来,正是它让我第一次接触到自托管。

不过,我不把每个容器都走本地 DNS 服务器,还有另一个原因。更新容器的时候,万一出问题、DNS 服务器挂了,我不希望这连带影响其他依赖它的容器的 DNS 解析。所以对某些容器,我想要一个外部 DNS 解析器作为兜底。

目前我的 DNS 用的是 AdGuard,上游 DNS 查询由 dnscrypt-proxy 通过 DNSCrypt 处理,因此 DNS 流量在传输过程中是加密的。对于那些实在承受不起 DNS 故障的容器,我还配置了 Cloudflare 作为兜底。不过它们的主 DNS 仍然是本地的 AdGuard。

我的服务器上还跑着 NixOS,如果你感兴趣,我的 dotfiles 是开源的。

来源: Karthikey Hegde's Blog← 返回首页