DNS 泄漏的判断边界

DNS 泄漏指域名查询偏离预期的解析路径。例如,浏览器访问已经通过代理,但查询域名的请求仍由本地运营商 DNS 处理。网页能够打开、出口 IP 已经变化,都不能单独证明 DNS 同样经过了预期路径。排查时要分开观察应用连接、DNS 上游与 DNS 连接的实际出口。

本文配置以使用 mihomo 内核的客户端为对象。历史 Clash 内核与不同客户端的字段支持存在差异,修改前先在客户端的「关于」或内核信息页记录内核名称、版本和当前配置来源。菜单名称随客户端变化,下面直接使用配置字段说明,避免把某个客户端的开关位置当作通用路径。

检测步骤:建立基线,再比较代理状态

1. 记录未接管时的结果

  1. 暂停代理接管,记录当前公网出口 IP、系统配置的 DNS 地址和浏览器安全 DNS 设置。
  2. 访问 https://www.dnsleaktest.com/,分别运行 Standard test 与 Extended test,记录解析器 IP、组织名称和检测时间。
  3. 开启当前使用的系统代理或 TUN,清理缓存后,在同一个浏览器再次运行两种测试。
  4. 保持节点、浏览器和网络不变,每次只调整一项 DNS 设置,再重复检测。不要同时更换节点、解析器和接管模式。

在线检测通常通过随机子域名观察递归解析器来源。结果中的地址往往属于解析器的递归出口,不是设备出口,也不一定等于配置中的 1.1.1.1 等接入地址。公共 DNS 的 Anycast 调度和地理库误差还会影响显示地区,因此“解析器国家与节点国家不同”只能作为线索,不能直接判定泄漏。

2. 清理缓存并保留测试条件

Windows 可在终端执行以下命令;macOS 的第二条命令需要管理员授权。清理后关闭并重新打开浏览器,必要时重新连接代理,避免旧连接继续复用原来的解析路径。

# Windows
ipconfig /flushdns

# macOS
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

# 使用 systemd-resolved 的 Linux
resolvectl flush-caches

不是所有 Linux 发行版都使用 systemd-resolved;如果命令不存在,应检查当前使用的解析服务。测试记录建议包含五项:时间、接管模式、浏览器安全 DNS 状态、解析器组织和实际连接出口。这里不提供虚构的实测百分比,修复是否生效应以当前设备的重复检测结果为准。

3. 按证据判读,而非只看地址数量

检测现象可能原因下一步
出现本地运营商解析器系统 DNS 未接管,或策略明确直连检查 TUN、53 端口请求与 DNS 策略
只出现公共解析器内核或浏览器正在使用公共 DNS继续核对连接出口,不能据此认定走代理
出现多个公共解析器并行解析、fallback 或解析器集群与配置中的上游逐一对应
浏览器与命令行结果不同浏览器独立 DoH、缓存或应用代理差异统一解析入口后复测

fake-ip 与 nameserver:先建立可验证的解析入口

enhanced-mode: fake-ip 会让内核为适用的域名返回虚拟地址,再依据映射处理连接。它减少了部分连接在建立前对真实地址解析的依赖,但并没有取消所有上游查询。直连连接、地址规则判断和部分应用场景仍可能需要真实解析结果。

下面是用于检查 DNS 链路的配置片段,不是完整的订阅配置。把同名字段合并进现有配置,保留已有节点、策略组和规则;同一层级不要重复出现两个 dns:。示例的 7890 是混合代理端口,1053 是本地 DNS 监听端口,两者不可混用。

mixed-port: 7890
mode: rule

dns:
  enable: true
  listen: 127.0.0.1:1053
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  fake-ip-filter:
    - "localhost"
    - "*.lan"
  nameserver:
    - https://1.1.1.1/dns-query
    - https://8.8.8.8/dns-query
  • enable 开启内核 DNS。仅启用此项,不会自动让所有应用向该监听地址发送请求。
  • listen 绑定回环地址,供本机测试使用。不要为了排查而直接监听所有网卡,以免向局域网开放解析服务。
  • fake-ip-filter 中的域名使用真实地址解析。应按实际兼容性需要添加,不要为了修复一个应用就排除所有域名。
  • nameserver 指定上游。多个上游不等同于严格的主备顺序,不能假定第二个地址只有在第一个完全失败后才被使用。

可用下面的命令直接检查内核的 UDP DNS 监听。对未被过滤的域名,预期能看到 198.18.0.0/16 范围中的虚拟地址。这个结果只证明本次查询到达了内核,尚不能证明浏览器也使用同一入口。

# Windows
nslookup -port=1053 example.com 127.0.0.1

# 安装了 dig 的 macOS / Linux
dig @127.0.0.1 -p 1053 example.com A

DoH 的出口:加密传输不等于代理传输

示例中的 DoH 通常通过 TCP 443 建立 HTTPS 连接,查询内容不会以普通 UDP 53 的形式传输。但如果这条连接直连,解析器仍会看到本地网络的公网出口;本地网络也仍可能观察到解析器连接的目的地址。若目标是让解析器看到代理节点出口,还需要核对内核发起的 DNS 连接如何选路。

使用 respect-rules 前先处理节点域名解析

支持该字段的 mihomo 可用 dns.respect-rules: true 让 DNS 连接遵循路由规则。它并不是“所有 DNS 强制代理”的开关:如果解析器 IP 匹配到 DIRECT,连接仍会直连。需要在实际规则中把选定解析器的目标地址交给已有代理策略,同时避免较早的直连规则抢先匹配。

启用时还应配置 proxy-server-nameserver,单独解决代理节点服务器域名的启动解析。否则可能出现循环依赖:连接代理需要先解析节点域名,而解析又要求先连接代理。该启动解析器本身也要符合网络条件;如果使用直连启动解析,它查询的节点域名会暴露给相应解析器,这是需要明确接受的边界。

使用域名形式的 DoH 地址时,还需要解决解析器自身域名的启动解析。default-nameserver 用于这类基础解析,不是普通查询的默认分流策略。上面的示例使用 IP 形式的 DoH 地址,便于先缩小排查范围,但仍需确认上游可达、TLS 验证正常,并检查实际出口。

fallback 与 nameserver-policy:减少意外的解析分支

fallback 可能参与并行查询

fallback 不能简单理解为“主 DNS 超时后才启动”。配置后,内核可能向主解析器与备用解析器发起查询,再依据 fallback-filter 决定采用哪一侧结果。因此,检测页面看到备用解析服务,并不一定说明主解析器故障,也不必然表示请求绕过了代理。

排查初期建议先省略 fallback,让路径更少。确认基础链路后,如果确有过滤异常地址的需求,可以把下面的字段合并到同一个 dns 块内。此例关闭 GeoIP 判断,仅示范用明确地址范围触发备用结果,不代表能识别全部污染答案。

  fallback:
    - https://9.9.9.9/dns-query
  fallback-filter:
    geoip: false
    ipcidr:
      - 0.0.0.0/32
      - 240.0.0.0/4

若开启 fallback-filter.geoip 并设置 geoip-code: CN,主解析结果是否属于指定地区会影响结果选择。这属于答案筛选,不是 DNS 出口控制。数据库过期、CDN 地址归属变化和跨地区部署都可能影响判定,不能把 GeoIP 判断当成通用防泄漏开关。

nameserver-policy 指定“由谁解析”

nameserver-policy 可按域名选择解析器。例如,以下片段让 example.com 及其子域名使用指定 DoH。该域名用于语法演示;实际应填入确有专用解析需求的域名,并在合并后验证。

  nameserver-policy:
    "+.example.com":
      - https://1.1.1.1/dns-query

域名解析策略和连接分流规则是两套判断。指定解析器不等于把该网站流量改为代理;同样,把网站规则设为代理也不等于其解析器连接必然走代理。企业内网和家庭设备域名可能需要本地解析器,若为这些域名单独设置直连解析,应把它记录为有意保留的例外,而不是拿它与公共域名测试混在一起。

TUN DNS 劫持:接管系统的 53 端口查询

系统代理主要影响遵循代理设置的应用,通常不会自动改写系统 DNS 路径。TUN 把更多 IP 流量交给内核,再配合 DNS 劫持接收普通 DNS 查询。下面仍是合并片段;需要使用支持 TUN 的 mihomo 客户端,并按平台要求授权创建虚拟网卡和修改路由。

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53
    - tcp://any:53
  • auto-route 用于自动设置路由。启用后应确认客户端显示 TUN 已运行,而不是只有开关处于开启状态。
  • any:53 与 tcp://any:53 分别覆盖进入接管路径的普通 UDP、TCP DNS 请求。
  • auto-detect-interface 帮助识别出口网卡。存在多个 VPN、虚拟网卡或特殊路由时,仍需检查实际选中的接口。

DNS 劫持不等于拦截所有加密解析。应用自带的 DoH 使用 HTTPS,DoT 通常使用 TCP 853,它们不会因为列出 53 端口就被改为内核 DNS。已经被其他 VPN 接管、被排除路由绕过或在特殊系统解析链路内处理的请求,也可能不进入这组劫持规则。

浏览器安全 DNS 与 IPv6 分开检查

测试期间,可先关闭浏览器的独立安全 DNS,让浏览器与系统共用解析路径。Chrome 可在「设置」→「隐私和安全」→「安全」中检查「使用安全 DNS」;具体文字可能随版本变化。如果需要保留浏览器 DoH,就应把它当作独立路径检查,包括其解析器、连接出口和失败后的回退行为。

同时检查 IPv6。DNS 返回 AAAA 记录与 IPv6 流量被正确接管不是同一件事。设置 dns.ipv6: false 影响内核的 AAAA 响应处理,不能代替系统 IPv6 路由控制;应用自带解析仍可能得到 IPv6 地址。不要只看 IPv4 出口就结束测试。

修复后的验收与常见故障

用三层证据确认结果

  1. 入口证据:本地 1053 端口查询正常,fake-ip 映射符合预期;启用 TUN 后,系统普通 DNS 查询能进入内核。
  2. 选路证据:在客户端连接面板或调试日志中核对解析器地址、匹配策略与连接出口。启用调试日志后及时恢复常用日志级别,分享前删除域名、节点和订阅信息。
  3. 外部证据:重新运行标准与扩展检测,确认出现的解析器均能由当前配置解释,并验证浏览器、命令行及需要保护的其他应用。

还可以在实际出口网卡抓包,使用 Wireshark 显示过滤器 udp.port == 53 || tcp.port == 53 || tcp.port == 853 查找普通 DNS 与常见 DoT 连接。抓包接口必须选对:虚拟网卡中的明文请求可能随后被内核接管,不能直接当作物理出口泄漏。DoH 混在 HTTPS 中,单靠这个过滤器看不到其查询内容。

开启 fake-ip 后检测仍显示运营商 DNS,先查哪里?

先用本地 1053 端口验证 DNS 配置已加载,再确认系统查询进入 TUN。随后检查浏览器独立解析、其他 VPN、策略中的本地解析器,以及订阅更新是否覆盖了修改。fake-ip 本身不会替系统选择 DNS 入口。

配置 DoH 后所有网站都打不开,怎样恢复?

恢复修改前的配置,先验证节点和基础网络可用。然后只启用一个已确认可达的 DoH,查看是否存在超时、TLS 错误或节点域名解析循环。不要在这一阶段同时增加 fallback、策略解析和规则跟随。

订阅更新后修复失效,怎样保留设置?

优先使用客户端支持的覆写、合并配置或配置增强机制,而不是只改一次下载下来的订阅文件。保存后查看最终生效配置,确认 dns 与 tun 字段未被后续合并覆盖。具体功能名称以所用客户端为准。

验收目标不是让测试页只显示一个地址,而是让每一条解析路径都有明确来源和出口。完成后再逐项恢复浏览器安全 DNS、局域网解析例外与其他网络工具,每恢复一项就重复检测。这样可以定位新增分支,而不是等下一次异常时重新猜测。