Contents

梯子原理与Clash.Meta配置:3.DNS原理详析

本文以 CC BY-NC-SA 4.0 协议发布

这部分属于进阶内容,读懂这部分之后,你就会知道为什么 DNS 配置要这么写、为什么分流规则要按特定的顺序写、为什么会有redir-hostfake-ip的区别,以及什么是 DNS 泄露、如何避免出现 DNS 泄露问题。

重新贴一遍前面发过的推荐DNS配置:

dns:
  enable: true				# 是否启用 Clash 的 DNS 模块(建议启用)
  use-hosts: true			# DNS 解析前是否检查 hosts 小节中的条目(建议启用)
  use-system-hosts: true	# DNS 解析前是否检查系统 hosts 文件中的条目(建议启用)
  ipv6: false				# DNS 是否返回 IPv6 结果
  
  # fake-ip 相关配置(原理将在后面说明)
  enhanced-mode: fake-ip	# DNS 处理模式,可选 redir-host/fake-ip
  fake-ip-filter:       	# fake-ip 白名单列表,其中的地址不走 fake-ip 处理
    - "*.lan"
    # Windows Connnect Detection	
    - '*.msftconnecttest.com'	
    - '*.msftncsi.com'
    # QQ Quick Login
    - 'localhost.ptlogin2.qq.com'
    - 'localhost.sec.qq.com'
    # STUN Server
    - 'stun.*.*'
    - 'stun.*.*.*'
  
  # dns 解析相关设置(原理在后面说明)
  default-nameserver:		# 默认 DNS, 用于解析 DNS 服务器的域名(先有鸡-先有蛋问题)
    - "223.5.5.5"
  nameserver:				# 主 DNS 服务器
    - "system://"          	  # 系统本地默认 DNS
    - "223.5.5.5"       	  # 阿里公共 DNS
    - "180.76.76.76"    	  # 百度公共 DNS
    - "119.29.29.29"    	  # 腾讯公共 DNS
  fallback:					# 后备 DNS 解析服务器(一般情况下填境外 DNS)
    - "tls://1.1.1.1#✈ 机场选择"   # CloudFlare DoT DNS,走代理查询
    - "tls://8.8.8.8#✈ 机场选择"   # Google DoT DNS,走代理查询
  fallback-filter:			# 哪些情况下采纳 fallback DNS 服务器的结果?
    geoip: true       		# 启用 geoip 判定,检查返回的 IP 地址的地理位置
    geoip-code: CN			# 除了国内的 IP 之外,其他地方的 IP 结果会强制使用 fallback DNS 的结果
    						# 用于防止 DNS 污染
    ipcidr: 				# 在这个网段内的 IP 结果也会被考虑为被污染的 IP
      - 240.0.0.0/4

hosts:
	# 可以在这里自定义一些 hosts 条目,和系统 Hosts 功能相同
	# membership-restaurant.com: 1.14.5.14

1. 为什么要用 Clash DNS?

我们都知道,DNS 是根据域名查询 IP 地址的服务。任何对域名的访问,都必须解析为 IP 地址后才可以向对方服务器发起通信,一般这个过程是由系统自行完成的。

由于历史原因,DNS 不像 HTTP 那样有完善且普及的 TLS 加密保护,目前大部分 DNS 流量还是以 UDP 明文数据包传输为主,这就给 DNS 污染 留下了可乘之机。而且,明文的 DNS 查询报文会向任何中间人泄露你正在访问的网站地址,存在隐私问题,这也就是所谓的“DNS 泄露”。

应对这些问题最简单的办法,是用经过加密的DNS over TLS或者DNS over HTTPS连接到境外服务器进行 DNS 查询。这种方法固然有效,但是相较于传统的比较快速的 UDP DNS 查询,加密连接会让 DNS 的响应时间变长很多倍,导致影响到用户日常上网的体验。而且,境外 DNS 服务器本身也可能因为被 SNI 阻断/IP 黑洞而无法访问。

因此,我们需要更智能一点的 DNS 查询方式。让我们想想,最好可以同时对国内国外 DNS 服务器发起查询,并判定结果有没有被污染,正常的查询就用速度特别快的国内 DNS 结果,碰到被污染的查询就以国外的结果为准。而且最好还能跟梯子分流结合起来,在 DNS 查询之前先把该走代理的流量全部送走,尽量避免在本地进行不必要的查询……

这么看起来有点白日做梦的要求能实现么?

你别说,你真别说。

让我们先从Clash DNS的工作原理开始了解:

2. Clash DNS 在哪些情况下会被使用到?

事实上,Clash 的 DNS 模块并不会在每个请求中都被使用到。当一段流量进入 Clash 之后,它会首先进入分流规则列表,和一条条规则进行匹配。

如果这个请求是对某个IP地址发起的,那么它必然不会涉及到DNS的使用,这些请求与本章节是完全无关的。当且仅当请求是对某个域名发起的,在规则匹配的过程中,才可能出现需要调用Clash DNS模块的情况,具体有如下这些:

  1. 某一条分流规则匹配成功,并且发现它的出口策略组是 DIRECT 直连。

    当匹配到策略组 DIRECT 的时候,Clash 需要在本地发出这个请求,不走代理。这时,由于请求的目标是域名,Clash 必须先在本地进行 DNS 解析得到目标域名的 IP,然后才能发出请求。

  2. 规则匹配过程中,遇到了一条基于 IP 分流的规则(如 IP-CIDR、IP-ASN、GEOIP、LAN 等),并且规则中没有标明no-resolve

    此时 ,请求的目标是域名,规则中提供的信息是IP,两者没法直接比较。于是 Clash 会尝试在本地进行 DNS 解析,拿到目标域名对应的 IP 地址,再和此分流规则进行比较判定是否匹配。

  3. 当一段 UDP 流量出站时,不管走直连还是代理,Clash 都会在本地进行一次 DNS 查询,拿到真实 IP 地址后再发出数据包

    这是一个特性,用来解决很多梯子协议尚未支持 UDP 域名传递的问题。此特性以后可能会删除,这里就不再赘述,不是本章节的主要内容。

  4. 当Clash收到一个DNS请求时

    如果你开启了TUN,或者启用了DNS劫持,那么Clash是会直接收到来自各个应用的DNS请求的。如何对这些DNS请求进行处理和响应?就有了redir-hostfake-ip两种不同的策略。

3. Clash DNS 在工作时具体是怎么查询的?

nameserver:
  ......
fallback:
  ......
fallback-filter:
  .......

在之前的配置文件中我们可以看到,nameserverfallback 部分分别配置了一组 DNS 服务器。

  • nameserver 中的是主 DNS 服务器,负责主要的查询工作。这里一般填解析速度比较快的境内 DNS 服务器,前面的配置中直接用了系统 DNS 服务器 system://
  • fallback 中的是后备 DNS 服务器,在主 DNS 服务器的结果可能出现污染时,将选择 fallback 服务器的结果返回。这里面一般填境外的服务器,保证查询结果绝对无污染。

具体的查询逻辑如下:

  • 正常情况下,Clash 将向 nameserverfallback 中所有的服务器同时发起 DNS 请求,取快者的结果返回。
  • 一般来说nameserver 的服务器会比较快地返回,而向 fallback 服务器发起的 DNS 请求(在此配置文件中)被要求走代理查询以保证连接畅通,因此其响应时间会更长。
  • 获取 nameserver 服务器先返回的结果后,如果发现结果在 fallback-filter 范围内,就可以判定这个主 DNS 服务器的结果可能被污染,此时选择等待 fallback 中服务器的响应作为最终结果。

4. 一直听说的“DNS泄露”到底是怎么回事?危险吗?有办法预防吗?

在刚刚的那个查询流程中,有没有发现什么潜在的问题?

没错,当 Clash 在本地进行 DNS 查询时,是向国内国外服务器并发发起查询的。因此,如果不加额外配置,在查询那些可以直连的DNS服务器的时候(往往是国内 DNS 服务器),你要访问的网站会随着明文 DNS 查询报文暴露给所有中间人。

这就是所谓的 DNS 泄露

DNS 泄露危险吗?严格来说,这只是一种隐私信息的泄露,对外界暴露了你正在访问的某些网站域名,一般这种泄露并不是什么大问题。但是在特定情况下(比如在公司带薪摸鱼),这种无伤大雅的隐私泄露有时候也会成为大问题。另外,🧱也可以通过你DNS泄露的域名识别到你正在访问特定的网站,进而可能尝试对你的流量进行识别,或者对你的机器进行反向墙等等。

总的来说,这个问题还是处理一下比较好。


想要解决DNS泄露问题,就需要从其源头下手进行封堵。让我们回忆一下前面提到的,在哪些情况下 Clash 的 DNS 模块会被使用到:

  1. 当匹配到的分流规则的出站是 DIRECT 时
  2. 当一个域名请求遇到一个基于 IP 的分流规则时
  3. 当Clash收到一个DNS请求时

这几种情况其实都会触发Clash DNS的并发查询,进而可能引起“DNS泄露”。

  1. 对于第一种情况,Clash需要直接对DIRECT的目标发起连接,因此必须进行DNS查询。对于DIRECT出口的流量,一般我们不用太担心,随便它发生“泄露”,因为这些主要都是国内网站,DNS泄露所导致的风险并不大。

    如果你谨慎到连DIRECT出口的国内流量都需要完全不留痕,可以在Clash的DNS配置中指定direct-nameserver条目,将DIRECT出口流量使用的DNS服务器全部设置成国外的DNS服务器,走代理查询即可。

  2. 对于第二种情况,Clash必须将域名解析为IP后才可以进行规则比较。一般来说,恶性的 DNS 泄露问题往往在这里发生:

    由于某些 IP 分流规则顺序安排不当,排在非常前面,导致分流规则匹配过程中经过它的很大一部分流量错误地提前触发了 DNS 查询。这些流量本来应该直接走代理出站,由远端节点负责DNS解析,但是现在却因为提前碰到了IP规则而被迫要进行本地DNS查询,进而导致DNS泄露的发生。

    这种情况的处理办法也很简单:严格控制分流规则的顺序即可。由于 Clash 对分流规则的匹配是从上到下按顺序进行的,因此我们只要尽量把会引起 DNS 解析的规则放在靠后的位置(比如 GEOIP 或 IP-CIDR 规则,或者含有 IP-based 规则的 RuleSet ),避免让流量提前接触到不必要的 DNS 查询,这样就可以了。

    一定有 IP-based 的规则要放在靠前位置的话,也记得在规则最后加上 no-resolve 参数,要求 Clash 在碰到这条规则的时候不要尝试进行本地 DNS 解析。例子如下:

rules:
  ...
  - GEOSITE,google,✈ 机场选择
  - GEOIP,telegram,✈ 机场选择,no-resolve
  - GEOSITE,telegram,✈ 机场选择
  - GEOSITE,openai,✈ 机场选择
  - GEOSITE,onedrive,✈ 机场选择
  ...
  - GEOSITE,category-social-media-!cn,✈ 机场选择
  - GEOSITE,category-entertainment,✈ 机场选择
  - GEOSITE,category-communication,✈ 机场选择
  - GEOSITE,category-scholar-!cn,✈ 机场选择
  ...

  # 尽可能为已知的域名配置好分流规则,最大程度避免不必要的本地DNS解析
  # 上面的所有规则,都不会引发 DNS 查询,不可能导致 DNS 泄露
  # 从下面的规则开始,可能会引发 DNS 查询

  - GEOIP,telegram,✈ 机场选择			# telegram IP 走代理
  - GEOIP,CN,DIRECT						# 中国 IP,走直连
  - MATCH,🐟 漏网之鱼					# 未被分流规则识别的剩余流量走“漏网之鱼”
  1. 对于第三种情况,当Clash收到DNS请求时,如果直接进行本地DNS查询,也必然会导致泄露的发生。这种情况处理起来更为复杂,详见下面对redir-hostfake-ip的讨论。

5. 前面配置中 nameserver / fallback 服务器为什么选这几个?为什么不用 DNS over HTTPS/TLS?

结合上面的 DNS 工作流程小节和分流规则小节,我们再梳理一遍流量进入 Clash 处理,以及触发 DNS 查询的整个过程:

  • 流量进入 Clash,会进入分流规则进行逐条匹配
  • 在分流规则的前半部分,设置了大量基于域名的分流规则,以及包含 no-resolve 参数的基于 IP 的分流规则,其目的是尽可能在不触发本地 DNS 解析的情况下尽量对流量进行识别并分流走
  • 当前面的分流规则都无法匹配流量时,进入最后一部分,将会尝试对域名进行本地 DNS 解析后再尝试匹配 IP-based 规则
  • 在匹配的过程中,如果某条分流规则匹配成功,并且发现其出口策略组为 DIRECT,Clash 会在本地进行一次 DNS 解析,拿到域名对应的 IP 地址后直接发起连接

从上面的流程可以看出,经过分流规则的处理之后,在本地发起的 DNS 查询的目标其实绝大部分都是出口策略组为 DIRECT,也就是在国内需要直连的网站,只有极少数未被分流规则匹配到的比较冷门的域名才会需要进行本地 DNS 解析。

因此,在本地 nameserver / fallback 服务器的设置上,首要注意的是要保证国内域名的解析速度,以免大量走 DIRECT 出口的国内请求在出站时因为DNS响应时间长,导致过大的延迟。与此同时,我们也需要尽量确保国外域名解析结果的正确性,以免极少数冷门域名因为 DNS 污染而无法访问。

本配置文件教程中编写的DNS配置,正是按这种想法来的:

  • 首先在 namespace 中设置数个国内公共 DNS 和系统 DNS,取快者结果返回,用于保证国内域名的 DNS 解析速度。此时尽管会发生 DNS 泄露,但是绝大部分流量都属于国内域名,发生泄露的隐私风险并不大。
  • 其次,在 fallback-filter 中设置当域名的geoip位于国外,或DNS结果位于可疑区域内时,取 fallback DNS 的结果作为最终结果。在 fallback DNS 中使用 1.1.1.1 和 8.8.8.8 两个最大的全球公共 DNS,走代理查询,这样可以保证境外域名 DNS 查询的结果绝对正确。

至于为什么不使用 DoH/DoT?

答案是没必要。在此 DNS 方案中,nameserver 内的 DNS 服务器主要负责查询国内域名的 DNS 结果,经过它查询的域名不太可能出现污染的情况;而 fallback 内的 DNS 服务器由于走代理到远端进行 DNS 查询,因此同样没有使用 DoH/DoT 的必要。

并且,DoH/DoT 会将 DNS 查询的响应时间延长很多倍,拖慢 DIRECT 出站访问国内域名的速度,这会给日常使用各种国内服务造成较大影响,可以说是得不偿失。

6. Fake-IP 又是什么?它解决了哪些问题?

前面讲 Clash 入站的时候提到过,在启用 TUN 模式 / 透明代理时,系统会把所有流量都路由到 Clash 中,这当然也包括程序发出的各种 DNS 请求。在收到一个 DNS 请求后,Clash 应该如何处理呢?

不同于普通的 HTTPS/TCP 流量,由于存在 DNS 污染的可能,Clash 不能直接把这些 DNS 请求交给系统处理。

此时有两种解决方案:

  1. 直接用 Clash DNS 模块进行一次本地DNS查询,将查询的结果返回回去。这也就是所谓的redir-host模式
  2. 返回一个给定范围内的虚假的IP地址,这也就是所谓的fake-ip模式。

这时候读者们可能会想了,这DNS请求怎么还能返回“虚假的IP地址”?这样程序还怎么连接到正确的服务器上?这不是作茧自缚吗?

别急,你听我慢慢说来。

没错,在Clash开发的早期阶段,redir-host是最直观的,也是唯一的选择。fake-ip方案是随着后续版本更新而逐渐发展成熟,被加入到内核当中的,并受到了不少用户的欢迎。

那么fake-ip为什么会出现呢?它一定是解决了redir-host模式在早期存在的一些问题,因此才会被引入。

我们首先来看看redir-host的默认行为逻辑:当收到任何一个DNS请求时,直接在本地进行DNS查询,返回查询结果。

这种方法逻辑简单有效,但是会引入一些问题:

  1. 在默认的 redir-host 模式下,Clash 会尝试对所有的 DNS 请求都返回真实的 IP 结果。此时如果不加额外设置的话,Clash 会对所有DNS请求都发起 nameserver/fallback 并发查询,这样所有的请求都会 100% 发生 DNS 泄露,你要访问的网站会随着明文 DNS 查询报文暴露给所有中间人。这是有一定的隐私风险的。

  2. 当程序收到 Clash 返回的真实 IP 结果后,会向此 IP 地址发起请求,当Clash再受到新请求要进行代理的时候,只能获取到这个请求的目标 IP 地址,没法再获取到要访问的域名了。这对于分流来说是一个不太好的消息,毕竟现有大部分分流规则都是基于域名的。

    Clash 为了在截获到新请求时,还能根据域名正确地进行分流,会自行维护一个内置 DNS 模块的 域名-IP 地址 Mapping 映射表,每当其内置 DNS 模块进行一次查询时,就会把请求的域名和 DNS 查询结果放到映射表中记录下来,这样当程序向解析结果 IP 地址发起请求时,Clash 就可以从表中找到之前记录下来的域名,然后用这个域名进行分流规则的匹配。

    这个 Mapping 机制听起来很有效对吧,但是事实上它有一个比较严重的问题——无法处理单 IP 对应多域名的情况。

    比如像 CloudFlare 上设置“小云朵”代理的网站,其所有网站的访问流量都需要从 CloudFlare 的几个公开 IP 地址入口进入,这种情况下,很容易出现同一个 IP 对应多个网站的情况。此时如果 Clash 还按照原来的方法根据 IP 地址拿到之前记录的域名进行分流匹配,则很可能拿到一个错误的域名,导致后续分流出现各种问题。

    img

    偏偏 CDN 的使用非常广泛,网站数量非常之多——因此这个问题的存在还是很麻烦的。

    而且除了 CloudFlare 之外,当查询得到 DNS 结果被污染,不同网站被污染到同一个虚假的 IP 地址上时,Mapping 机制也同样无法正常工作。

  3. 纵观上面的 DNS 解析-请求截获 这个流程,有没有发现这一次的本地 DNS 解析过程是没有必要的?

    事实上,在透明代理环境下,redir-host 的本地 DNS 解析过程纯粹只是为了让映射表中生成这样一条 域名-IP地址 Mapping 映射记录,以供后续 Clash 截获向此 IP 发起的请求时,可以找到原来的域名用来分流。那我们为什么不直接返回一个和域名一一对应的假 IP,跳过这一次本地的不必要的查询流程呢?

    等程序发起真正的 TCP 连接的时候,请求将根据假 IP 再次被路由到 Clash,Clash 根据假 IP 找到其一一对应的域名信息,然后进行分流匹配工作。

fake-ip模式使用的,就是这个所谓的“返回假IP”的思路。很显然,通过返回假IP,可以有效减少不必要的本地 DNS 查询、消除额外的查询延迟,防止Mapping机制失效,而且还可以避免产生 DNS 泄露,并提高Clash对流量的捕获能力。


Clash 的 Fake-IP 实现受启发于 RFC 3089。在fake-ip模式下,当收到一个 DNS 请求时,Clash 不会真的在本地发起 DNS 查询,而会返回一条“假的”IP 结果,并且把这个 fake-ip 和查询的域名两者之间映射关系同样用 Mapping 表缓存下来。

当程序拿到这个“假的”IP 结果后,会尝试对其发起进行 TCP/UDP 连接。此时这个 TCP 连接(通过 TUN / 透明代理)再次被 Clash 截获,Clash 从缓存中找到它要访问的域名,直接使用这个域名进入分流规则匹配的流程。如果域名匹配到要走代理出站的规则,就把请求转发出去,不需要再在本地进行 DNS 查询;如果域名匹配到直连的规则,那正常在本地执行 DNS 查询。

redir-host不同,fake-ip的映射表中每个 IP 地址都由 Clash 手动分配给不同的 DNS 请求,这样就保证了绝对不会出现条目冲突的情况,彻底解决了 redir-host 中类似的问题。

7. 如果我更倾向于使用 redir-host,应该怎么进行配置?

事实上,redir-host模式也没有完全被抛弃。Clash.Meta 对redir-host的 Mapping 问题进行了修补,增加了 sniffer 域名嗅探机制,通过读取 TLS 握手包内的 SNI 域名字段,可以将程序请求访问的 IP 还原成域名,有效解决 Mapping 机制的短板。理论上开启 sniffer 后,就不会再出现 Mapping 条目冲突的问题。

SNI 阻断 也是靠读取 TLS 握手包内的 SNI 域名字段来对流量进行判别和拦截的(笑

但是redir-host需要进行本地DNS查询,进而导致 DNS 泄露的问题仍然没有得到解决。

为了解决此问题,在编写配置文件的时候,我们需要在 nameserver-policy中再写一遍“分流规则”,对各种常见域名使用的DNS服务器进行预设,将可能产生隐私风险的域名设置为走代理查询(这样会慢很多!),剩余其他域名则继续进行正常的 nameserver/fallback 并行解析。经过完善的配置以后,redir-host使用起来也不会有什么太大的问题。

可惜,redir-host不必要的本地/远端DNS查询带来的额外延迟开销仍然是没法消除的,因此其在请求冷响应速度上始终会略逊于fake-ip模式。

8. 所以?我们应该无脑选择 Fake-IP 吗?

计算机领域没有银弹。事实上,fake-ip模式也同样存在一些自己的问题,导致其并不能完全替代掉redir-host

已知的一些问题如下:

  1. 对任何 DNS 查询返回假 IP,可能造成问题

    有些程序在收到 fake-ip 响应后会产生问题。像一些需要用到真实对端 IP 地址的服务(比如 P2P 等),如果解析域名时拿到的是错误的 IP 地址,会影响服务的可用性。另外,有一些网络验证较为严格的应用(比如某些银行等),检测到自己域名的解析结果是类似fake-ip常用的198.18.x.x结果时会认为网络受到劫持或者网络不可用,采取一些保护措施,导致服务不可用。

    解决方案:把产生问题的域名加到fakeip-filter里面,Clash 在处理这些域名的 DNS 查询时,会回退到 redir-host 的行为,仍然进行本地 DNS 查询并返回真实结果。

  2. fake-ip模式下 ICMP 受到影响

    如 ping、traceroute 等使用ICMP协议的工具,会受到 fake-ip 的影响,无法呈现出正确结果。

因此,在DNS上具体使用什么策略,读者们还是要按自己的需求进行选择。比如在路由器场景下,更建议使用redir-host模式,以尽可能保证局域网内所有设备都可以正常工作,不至于出现奇怪的问题。

9. DNS 部分特性总结

总结一下,Clash 的 DNS 模块还是相当重要的。

  • 在 TUN 模式 / 透明代理下,Clash 可以使用 fake-ip 方式处理到达的 DNS 请求,避免了DNS 泄露问题,提高系统响应速度。
  • 在分流规则中,通过合理安排分流规则的顺序,尽量避免不必要的本地 DNS 解析,大大减少了 DNS 泄露的可能性。
  • 当不得不需要进行本地 DNS 解析的时候,结合使用并发查询与 fallback-filter,在响应速度和结果正确性两者之间取得了较好的平衡。

系列全文目录: