提示:本篇文章可能篇幅较长,知识点较多,需要花40min甚至更长时间才能读完,可以通过左侧导航栏快速跳转。我将尽量用大家都听得懂的方式跟大家解读有关DNS在科学上网中的一些问题。知识本就没有绝对正确,如有错误,欢迎纠正。出现的人名、ip地址等敏感信息已打码。

前情概要

写这篇文章是前两天注册steam账号时出现了一行“请勿使用VPN或代理工具”的提示,看到后便心头一颤,当时我所掌握的知识告诉我,这不和前阶段Xray项目的issue中有iphone用户爆出收到疑似公安局警报一样吗?难道steam的探测机制其实和这是一个道理?

然而询问群友后发现,事情好像并不简单

邈邈随即建议我去试试whoer.net,经过我在不同代理模式下的测试,发现结果和上面我提出的情况如出一辙。但泄露地理位置的问题,或者说被探测出使用VPN的问题究竟出在哪?要怎么解决?这和DNS又有什么关系?以及后来群友提出的开全局、禁webRTC、使用Doh等方式能否真正意义上完全杜绝DNS投毒和泄露的情况?

研究了几天,写篇文章记录一下有关这些问题的答案和解决方法,还是有很多东西可以学习的。

我将分三个部分,从DNS投毒和泄露的原理、_Clash_内核的工具的DNS处理方式、以及常见Clash配置存在的问题,并逐一回答上面提出的这些问题(_V2rayN_的使用会另外开一篇专门介绍,本篇主要讲解DNS泄露原理及基本应对思路)

一、DNS投毒和泄露

DNS工作原理

在介绍标题内容前,先从最基础的东西开始讲起。先来看看DNS的工作原理是什么。大佬可以跳过这部分。

假如你从运营商拉了一条宽带,运营商会分配给你一个光猫,光猫下面接着你自己的路由器,路由器通过pppoe拨号,获取运营商分配的公网ip和DNS服务器,假设公网ip是2.2.2.2,DNS服务器一般会分配两个,假设其中一个的IP为3.3.3.3。同时 路由器作为局域网的网关会有自己的内网IP地址。假设为192.168.0.1,家里的所有网络设备都会连接到这台路由器。路由器通过dhcp为每一台网络设备分配一个内网IP。以及默认网关DNS等信息。

一般情况下 默认网关和DNS服务器都是路由器。

这是最常见的家庭网络拓扑。

当我们在浏览器中访问百度的时候,由于baidu.com只是为了方便人类记忆的字符串。我们都知道,要定位某一台服务器必须通过IP地址,浏览器并不能通过这串字符找到百度服务器的位置,所以需要有一个地方能查到baidu.com这串字符对应的IP地址,这个地方就是DNS服务器。首先,浏览器会检查浏览器DNS缓存里是否有百度的IP,发现缓存列表中并没有。其次,除去浏览器的DNS缓存,操作系统本身也有DNS的缓存列表。windows可以通过ipconfig /displaydns查询。其中包含我们在系统hosts文件中手动添加的绑定记录。如果在操作系统DNS缓存中也没有找到,浏览器会构建一条DNS查询的应用程数据包,内容是百度的IP地址是多少,DNS服务的默认端口是53。默认传输方式是udp。接着封装MAC地址 并从网口发出数据

数据包来到路由器,路由器一看目标IP是自己,于是会一层层的解封装 得到电脑的DNS请求,路由器同样首先检查自己的DNS缓存列表中是否有百度的IP地址,如果没有,于是会构建一条DNS查询的应用程数据包,内容同样是baidu.com的IP地址是多少,目标IP为路由器配置的上游DNS服务器,将其发送到公网

数据包会来到运营商给我们分配的DNS服务器 3.3.3.3,收到数据后得知了路由器想要百度的IP,同样先检查自己的缓存列表。没有的话再询问其他上游DNS服务器。运营商的DNS一般会配置多个上有DNS服务器,具体发给哪一台会根据自己的负载均衡策略选择。假设发给了IP为6.6.6.6的服务器,并且假设这台服务器没有配置DNS转发,所以不会将请求转发给其他DNS服务器。

之后这台上游服务器会负责帮我们查询百度的IP。查询方法不是我们关注的重点,感兴趣的朋友可以自行了解DNS的迭代查询。我们只需要知道,最终这台DNS服务器会找到解析baidu.com的权威DNS服务器,并把下面这条数据包交给他。假设权威DNS服务器地址是9.9.9.9

百度的权威服务器收到请求后,他就会有baidu.com这个域名绑定的IP,于是将IP和TTL返回给6.6.6.6这台DNS服务器。TTL全称time-to-live,在DNS中表示缓存时间。假设为十分钟,6.6.6.6收到数据后会缓存下来。并将结果告诉3.3.3.33.3.3.3同样也会将结果缓存,并发回给我们的路由器。路由器也是同样的操作。缓存下来并将结果发回给我们的电脑。但这里需要注意,由于家用电脑的性能有限,可能为了节省内存,会将TTL值设置的相对较小。比如30秒。TTL值没必要按照权威DNS服务器的值。当浏览器获取到IP之后,才正式的开始发起访问百度的请求。后面的步骤就是正常访问ip地址了。之后如果你再访问百度,还是先检查缓存列表,如果记录还在缓存列表中,说明TTL还没有到期。会直接从缓存中获取百度的IP。并不会发起DNS请求。如果TTL值到期了,则会将记录从缓存中移除,就又得重新发起DNS请求获取百度的IP。

什么是DNS投毒和泄露?为什么存在这些问题?

为什么需要DNS以及DNS的执行流程相信大家都清楚了,接下来就是我们今天的主题。什么是DNS投毒和泄露?

先来看一下两者定义(上图为DNS泄露,下图为DNS投毒)

简单来说,DNS泄露是指代理访问的网站没有通过节点进行DNS请求,导致电脑向公网发起明文DNS请求,导致访问目的泄露。而DNS投毒是指由于DNS泄露的问题,导致被发回的DNS查询结果中夹杂了目的之外的结果

所以一切问题的关键就在于怎么解决DNS泄露问题。那么为什么会存在DNS泄露?

在这之前,你可以先在你觉得安全的代理模式下访问一下这个网站,看一下返回的DNS查询过程中是否有中国大陆的DNS服务器。如果有,那么在我对DNS泄露定义的理解下,你的节点已经存在DNS泄露问题。如下图。

这里请注意,DNS泄露的说法只有在我们使用代理之后才存在。假设我们没有使用代理的情况下查询DNS泄露,列表中返回了国内的DNS服务器,这只能说明这些DNS服务器负责帮我们进行了DNS查询,并不代表DNS泄露了。

如果此时我将电脑的本地DNS服务器改成1.1.1.1,意思是让1.1.1.1负责DNS解析,列表中返回了美国的DNS服务器,这也不能说明DNS没有泄露。只有当我们通过代理访问目标网站时,电脑给互联网发送了请求目标网站IP地址的明文DNS请求,这种情况才是DNS泄露。

要讲清楚这个问题就必须先来了解这个网站是如何进行DNS检测的

在网站下按F12调出浏览器的开发者面板,来到网络面板

可以看到网站在不停的发送请求,并且每个请求访问的域名都不一样,目的是为了确保我们的DNS缓存,以及所有上游DNS缓存中都没有这条记录。根据刚才的讲解,当浏览器中没有找到缓存,会构建查询域名IP的DNS请求。由于域名完全随机,路由器也不可能会有缓存,请求将被转发到运营商的DNS,由于运营商配置了多个上游DNS服务器,可能会交给不同的上游,我们假设交给了6.6.6.6,这台DNS负责去寻找这个域名的IP,最终会找到这个域名的权威DNS服务器。

此时的数据包长这样

由于设置了泛解析,查询任何子域名都能获取到IP,ipleak的权威服务器在返回结果给你的时候偷偷做了一个动作,记录下了发送给他请求的DNS服务器的IP地址,并且告诉了你。由于每个访问者使用网站进行查询时域名是完全随机的,也就是说每个人发起DNS查询的域名都不一样,所以网站根据这条查询记录就能知道是你在使用这个DNS服务器。再根据IP归属地得知这个DNS服务器的地理位置。由于网站在不停的发送新的域名进行探测,运营商的DNS可能会使用不同的上游DNS,所以可以探测到多个不同的DNS服务器IP地址。

另外,虽然1.1.1.1是任播DNS,但我们国内使用的话会被反向优化到美国,用美国的DNS服务器,那么它的上游DNS也肯定是美国的IP。所以当我将本地的DNS改成1.1.1.1之后,DNS泄露查询到的都是美国的DNS服务器。所以DNS泄露的并不是你的电脑IP,而是你上上上……上上上游的DNS服务器ip

DNS泄露在代理中的问题

大家可能松了一口气,觉得并没有什么大不了。那么再来看一下使用代理之后是一个什么情况。

以普通的全局系统代理为例,首先,浏览器要访问谷歌。

由于配置了系统代理,浏览器不再进行DNS查询。而是直接将访问网站的请求,交给我们的代理客户端。

代理客户端拿到请求之后,此时会有两种情况。一种是不发起DNS请求就能判断他是走直连还是走代理,这种情况基本不存在DNS泄露。

另一种情况是发起DNS请求,获取到IP之后,再来根据IP判断是走直连还是走代理。这种情况大概率存在DNS泄露。即使你配的DNS是1.1.1.1或者8.8.8.8,这种国外的DNS提供商,只要发起来DNS请求,那就泄露了。还会因为上面我们所分析的情况迷惑你,因为查不到国内DNS,还以为没有泄露。假设clash使用了IP规则进行分流,就需要先得到谷歌的IP地址,于是会发起DNS请求,内容是谷歌的IP地址是多少。

当这条请求发给互联网的DNS服务器之后,问题就发生了。DNS请求是完全明文的,除非你使用DoT或者DoH进行加密。或者有些客户端默认会将DNS请求通过节点加密进行远程DNS。但这两种情况都会增加延迟,不算是好的方案。而且大部分用户都不会进行加密和远程DNS。也就是说,运营商或者中间任何一台路由器都可以看到你的意图是访问谷歌。后面的情况也就不用我细说了。当你要完谷歌的IP之后,马上给其他服务器发送一堆加密数据,鬼都知道你在干啥。

甚至可能根据这个行为来封禁节点服务器,再比如奈飞这种对地区要求高的网站,如果代理软件偷偷的发送了一个DNS,并且存在DNS泄露,负责跟奈飞的权威DNS服务器对话的这台DNS服务器的IP归属地就会和当前访问奈飞网站的代理IP不是同一个地区,那就判定你在使用VPN工具从而禁止观看。到这里,其实我们就已经能够解释文章开头提到的Steam VPN检测问题。并且是一个非常强的代理特征就是DNS泄露以及DNS泄露所导致的问题

二、Clash的DNS处理方式

问题已经提出来了,那要怎么解决呢? 那当然是把提出问题的人解决掉就好了

要想解决这个问题,就需要先来看一下目前主流代理软件是如何处理DNS请求的。

1.原理

考虑到很多人可能和我一样不喜欢看官方文档,所以这里我主要给你介绍一些最主要的基本功能,也是我们下面用得到的

先来看一下以clash内核为主要代理核心的工具的基本配置文件,但不要被吓到了,我会解释它

当我们在电脑上运行_Clash for Windows_时,它便调用了_Clash_的内核且加载了类似像上面的yaml配置文件。配置文件可能是机场提供的订阅地址或者是你通过订阅转换获取的。总之配置文件的内容大概长这样

解释一下上面各个参数的作用

  • port:监听端口

  • proxies:节点

  • proxies-groups:节点组

  • rule:分流规则

上述配置中,监听端口7890,配置了两个节点,以及两个节点组,第一个节点组包含两个节点,默认选中了香港节点,第二个节点组包含两个节点,默认选择了美国节点

其中分流规则参数如下

  • DOMAIN:域名全匹配

  • DOMAIN-SUFFIX:后缀匹配

  • DOMAIN-KETWORD:域名关键字匹配

  • SRC-IP-CIDR:源IP/IP段匹配

  • IP-CIDR:目标IP/IP段(ipv4)

  • IP-CIDR6:目标IP/IP段(ipv6)

  • GEOIP:使用GEOIP数据库进行ip属地匹配

  • DST-PORT:目标端口

  • SRC-PORT:源端口

  • PROCESS-NAME:发起网络请求的进程名

  • MATCH:以上所有规则均不匹配的流量

现在假设我们访问的是google.com

首先看第一条规则。默认选中的节点是香港节点,所以会将请求交给香港节点。但我们访问的是www.google.com,而不是google.com,所以不匹配进入下一条

下一条规则是后缀匹配youtube.com,很明显也不匹配

再下一条规则是关键字匹配 youtube,很明显也不匹配

下一条又是域名匹配,如果是ad.com,直接reject,相当于丢弃 主要用于广告屏蔽

再下一条匹配源IP段,如果源IP段是192.168.1.201,那么就走direct,也就是直连,主要用于局域网其他设备的分流,由于这个请求是本机浏览器直接发送给clash的,所以原IP为127.0.0.1,因此这条规则也不匹配

下一条匹配目标IP段,如果访问的目标是127.0.0.0/8这个网段,就走direct直连,但是我们用来匹配的是www.google.com这个域名,很明显IP和域名是没办法进行对比的,所以需要先通过上集讲的,DNS解析获取谷歌的IP地址,但是,这条匹配规则后面加上no-resolve,表示不进行DNS解析,相当于直接跳过了这条基于IP的匹配规则

下一条是IPv6的匹配规则,同样加了no-resolve,也直接跳过了

再下一条匹配GEOIP,GEOIP是一个数据库,里面包含了常见的IP归属地分类,如果是国内的IP,就走直连。由于后面没有加no-resolve,所以需要将谷歌的域名解析为IP进行匹配,由于Clash没有配置DNS模块,所以会交给电脑配置的本地DNS服务器进行解析,Clash会构建一条查询谷歌域名的DNS请求,发送给电脑配置的本地DNS服务器,也就是路由器,由于DNS请求是明文的,此时就产生了DNS泄露,后面的解析步骤就是上面讲过的内容了。

这里要注意的是,请求被墙的域名,大概率你会收到一条被污染的DNS响应,假设谷歌的真实IP是4.4.4.4,但是你收到的谷歌IP却是5.5.5.5,这就是DNS污染,clash拿到这个被污染的DNS响应,以为谷歌的IP就是5.5.5.5,于是会用这个IP和GEOIP的规则进行匹配,发现这个IP的归属地不是国内,所以这条规则也是不匹配的,有的朋友可能有疑问 你怎么知道他不是国内的,我也不知道,猜的。因为一般情况下不会污染成国内的IP,但也不能保证。可以思考一下如果是国内的IP会导致什么情况。

接着匹配源端口和目标端口以及进程名称

如果是curl这个进程发起的请求,则交给节点组2,节点组2默认选中了美国的节点

很明显以上规则都无法匹配www.google.com,_Clash_还有一个兜底,叫做MATCH,所有没有匹配的规则全都交给MATCH处理,也就是节点组1,所以这条访问谷歌的请求,交给了节点组1的香港节点,将会使用香港ss节点的配置信息,对数据进行加密,这里要注意的是,刚才DNS获取到的IP,只是用来进行IP规则的分流匹配,你发给香港节点还是域名,所以节点收到数据后,会在他的网络环境中再次进行DNS解析,获取正确的谷歌IP。因此本地的DNS污染并不会影响访问,但是存在DNS泄露

2.解决方案

利用no-resolve

一种是给所有基于IP分流的匹配规则都加上no-resolve,这样就不会触发DNS请求。但是会有一个问题,假如此时我想访问百度,clash收到请求后会一条条进行匹配,当来到GEOIP这条规则时,如果没有加no-resolve则会发起DNS请求获取百度的IP。返回的肯定是一个国内的IP。也就匹配上了这条规则,将会走直连。但是为了防止DNS泄露,加上了no-resolve,这样就不会发起DNS请求,百度的域名也就无法和IP归属地进行匹配,这条规则直接跳过了。最终将会交给MATCH走代理出去。这肯定不是我们所希望看到的,所以不仅要给所有IP规则都加上no-resolve,同时加上了国内域名走直连的分流规则。这样百度等国内网站通过域名匹配也可以走直连。但是一些小众的国内网站并不在规则列表里,所以会出现走代理的情况。这其实就是_v2rayN_的绕过大陆模式,这样的匹配效率最高,不会为了分流而发起DNS请求。所有国外网站都会走代理。缺点是一些小众的国内网站也会走代理,你可能需要单独为这些网站添加走直连的规则。如果你平时主要访问国外网站,这种模式比较适合你

黑名单模式

另一种方式类似_V2rayN_的黑名单模式,就是在IP规则前先把所有的黑名单网站的匹配规则先加上。比如google youtube facebook twitter等等。这样就可以优先匹配到域名规则直接交给节点处理,不会再往下匹配,也就不会触发IP规则的DNS请求了。需要注意的是,有些机场的_Clash_订阅会把没将no-resolve的IP规则放在前面,导致优先发起了DNS请求,造成DNS泄露。如果用这个配置访问上面演示过的DNS泄露网站,由于没有匹配到上面的任何规则,会来到GEOIP这条没有加_no-resolve_的IP规则,于是使用本地DNS发起了DNS请求。数据包如下

前面也讲过,这个网站会记录你的上游DNS服器的IP地址。很显然列表中将会显示国内的DNS提供商。_Clash_得到IP后发现归属地不是国内,于是会继续向下匹配,最终还是走了代理。节点收到域名后会再次进行DNS获取IP,所以列表中的香港DNS是节点服务器使用的DNS提供商。

我在上面提到,我认为如果列表中有国内的DNS提供商,说明肯定泄露了,现在你觉得这种情况算DNS泄露吗?虽然检测到了国内的DNS提供商,但是如果你只在意google,youtube等网站有没有泄露,这样的配置确实是没有泄露……

吗?

真有这么简单的话,就不会难倒这么多群友了。如果使用系统代理这种侵入性较小的方式,这样的分流配置确实就能解决DNS泄露。真正让人难以理解的是在软路由,手机端和TUN模式这种透明代理的情况下的DNS流程。前面讲的这么多,主要是让大家熟悉_Clash_的执行流程。接下来才是的重点。

先来改变一下当前的网络结构

Clash_不再设置为系统代理,而是开启了TUN模式,或者不在电脑中运行,直接运行在路由器里。路由器会将局网其他设备访问互联网的数据转交给_Clash,这样更容易理解一点。配置文件跟系统代理主要的区别是添加了DNS模块

此时浏览器访问谷歌,根据上面讲的内容,浏览器将首先发起DNS请求,获取谷歌的IP,数据包会来到路由器,路由器会将DNS请求劫持到Clash中,Clash收到浏览器的DNS请求,他也不知道谷歌的IP是多少,再看看自己的DNS配置是redir-host的模式,这个模式必须给浏览器返回一个真实的IP。所以,Clash需要先获取谷歌的IP地址,于是向配置的这三个上游DNS服务器同时发起DNS请求,谁第一个返回就用谁的结果。假设114最先返回了谷歌的IP为5.5.5.5,记住这个是被污染的结果。

Clash_有个映射表,用于保存查询的结果,并将结果返回给浏览器,浏览器拿到谷歌的IP,向这个IP发起访问谷歌的http请求,请求来到路由器的_Clash。_Clash_查看映射表中IP对应的域名,用域名进行规则匹配,匹配了第一条规则,于是将请求交给节点组1的香港节点处理。

此时浏览器发给路由器的数据包长这样

这里要注意,交给节点处理的还是谷歌的域名,由远程的节点服务器再次进行DNS解析,获取正确的谷歌IP,所以本地即使是污染的IP也并没有关系。

但由于redir-host必须给浏览器返回一个真实的IP,所以_Clash_必然要发起DNS请求去获取谷歌的IP,这样就必然存在DNS泄露。还有一个更大的问题,假如浏览器接下来访问youtube,还是先发起DNS请求获取youtube的IP,由于使用redir-host的模式,Clash会向三个上游DNS服务器同时发起查询youtube IP的DNS请求。假设114最先返回youtube的IP为5.5.5.5,和刚才谷歌的IP一样,他们搭建在同一个服务器上,浏览器拿到IP后发起访问youtube的http请求,请求来到Clash,这时查看映射表就出了问题

IP是一样的,_Clash_不知道要访问哪个域名。大家可能会想,http请求中不是有youtube的域名吗?直接看http的域名不就行了?但是_Clash_并没有类似_V2ray_系列的流量探测功能,所以无法探测http请求的域名。为了解决这个问题,_Clash_的redir-host模式不再进行远程DNS,也就是节点服务器收到的直接就是5.5.5.5这个IP地址,这样就不需要查看映射表了。

但是又会造成另一个问题,5.5.5.5是被污染的IP,节点服务器拿到这个IP肯定无法正确的访问youtube。如果被污染的这个IP确实搭建了网站,还会出现访问youtube却进入其他网站的奇特现象。由于证书和域名不匹配,浏览器会发出报警

并且有时会污染到一个保留IP,会出现其他意料之外的情况。

fallback

此时可以加上fallback解决DNS污染的问题,里面主要放不会被污染的DNS服务器,比如经过加密的dot或者doh。

当发起DNS请求的时候,会同时向上游的三个DNS服务器以及fallback中的DNS服务器发起DNS请求,如果上游DNS服务器返回的IP不是国内的,就使用fallback中DNS返回的IP,以确保国外IP没有被污染,节点服务器可以拿到正确的IP进行访问。

但是这样又会引起其他的问题。原本我以为_Clash_的doh域名会匹配规则走代理,但经过测试发现是走直连,不知道是不是bug。按正常逻辑应该要走代理,所以doh是通过本机IP直接发起的,获取到的IP地址是和本机地理位置相近的CDN服务器,这个结果对节点服务器而言可能位置比较远,无法获得CDN优化,甚至可能导致速度变慢。

另外国内直联国外doh体验极差,间歇性被墙,而且加密DNS需要先握手建立连接,延迟也比较高,所以国外加密DNS提供商基本提供不了服务。还有一些机场提供DNS解锁流媒体的服务,如果直接传IP给节点,节点就无法发起DNS请求。导致解锁无效

fakeip

眼看redir-host的问题越修越多,就在今年一月,官方直接把redir-host的模式砍了。是的没错,直接就砍了。

果然解决提出问题的人才能彻底的解决问题

其实这个问题_Clash meta_通过加入流量探测功能解决了,但_Clash meta_到我写到这的时候,其最新版依然是前阶段被爆出存在RCE漏洞的版本,以后有机会再说说由_Clash_ GUI和内核导致的RCE漏洞。

目前_Clash_做透明代理 主要使用fakeip模式。ios端的小火箭QuantumultX surge就是只用fakeip。_V2ray_也有fakeip功能,叫做fakeDNS或者虚拟DNS。说的都是同一个东西。

当我们把DNS配置为fakeip之后,此时浏览器访问谷歌浏览器,首先发起DNS请求获取谷歌的IP,请求会来到路由器,路由器会将DNS请求劫持到_Clash_中,_Clash_收到浏览器的DNS请求,他也不知道谷歌的IP是多少,再看看自己的DNS配置是fakeipfakeip并不需要给浏览器返回一个真实的IP,_Clash_会心一笑,直接从配置的私有IP段中选择一个假的IP,生成DNS响应,返回给浏览器,并且记录了IP和域名的映射关系

映射关系如下,ip为_Clash_自己生成的假ip

浏览器拿到这个假的IP之后,向这个IP发起访问谷歌的dns请求。请求来到路由器的_Clash_,_Clash_查看映射表中IP对应的域名,用域名进行规则匹配,匹配到了第一条规则。于是将请求交给节点组1的香港节点处理,交给节点处理的还是谷歌的域名。由远程的节点服务器进行DNS解析,获取谷歌的正确IP,可以看到本地并不需要进行DNS解析,也就不存在DNS泄露了,延迟也会降低,并且解决了多域名指向同一个IP的情况。

因为返回的都是假的IP,_Clash_可以控制每个IP只对应一个域名,如果此时再访问DNS泄露网站,首先会返回一个假IP给浏览器,浏览器再发起http请求。此时浏览器发给路由器的数据包如下

_Clash_拿到数据包后对映射的域名进行规则匹配,由于没有匹配到任何规则,会来到GEOIP这条没加no-resolve的IP规则,此时Clash也不知道这个域名的IP,于是同时向上游的三个DNS服务器,和fallbaCk中的DNS服务器发起DNS请求,Clash得到IP后 发现归属地不是国内,于是会继续向下匹配,最终匹配MATCH走了代理。

由于同时向多个DNS发起请求,泄露测试列表中会返回好几家DNS提供商,这种情况算不算泄露就仁者见仁了。如果你认为谷歌这种黑名单网站没漏,就不算泄露的话 也并没有什么不妥。fakeip这种侵入式的解决方案也并不完美。虽然能少一次DNS请求,降低延迟,但毕竟是假的IP,在某些情况下会出现不可预料的问题。比如访问百度获取到了假的IP,一段时间后_Clash_意外退出了,而电脑中缓存了百度的假IP,再次访问百度的时候就会访问假IP,此时就会无法访问。

一些其他问题

虽然Clash将DNS响应中的缓存ttl值设置为一秒,但上面也说了,应用程序并不一定会遵守ttl值的规定。为了防止频繁发起DNS请求,可能会延长缓存时间,还有一些程序会开启DNS重绑保护,当DNS获取到一个私有IP则认为出现了非法DNS劫持而被丢弃。

典型的是windows系统使用fakeip会出现联网图标显示没网的情况

解决方法是加入fake-ip-filter,放在里面的域名不会返回假的IP,会发起DNS请求获取真实的IP地址,相当于回退到redir-host的模式。缺点想必看到这里也就不用我多说了

另外由于UDP在有些场景下必须使用真实的IP,所以目前_Clash_处理udp流量的域名即使是使用fakeip模式,也一定会发起DNS请求。比如基于UDP的QUIC协议,也就是浏览器用的HTTP3。启用了QUIC的浏览器访问youtube这种支持QUIC的网站,就会发起DNS请求,导致DNS泄露。

解决办法是禁用浏览器的QUIC功能。

至于其他fakeip客户端对UDP,是不是同样的处理方式我不清楚。反正Clash目前是这样子的。虽然有这些小问题,但是瑕不掩瑜,Clash还是非常强大好用的

三、Clash DNS泄露实测

我们以用户量最多的_Clash for windows_为例,规则是全局模式使用VMESS+WS的节点,

DNS泄露在透明代理中比较常见,所以我们启用TUN模式

这是_Clash for windows_自带的配置模板,没有做任何修改,默认配置

尝试访问谷歌,可以正常访问

进入泄露检测的网站ipleak,列表中出现了美国和中国的DNS服器

按照很多人的理解是全局就不会泄露,那理应不会出现中国的DNS服器。此时我们打开wireshark抓包工具,查看物理网卡的DNS请求

可以看到网卡在不断的发起DNS查询,查询的目标网站就是ipleak的域名,发给了8.8.8.8 ,223和114,也就是我们在_Clash for windows_中配置的三个DNS服务器。

我们发出的内容是请求这个域名的IP地址,DNS服务器回应的IP是下面这个

在一般的家庭网络拓扑中,wireshark能看到什么内容,运营商也能看到什么内容。所以运营商知道你在向8.8.8.8,223和114获取ipleak的IP地址。

我们可以点击这里查看最终交给_Clash_执行的配置文件

造成这个问题的原因是,我们的DNS配置的是redir-host的模式。

因为redir-host它必须要返回一个真实的IP,所以必然发起DNS请求,即使是全局模式。不过redir-host已经被_Clash_弃用了,我这里只是演示一下这种情况,是通过mixin将fakeip替换为redir-host

将mixin关闭,此时clash的DNS恢复成了fakeip模式

fakeip 全局模式会泄露吗?刷新网页试一下,再清空抓包记录

可以看到例表中依旧出现了国内的DNS服务器。回到抓包工具

还是同样的发起了DNS请求。但区别是这一次不是发给clash配置的上游DNS,而是发给了192.168.2.1,也就是我物理网卡的DNS服务器。造成这个问题的原因是Windows的智障多宿主DNS解析,会使用所有网卡发起DNS请求。(微软美其名曰优化DNS负载)

解决这个问题要在组策略中找到禁用智能多宿主名称解析,将其启用即可

此时我们再来测试,就会发现列表中不再有国内的DNS服务器了

抓包工具也没有多余的DNS请求,DNS请求全都由节点发起

貌似已经解决了全局模式下的DNS泄露问题,现在我尝试访问谷歌后,回到抓包工具

可以看到还是发起了DNS请求,而且是发给clash的3个上游DNS

运营商知道你在查询谷歌的IP

也就是说fakeip全局模式下,即使DNS泄露网站检测不到国内的DNS服务器,还是可能存在DNS泄露

造成这个问题的原因我前面也讲到了,clash目前处理UDP域名,始终会发起DNS请求获取真实的IP,可以在log面板中看到访问谷歌是通过UDP的

因为浏览器开启了QUIC访问,这里就不多赘述了

关闭后访问谷歌,便不会再使用UDP

到这里为止,全局模式下的DNS泄露已经基本解决了。只有当你解决了全局模式下的DNS泄露问题,才有必要研究分流模式下的DNS泄露。否则在全局都没有解决的情况下,你分流再怎么调也都是会漏

解决分流DNS泄露就比较简单了,前提是你已经熟悉了clash的分流规则

将clash切换到规则模式

可以看到列表中又会出现国内的DNS服务器

此时就需要研究_Clash_的分流规则,为了演示我只配置了几条规则

由于ipleak这个网站不在规则列表中,这条IP规则将会使用_Clash_内置的DNS服务器发起DNS请求,所以列表中出现了国内的DNS服务器

但是YouTube和谷歌由于存在使用代理访问的规则,所以访问YouTube并不会发起DNS请求。也就是说虽然DNS泄露检测的列表中显示了国内的DNS服务器,但是访问YouTube并不会泄露DNS。这种属于黑名单模式。也就是将所有的黑名单网站添加到规则中,剩下的网址交给DNS解析。获取到IP之后再根据IP归属地进行精确的分流

再来看另一种DNS泄露的情况,此时我把ipleak的网站加入了走代理的规则。注意插入的位置很关键,要插入到IP规则的上面,否则还是会先发起DNS请求

此时再来刷新网页,这样就没有国内的DNS服务器了。因为DNS请求已经交给节点去处理了。但如果此时我访问Paypal,由于没有域名规则,所以会发起DNS请求获取IP进行GEOIP的规则匹配,这样对于Paypal来说就是存在DNS泄露,Paypal可能会通过这种检测方式,来判断你的网络环境存在风险。所以你如果只是将DNS泄露检测的网站,加入规则列表中是掩耳盗铃的做法

接着再来说一下我们普通用户的DNS应该怎么配。我看到有些机场的clash配置文件DNS都配出花了,但其实我们普通用户只需要填入一个能正常提供DNS服务的就行了。而每个家庭的网络默认就会分配运营商提供的DNS,你配的什么114 223都不一定有它快。

有些朋友可能会说114 223稳定啊,如果说运营商给你提供的DNS都不稳的话,说明你的网也快断了。所以对于大部分用户来说直接使用运营商提供的DNS即可,不管是黑名单模式还是白名单模式都适用。

另外关于用DoH这种加密DNS,如果用国内的DoH,你只是防止了运营商不能看到你请求的网址,DoH的服务提供商还是能看到,还不如不加密。原版的_Clash_ DNS请求是直连国外DoH,间接被墙,根本受不了。所以如果你平时是使用_Clash_的话,DNS就和我一样,配置成网关默认DNS即可

Clash也支持直接使用DHCP获取DNS服务器,效果是一样的,这样简单的DNS配置能满足大部分用户的需求。当然也没有什么配置可以放之四海皆准