关于 Clash 科学上网的最佳实践

GFW Mar 15, 2024

回国出趟差,迫于 ChatGPT 在公司内网访问不了,又要开始研究翻墙技术。这次回国发现墙又增高了不少,不过也没之前朋友传言的那么恐怖,传言说几小时封一个节点,得用 IPv6 不断换 IP。我的节点有国外家庭宽带也有 VPS,实际体验下来没那么恐怖,节点一直都连得上,只是墙在 DNS 方面又加高了一些。

经过几天的搜寻资料,阅读文档,尝试各种组合,我可以负责任的说,在现在这个时间节点,没有人比我更懂软件里这些配置项。

DNS – 上 网的根基

目前所有公共 DNS 都污染了谷歌 / ChatGPT 这些域名。普通 DNS 请求走的是 UDP 明文连接,少部分情况走 TCP 但是也是明文,污染一部分来自于墙的抢答,墙现在是随机返回一个 IP,没什么规律可言,所以没有好的检测应对策略。另一部分来自于 DNS 本身的污染,污染源我就无从得知了。具体表现如下,从国内 223.5.5.5 查询 chat.openai.com,返回的结果是被污染过的:

dig chat.openai.com @223.5.5.5

;; ANSWER SECTION:
chat.openai.com.	1	IN	A	128.242.245.93

;; Query time: 8 msec
;; SERVER: 223.5.5.5#53(223.5.5.5) (UDP)

但是从境外请求 223.5.5.5 就是正确的

dig chat.openai.com @223.5.5.5

;; ANSWER SECTION:
chat.openai.com.	146	IN	CNAME	chat.openai.com.cdn.cloudflare.net.
chat.openai.com.cdn.cloudflare.net. 146	IN A	104.18.37.228
chat.openai.com.cdn.cloudflare.net. 146	IN A	172.64.150.28

;; Query time: 28 msec
;; SERVER: 223.5.5.5#53(223.5.5.5) (UDP)

有人可能会觉得墙在抢答,DNS 本身还是干净的,那我们再来一组实验,从境内分别使用国内的 DoH 和国外的 DoH:

# 从境内请求境内的 DoH
dog --time chat.openai.com --https @https://dns.alidns.com/dns-query

A chat.openai.com. 1m45s   108.160.162.102
Ran in 118ms

# 从境内访问境外的 DoH
dog --time chat.openai.com --https @https://1.1.1.1/dns-query

CNAME chat.openai.com.                    1m53s   "chat.openai.com.cdn.cloudflare.net."
    A chat.openai.com.cdn.cloudflare.net. 1m53s   172.64.150.28
    A chat.openai.com.cdn.cloudflare.net. 1m53s   104.18.37.228
Ran in 1631ms

结果不言而喻了,国内的 DNS 环境,除了墙会抢答之外,DNS 本身也会污染一些结果。不过我相信阿里 DNS 解析大多数域名还是干净的,只是污染了某几个特殊照顾的域名。这点也是下面某些配置的前提。

关键词说明

为避免歧义,先对几个关键词作出说明

  1. 终端:指运行师夷长技工具的设备,例如电脑/手机等等
  2. 代理工具:即上面说的师夷长技工具,例如 Clash / Surge 等等
  3. 远端:指代理工具使用的节点
  4. 本地:泛指终端代理工具
  5. 兜底策略:没有匹配任何规则的策略,例如 DIRECT / Proxy 等等

关于 tun 模式和 fake IP

在普通的模式下,即设置系统 socks5 / http 代理的情况下,当终端发起一个连接的时候是直接全部封装网络包发出去,发之前不做域名解析。在这种情况下,如果触发了代理工具的分流规则,自动就会走远端解析,反之如果没触发,走直连并且使用前面配置的 nameserver (223.5.5.5) 解析。

而在 tun 模式下,由于在网卡层接管流量,代理是透明的,终端则和正常请求一样,需要先拿到一个 IP 才可以发起连接,普遍来讲代理工具都会劫持所有 53 端口的请求,也就是说 IP 普遍来讲都是代理工具提供的,可以是真实解析再提供,也可以是提供一个假的 IP 骗终端先发起连接。

真实解析固然是好,但是速度从几十毫秒到几千毫秒不等,而且这个 IP 还不一定用得上,例如发起的连接触发了分流规则,走远端解析,或是 IP 被污染导致网站打开失败。这个时候就可以引入一个概念 fake IP。fake IP 这个概念出自 RFC3089,早在 2001 年就被提出来了,下文的讨论假设 fake IP 被设置为 28.0.0.1/8 网段.

在 tun 模式下,代理工具是可以劫持所有网络包的,假设用户请求 chat.openai.com,代理工具先返回一个假的 IP 比如 28.0.0.2,同时记住这个映射关系:"chat.openai.com": "28.0.0.2",当终端28.0.0.2 发起连接的时候,代理工具就知道要访问的域名是 chat.openai.com,就开始判断规则,看是否整个走远端解析,或是走直连并且使用 223.5.5.5 解析。在这里不由得佩服代理工具开发人员的智慧。

简单讲:

  • 使用普通的系统代理可以让终端第一时间发起连接,进入分流阶段,缺点是部分应用不遵守系统代理,优点是除了前面一个缺点全都是优点
  • tun 模式是为了让终端实现全局代理的能力,但是 tun 模式下,终端拿到 IP 之后才会发起连接
  • fake IP 是为了让终端第一时间发起连接,进入分流阶段,后续和系统代理相同

用 fake IP 接管 DNS 会造成一些意想不到的问题,这里需要和普通系统代理做个取舍。

配置误区之 fallback dns

fallback dns 是 redir-host 时代的产物,是为了解决 DNS 污染问题。

redir-host 模式也是 tun 模式的一种(还有一种叫 fake IP),tun 模式工作在第三层网络层,拿不到请求的域名,只能拿到连接的 IP 地址。那他如何根据域名规则分流呢?答案是先拦截 53 端口的 DNS 请求,自己建立一个映射表,在终端发起请求的时候来匹配访问的域名,从而进行后续分流等操作。

但是如果多个网站部署在同一个 IP 下,或者 DNS 被污染到同一个 IP,redir-host 就不准了(除非加上嗅探功能)。配置 fallback dns 之后,如果获取到的 IP 被判定为被污染(需要回滚),就使用 fallback dns 解析出来的结果。为了确保拿到的结果完全正确,fallback dns 要设置为境外的加密 DNS 服务器,因为无加密或者境内的 DNS 都会被污染。

但正如前文论述,使用境外加密 DNS 毫无体验可言,而且这个本地解析出来的 IP,绝大多数场景下是用不到的,最终还是会走远端解析。终端只是需要一个 IP 建立连接,代理工具只是需要一个 IP 来维护映射表而已。费时费力 fallback 出来的一个真实 DNS 结果,最终还用不上。fallback DNS 的核心功能只是一定程度上解决 DNS 污染问题,降低了映射表出错的概率。

所以随着发展,redir-host 已经被官方扫进了垃圾堆,毕竟硬伤太多了。在继任者 fake IP 模式下,代理工具拦截 53 端口的查询,立刻返回一个假的 IP,让终端立刻发起连接。由于假的 IP 是自己生成的,可以确保每个域名 IP 都不同,彻底杜绝了上述映射表出错的可能性,并且分流结果可以拿去给远端解析。

所以,在弃用 redir-host 并且改用 fake IP 模式的情况下,fallback 极大概率是不需要配置的,把黑名单规则写全,全部走远端解析,剩下的没被污染的域名走国内 223.5.5.5 岂不是更快。

如果一定要用 fallback DNS 的话,确保填入境外的加密 DNS 即可,因为 fallback DNS 的最终目的是正确解析被污染的域名。不过这样体验极差,境外加密 DNS 至少需要 1.5 秒拿到结果。

Clash / Surge 配置注意事项

很多年前大家都熟练掌握分流配置,自动测速配置,规则集这些,就不再赘述,主要分享下有关 DNS 污染的应对策略。

协议选择

首先,原厂 shadowsocks 仍然是最稳的协议之一,v2ray 插件我测试下来测速会慢个 1 秒左右,原因未知,其他协议例如 vless vmess 也都不错,性能表现都大同小异,没有发现哪一种协议被墙盯上的

nameserver 配置

nameserver 使用普通的无加密 UDP 协议就好,例如 223.5.5.5 和 119.29.29.29,前提是大厂 DNS 只重点关照特定几个域名,以及墙只抢答 53 端口的这些域名。如果将来有一天大面积抢答国外域名,需要再想对策,例如使用境内 DoH。

为什么不建议折腾 DoH DoT 呢,用境内的 DoH 解析 chat.openai.com,该污染照样污染,第一章节已经论证过了。也别想着用境外的 DoH DoT,DoT 853 公网端口不定时抽风,稳定性很差。境外的 DoH 普遍查询一次都在 2 秒左右,毫无体验可言,第一章节也已经论证过了。

gfwlist 域名直接走远端解析就好,本地只解析直连的网站,而这些没命中规则的域名大概率是没被污染的,走 DoH 解析没有意义,除非你特别介意明文传输的 DNS 数据包。

fallback dns 配置

在上个章节已经论述过,fake IP 模式下不需要配置 fallback dns。我自己还遇到过一些麻烦,贴出来仅供参考:

  1. Clash Premium 内核下,如果规则里写了 IP-CIDR,1.1.1.1/32,Proxy,no-resolve终端进行 dig xxx.com @1.1.1.1 的时候是真的会走代理
  2. Clash Meta 内核不支持这个特性,我没做太密集的测试,初步测试是不行
  3. 会有大量网站打不开,原因不明
  4. 即使我使用例如 https://1.1.1.1/dns-query 不需要解析 DoH 域名的,也会有网站打不开的情况,原因不明

况且 fallback 支持的能力太弱了,只支持 geosite 匹配和 geoip 匹配,很容易误伤,即要么很多漏网之鱼,要么一杆子全打死,导致体验极差。本来好好的交给远端解析就好,多此一举还惹一身麻烦,总之只要你的 gfwlist 规则够用,就不太可能需要 fallback DNS。

分流配置注意事项

分流配置方面,尽可能移除不带 no-resolve 的 IP 规则(例如谷歌云亚马逊云的 IP 段),TG 之类的 IP 规则要保留但是必须加上 no-resolve,这样可以避免多一次解析 DNS。举个例子:

  1. 假设现在有一个规则列表,里面有域名匹配,有 IP 匹配。当终端发起一个连接的时候,如果没有命中域名匹配,或者域名规则比较靠后,则终端上运行的代理工具会发起一次 DNS 请求,用来判断是否在 IP 匹配规则里,这里就会有潜在的 DNS 污染 /泄露 问题
  2. 带上 no-resolve 之后,则不会进行 DNS 解析,除非应用是对 IP 发起连接才会匹配到这条 IP 规则

其他配置

节点测速必须用完整的时间,不要使用 unified-delay,因为某些协议的握手时间很离谱。例如我的节点上同时有原厂 shadowsocks 和 v2ray 插件 websocket 混淆的 shadowsocks,v2ray 插件的完整时间比原厂慢 1 秒,应该就是握手速度太慢,但是如果开启 unified-delay,插件版反而快 100ms。

如果你的兜底策略是直连,那么前面只写需要 Proxy 的规则就好了,不命中就会直连,没必要增加配置的复杂度,反之亦然。

我个人更喜欢兜底策略用直连了,因为现在各 vps 的 IP 地址被黑产污染的很厉害,兜底配置走代理很容易触发风控,例如 Cloudflare 的人机验证,谷歌的人机验证。我个人的想法是,只要 gfwlist 域名匹配规则够详细,就很难遇到 DNS 污染问题,对于非 gfwlist 的域名,走直连也没什么不好,遇到哪个网站直连慢的自己添加一条代理规则就好。

tun 模式的缺陷

tun 模式有两个问题,第一个问题下一个章节有解决方案,第二个就要自己取舍了

  1. 配合去广告 DNS 使用的时候,由于终端拿到的假 IP 不为空,有些没节操的软件会引发雪崩,即不断循环发请求,导致设备发热以及额外的流量消耗
  2. ping 命令拿不到真实的延迟

DNS 进阶配置

部分用户是有去广告的需求的,会在规则列表引进来几万条拦截广告配置,但是终端毕竟处理能力有限,不如把这件事交给服务器来做,即搭建一个 AdGuard Home 服务。

众所周知中国境内想要搭建 53 端口的 DNS 服务器是违法的,不过搭建在非 53 端口目前还没人管。恰巧 Clash 这类软件支持自定义端口号的 DNS 服务器,自建的好处是重复请求的域名处理时间基本上是 0 毫秒,请求的速度只取决于网络延迟,比公共 DNS 还是要快很多。

由于中国很大,很多网站的 CDN 都细化到省份甚至城市,自建 DNS 的风险之一就是会把 CDN 解析到距离服务器最近的位置。通常来讲横跨半个中国网速也不会太慢,但是这样毕竟不环保,解决方案是打开 AdGuard Home 的 EDNS Client Subnet 功能,上游 DNS 服务器就会根据 ECS 的信息返回正确的 IP。

请注意并不是所有上游 DNS 服务器都支持 ECS 功能,测试方法如下

# 新加坡
dig +subnet=111.65.50.0/24 api.bilibili.com @223.5.5.5

;; ANSWER SECTION:
api.bilibili.com.	1	IN	CNAME	a.w.bilicdn1.com.
a.w.bilicdn1.com.	1	IN	CNAME	i.w.bilicdn1.com.
i.w.bilicdn1.com.	1	IN	A	164.52.39.43

/* 164.52.39.43 归属地是新加坡 CDS Global Cloud CO., LTD */

# 广东深圳电信
dig +subnet=218.17.109.0/24 api.bilibili.com @223.5.5.5

;; ANSWER SECTION:
api.bilibili.com.	1	IN	CNAME	a.w.bilicdn1.com.
a.w.bilicdn1.com.	1	IN	A	59.36.228.17

/* 59.36.228.17 归属地是佛山 中国电信 */

# 北京移动
dig +subnet=223.104.3.0/24 api.bilibili.com @223.5.5.5

;; ANSWER SECTION:
api.bilibili.com.	90	IN	CNAME	a.w.bilicdn1.com.
a.w.bilicdn1.com.	90	IN	A	111.31.33.21

/* 111.31.33.21 归属地是天津 中国移动 */

根据我的测试,国内只有 223.5.5.5 和 119.29.29.29 支持 EDNS 解析,国外只有 8.8.8.8 和 223.5.5.5 支持 EDNS。这些公共 DNS 服务器对应的 IPv6 地址也支持 EDNS。

将上述支持 EDNS 的服务器配置到 AdGuard Home上游 DNS 列表,即可较为完美的实现去广告+CDN 加速。附加好处是自建服务器到终端这段路径的 DNS 响应不会被 GFW 抢答,因为现阶段 GFW 只抢答 53 端口。PS: 如果上游配置的是 53 端口无加密 DNS,自建服务器到上游仍然会被抢答。

另外需要注意,开启 EDNS 功能会在一定程度上影响 DNS 缓存,如果你只在服务器物理位置附近活动,建议不要开启此功能,如果你全国各地到处跑,开启 EDNS 会更好一些。

配置完之后即可实现去广告了,不过恭喜你喜提机身发热,请求日志雪崩的问题,日志列表可以看到大量涌入的类似于 dial DIRECT error: dial tcp4 0.0.0.0:443: connect: connection refused 的错误,原因是 fake IP 模式下广告软件收到一个假的但是有效的 IP,而自建 DNS 给代理工具返回的是一个空 IP(0.0.0.0)。有些没节操的软件这时候就会疯狂重试,点名批评一下微软和英伟达的日志上报代码。解决方案是搜集日志列表雪崩的域名,或者 AdguardHome 拦截排行榜前几名的域名,写成 Clash 规则并且 REJECT 即可,REJECT 仍然还有少部分会不断重试但是总体情况已经好很多了。

参考资料

  1. Clash Meta 文档
  2. 浅谈在代理环境中的 DNS 解析行为- Sukka's Blog

Tags

Jie Li

🚘 On-road / 📉 US Stock / 💻 Full Stack Engineer / ®️ ENTJ