Total Pageviews

Sunday 21 July 2019

利用neatdns-by-yingziwu解决dns污染问题

被GFW污染的域名毕竟是少数,那为什么不采用黑名单机制,仅将被污染域名发往国外DNS进行解析,其它域名则在本地进行解析。

由于GFW的DNS污染模块存在缺陷,无论请求的记录是什么(AAAA、MX、NS…),返回的均为A记录。 因此会日志中留下相关记录,所以使用黑名单机制可以根据日志自行发现被污染域名,而无须依赖外部列表。
本人尝试在本地使用 bind 自行搭建使用黑名单机制的防污染DNS,下面简单介绍一下实现过程, 相关代码可以到 Github 下载。

判断一个域名是否遭到DNS污染

使用黑名单机制的第一步便是判断一个域名是否遭到DNS污染。
第一节可知GFW的DNS污染是基于旁路抢答实现的。 GFW为了实现无死角全覆盖的DNS污染,直接劫持了53端口,对所有可疑请求进行抢答。 因此可以利用这一点,主动探测被污染域名。
  1. 找一个境外未开放53端口的主机。
  2. 向这个主机发起DNS查询。
  3. 由于其未开放53端口,不可能做出DNS响应。所有响应均为伪造的。
未被污染域名:
~ % dig www.4399.com @example.com

; <<>> DiG 9.13.7 <<>> www.4399.com @example.com
;; global options: +cmd
;; connection timed out; no servers could be reached
被污染域名:
~ % dig www.google.com @example.com

; <<>> DiG 9.13.7 <<>> www.google.com @example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- 24322="" a="" id:="" name="rest_code_e2c8ae1858be43df97fa5c45873c1035-7" noerror="" opcode:="" query="" status:="" style="background-color: transparent; box-sizing: inherit; color: #4183c4;">
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;www.google.com. IN A ;; ANSWER SECTION: www.google.com. 176 IN A 31.13.79.17 ;; Query time: 43 msec ;; SERVER: 93.184.216.34#53(93.184.216.34) ;; WHEN: Thu Feb 28 21:22:32 CST 2019 ;; MSG SIZE rcvd: 48 ~ % dig cmx.im @example.com ; <<>> DiG 9.13.7 <<>> cmx.im @example.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- 53543="" a="" id:="" name="rest_code_e2c8ae1858be43df97fa5c45873c1035-26" noerror="" opcode:="" query="" status:="" style="background-color: transparent; box-sizing: inherit; color: #4183c4;">;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;cmx.im. IN A ;; ANSWER SECTION: cmx.im. 196 IN A 31.13.64.49 ;; Query time: 41 msec ;; SERVER: 93.184.216.34#53(93.184.216.34) ;; WHEN: Thu Feb 28 21:22:42 CST 2019 ;; MSG SIZE rcvd: 40
防火墙的缺陷:
~ % dig cmx.im AAAA @example.com

; <<>> DiG 9.13.7 <<>> cmx.im AAAA @example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- 11667="" a="" id:="" name="rest_code_1641f9235a6e42598ab5a433fb7bd8a7-7" noerror="" opcode:="" query="" status:="" style="background-color: transparent; box-sizing: inherit; color: #4183c4;">
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;cmx.im. IN AAAA ;; ANSWER SECTION: cmx.im. 169 IN A 8.7.198.45 ;; Query time: 150 msec ;; SERVER: 93.184.216.34#53(93.184.216.34) ;; WHEN: Thu Feb 28 21:26:51 CST 2019 ;; MSG SIZE rcvd: 40 ~ % dig cmx.im MX @example.com ; <<>> DiG 9.13.7 <<>> cmx.im MX @example.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- 17918="" a="" id:="" name="rest_code_1641f9235a6e42598ab5a433fb7bd8a7-26" noerror="" opcode:="" query="" status:="" style="background-color: transparent; box-sizing: inherit; color: #4183c4;">;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;cmx.im. IN MX ;; ANSWER SECTION: cmx.im. 130 IN A 67.228.221.221 ;; Query time: 44 msec ;; SERVER: 93.184.216.34#53(93.184.216.34) ;; WHEN: Thu Feb 28 21:26:56 CST 2019 ;; MSG SIZE rcvd: 40 ~ % dig cmx.im NS @example.com ; <<>> DiG 9.13.7 <<>> cmx.im NS @example.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- 12967="" a="" id:="" name="rest_code_1641f9235a6e42598ab5a433fb7bd8a7-45" noerror="" opcode:="" query="" status:="" style="background-color: transparent; box-sizing: inherit; color: #4183c4;">;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;cmx.im. IN NS ;; ANSWER SECTION: cmx.im. 67 IN A 31.13.83.1 ;; Query time: 44 msec ;; SERVER: 93.184.216.34#53(93.184.216.34) ;; WHEN: Thu Feb 28 21:27:01 CST 2019 ;; MSG SIZE rcvd: 40
因此只要有域名列表,便能可以根据上述原理扫描出被污染域名。
Alexa 免费提供了 Top 1,000,000 网站的列表,你可以在这里下载。
使用这个列表,再加上 zdns 这个高效的 DNS 查询工具,便可以快速扫出 Alexa top 1 million 中被DNS污染的域名清单。

初始化

虽然可以根据日志,逐渐发现被污染的域名,但这需要时间。为了良好的用户体验,我们需要初始化。
一切开始之前,你需要安装 zdns 以及 python-tldextract
安装好之后,下载此项目
git clone https://github.com/yingziwu/neatdns.git
根据实际情况,适当修改 env.sh,然后运行 init.sh
bash init.sh
该脚本将会耗时30分钟。
如果你的网络状况良好,可以修改 zdns -threads 参数来缩减扫描时间,-threads 默认为 1000 进程。
脚本运行完毕后,将会在 $BIND_DOMAIN_LIST_PATH 输出被污染的域名列表,请保证 $BIND_DOMAIN_LIST_PATH 所在目录您有写入权限。
当然如果你不想等待这半个小时,你可以直接下载这个列表(在线查看)。

配置 bind

bind 默认配置,无需太多更改,做出如下修改就可以了。
添加日志模块:
logging {
  channel resolverlog {
    file "/var/log/named/resolver.log" versions 5 size 1m;
    severity notice;
    print-time yes;
    print-severity yes;
  };
};
转换域名列表:
#!/usr/bin/env python3

import json

dns_server_list = ['202.38.93.153','202.141.176.93','202.141.162.123']
base_dir = '/etc/bind/'

def gen_zone(domain,dns_server_list):
  temple_1 = 'zone "%s"'
  temple_2 = ' {type forward; forward first; forwarders { %s};};'
  zone = temple_1 % domain
  forwarders = ''
  for dns_server in dns_server_list:
      forwarder = dns_server + ';' + ' '
      forwarders = forwarder + forwarders
  zone = zone + temple_2 % forwarders + '\n'
  return zone

def gen_gfw_zones():
    with open(os.path.join(base_dir,'domain_list_poisoning.json'),'r') as f:
        domain_list = list(set(json.load(f)))
    domain_list.sort()

    with open(os.path.join(base_dir,'named.conf.gfw-zones'),'w') as f:
        for domain in domain_list:
            t = gen_zone(domain,dns_server_list)
            f.write(t)
    return

if __name__ == '__main__':
  gen_gfw_zones()
在 named.conf 文件中包含 named.conf.gfw-zones 文件:
include "/etc/bind/named.conf.gfw-zones";

定时更新

定时运行 update.sh 即可,建议一小时运行一次。

定时验证,去除无效条目

定时运行 validate.sh 即可,建议两周运行一次。
from https://blog.bgme.me/posts/some-ideas-about-anti-pollution-dns-server/

项目地址:https://github.com/yingziwu/neatdns
------------

利用 DNS 白名单,自行实现「智能解析」,搭配 ChnRoute 实现最佳白名单方案

Shadowsocks 的 Windows 和 macOS 客户端使用的 pac 代理模式都是基于一个叫 gfwlist 的黑名单维护的。这个名单维护的是被 GFW 拉黑的域名的列表。然而主要问题在于 GFW 真正使用的名单肯定不会轻易公开,gfwlist 依靠社区进行维护,经常会出现更新不够及时的情况。而且比如像 GitHub 这种网站,GFW 态度不确定,连接情况时好时坏,一般会倾向于把它直接加入需要代理的域名列表。而且这个黑名单相当于在赤裸裸宣告「天朝有个 GFW 做审查,这些网站都是被审查的」,风险相当大,说不定哪一天就被东风快递了。
接触透明代理之后听说了 ChnRoute,目前对 ChnRoute 的应用可以理解为 IP 白名单,也就是说在 ChnRoute 内的 IP 都进行直连,不在里面的统统走代理。虽然这样会导致一些周边国家,比如日本韩国这样,直连效果并不差的网站被强行走代理,不过对于非游戏党来说这样的问题还是可以接受的。
ChnRoute 由 CNNIC 直接进行维护。官方维护的东西可以认为比较靠谱,而且不会有什么风险,实际使用效果也还是非常好的。ChnRoute 更新频率并不算高,很长一段时间不更新问题也并不大(不过如果放到路由器上跑的话,在有 BuildRoot 环境的时候还是建议费点心思维护一下)。
结合 gfwlist 和 chnroute 思考,DNS 有黑名单,如果 IP 有白名单的话,那是不是 DNS 也可以有白名单?只要维护一份国内常见网站的域名列表,在这个列表内的域名使用国内 DNS 解析,结合智能解析,解析出来的地址应该也都是都是 ChnRoute 内的 IP,可以放心直连;不在列表内的使用可信 DNS 解析,结合智能解析,解析结果也是适合 Shadowsocks 服务端的 IP,这样直连和隧道都能得到最佳解析效果,岂不美哉。
然后就真的找到了这么个东西:
这份列表被命名为「accelerated-domains.china.conf」,寓意是「国内域名加速列表」。Readme 中的用途倒是基本写明白了「可以用来加速 VPN」,这种就是做事做得不够干净没有很好的规避风险的表现(雾)其实白名单的话 Readme 写的冠冕堂皇一点的话完全就没有什么风险,美滴很。白名单的好处也是不需要经常维护更新,大网站不会天天加域名改域名,就算加了临时走一下代理也不太影响使用,最多慢一点。不过这个名单的维护频率高得离谱,当然也不是坏事了。
列表的形式是 dnsmasq 配置文件,其中指定了这些域名使用 114.114.114.114 进行查询,那其实只要放到路由器里面,然后像下图这样修改 dnsmasq 的配置,并另外指定 dnsmasq 上游为一个可信 DNS(比如 DNS over HTTPS 或者 Shadowsocks 的 UDP 隧道)就行了。

实际使用的效果非常好,国内域名自动走 114.114.114.114 解析,解析只需 20ms 左右。国外域名解析通过 UDP 隧道,200ms。
这种方案也是草民目前使用的方案,优点十分突出,无论是纯净程度、解析速度、稳定性、线路优化都几乎完美,配置难度在所有配置中也不是最高的(不需要重新编译或者到处找第三方包)。缺点主要是这个白名单十分庞大,对于配置比较低的路由器来说完全吃不消(当然草民的是软路由,资源日常过剩,并不存在这个问题)。

No comments:

Post a Comment