斐讯N1 – dnsmasq+chinadns+unbound实现无污染智能DNS
此前DNS方案:
- 已经基于redsocks实现TCP全局翻墙
- 已经搭建过unbound并TCP upstream到8.8.8.8
- TCP流量通过ipset过滤掉了境内IP,只对非大陆IP翻墙
此方案的原理是所有的DNS解析都是经过unbound的TCP转发,同时TCP流量又会被redsocks拦截翻墙;
其缺点是:
- 所有DNS解析都翻墙,很慢
- 国内域名在墙外解析可能得到网站的境外CDN IP,导致按IP访问时依旧被iptables拦截翻墙到境外。
因此,必须解决DNS的智能解析问题,也就是国内域名国内解,国外域名国外解。
本次技术方案的整体架构:dnsmasq -> 本机chinadns -> 本机unbound。
利用chinadns
这是一个非常简短的C语言项目,源码很简单:https://github.com/shadowsocks/ChinaDNS。
它是一个只支持UDP协议的DNS服务器,其工作原理是:
- 加载一份大陆IP网段的数据到内存。(数据源:https://www.apnic.net/ 组织,可以说非常精确)
- 配置1个国内的DNS服务器,配置1个国外的DNS服务器。(国内or国外的判断依据仍旧是上面的大陆IP网段文件)
- 同时向2个DNS服务器发起UDP请求,谁先返回就先对谁进行判断:
- 国内DNS返回了国内IP,那么直接使用这个IP作为客户端返回值。
- 其他情况则使用国外DNS返回的IP。
chinadns依赖了2个很重要的客观事实:
- 国内DNS服务器返回快,一旦解析到的是国内IP则可以立即返回客户端,因此解决了国内域名的解析加速。
- 国内DNS解析国外域名返回的污染IP通常还是一个国外IP(只不过是错误的),因此国内DNS解析到国外IP可以直接忽视,等待国外DNS返回正确国外IP即可。
利用dnsmasq
为啥不直接让chinadns对外提供DNS服务呢?因为chinadns程序太简单了,只是实现了DNS双发解析的特殊能力。
我一开始也没有用dnsmasq,然而发现安卓设备上的谷歌全家桶app都提示:”无法联网”,然而网页都可以打开,这让我非常纳闷。
经过搜索得知,谷歌app访问的域名是独立的,并且国内谷歌框架的域名还是.cn后缀的:
services.googleapis.cn
这个域名在国内DNS解析会得到一个国内的IP,并且是被污染过的,所以chinadns也是无法识别出这种case的。
因此,我需要最前面放一个支持高级策略配置的dns服务器,直接配置services.googleapis.cn的正确的IP进去,这样就免得走后续的chinadns解析流程了。
整体搭建流程
让dnsmasq监听53端口,chinadns监听在530端口,原先的unbound换到531端口。
代理关系是:dnsmasq –(udp)–> chinadns –(udp)–> unbound –(tcp)–> 8.8.8.8。
其中,chinadns配置国内dns为114.114.114.114,配置国外dns为127.0.0.1:531(也就是本机的unbound),这样就可以复用unbound的TCP翻墙解析和缓存能力,同时chinadns搞定国内域名高速解析的需求。
之所以127.0.0.1被chinadns视为国外dns,是因为chinadns判断dns服务器是国内还是国外也是基于大陆IP网段,而127.0.0.1不在大陆网段文件内,因此很巧妙的被算作了国外DNS服务器。
调整unbound配置
首先改一下/etc/unbound/unbound.conf,把监听地址修改为127.0.0.1:531:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# Unbound configuration file for Debian. # # See the unbound.conf(5) man page. # # See /usr/share/doc/unbound/examples/unbound.conf for a commented # reference config file. # # The following line includes additional configuration files from the # /etc/unbound/unbound.conf.d directory. include: "/etc/unbound/unbound.conf.d/*.conf" # 结果缓存 msg-cache-size: 100m rrset-cache-size: 100m # 结果缓存时间1小时 cache-min-ttl: 3600 # 在过期前自动刷新 prefetch: yes # 禁用ipv6 do-ip6: no # 监听在所有网卡的531端口, tcp/udp同时服务 interface: 127.0.0.1 port: 531 # 允许所有来源IP的请求, 默认只允许Localhost access-control: 0.0.0.0/0 allow # 采用tcp转发给上游8.8.8.8 tcp-upstream: yes forward-zone: name: "." forward-addr: 8.8.8.8@53 |
然后systemctl restart unbound重启一下即可。
部署chinadns
下载chinadns源码并编译安装:
1 2 3 4 5 |
git clone https://github.com/shadowsocks/ChinaDNS.git cd ChinaDNS sh autogen.sh ./configure make && make install |
然后执行如下命令从apnic组织下载一份最新的大陆IP段列表,生成chnroute.txt文件:
1 |
curl 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' | grep ipv4 | grep CN | awk -F\| '{ printf("%s/%d\n", $4, 32-log($5)/log(2)) }' > chnroute.txt |
然后把chinadns二进制以及上述chnroute.txt文件分别放到:
1 2 |
/usr/local/bin/chinadns /usr/local/etc/chnroute.txt |
然后创建systemd配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
root@aml:~/ChinaDNS# cat /etc/systemd/system/chinadns.service [Unit] # 服务描述 Description=chinadns # 要求必须执行网络 Requires=network-online.target # 在网络启动之后启动 After=network-online.target [Service] # 简单服务 Type=simple # 运行用户与用户组 User=root Group=root # 进程退出立即重启 Restart=always # 执行命令 ExecStart=/usr/local/bin/chinadns -p530 -c /usr/local/etc/chnroute.txt -s 114.114.114.114,127.0.0.1:531 # 进程工作目录 WorkingDirectory=/root [Install] # 在系统启动后加载UNIT WantedBy=multi-user.target |
然后注册并启动该service即可:
1 2 |
systemctl enable chinadns systemctl start chinadns |
部署dnsmasq
安装:
1 |
apt install dnsmasq |
配置/etc/dnsmasq.conf,确保如下配置相同:
1 2 3 |
no-resolv no-poll server=127.0.0.1#530 |
- 前2行用于禁止dnsmasq从/etc/resolv.conf中获取nameserver地址。
- 第3行默认用chinadns作为上游dns,但是对于特例则直接走unbound翻墙解析。
下载googlehosts解决google全家桶的DNS污染特例,否则很有可能安卓手机之类的打不开谷歌全家桶:
项目地址:https://github.com/googlehosts
只需要下载里面的dnsmasq.conf:https://github.com/googlehosts/hosts/blob/master/hosts-files/dnsmasq.conf。
里面按照dnsmasq的配置格式指定了谷歌全家桶的各种特例解析,我们只需要把它放到如下目录,dnsmasq就会自动加载生效了:
1 2 3 4 |
root@aml:~# ll /etc/dnsmasq.d/ total 920 -rw-r--r-- 1 root root 936878 Nov 14 09:45 dnsmasq.conf -rw-r--r-- 1 root root 211 Oct 10 2018 README |
然后启动dnsmasq:
1 2 |
systemctl enable dnsmasq systemctl restart dnsmasq |
验证
如果你已经按照我此前的博客将斐讯N1的DNS指向了自身的话,那么现在直接dig测试即可,都可以瞬间返回:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
root@aml:~/ChinaDNS# time dig baidu.com ; <<>> DiG 9.10.3-P4-Debian <<>> baidu.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30149 ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;baidu.com. IN A ;; ANSWER SECTION: baidu.com. 1323 IN A 39.156.69.79 baidu.com. 1323 IN A 220.181.38.148 ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Mon Nov 04 21:56:14 CST 2019 ;; MSG SIZE rcvd: 70 real 0m0.079s user 0m0.047s sys 0m0.031s |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
root@aml:~/ChinaDNS# time dig facebook.com ; <<>> DiG 9.10.3-P4-Debian <<>> facebook.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49657 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 512 ;; QUESTION SECTION: ;facebook.com. IN A ;; ANSWER SECTION: facebook.com. 34 IN A 74.86.142.55 ;; Query time: 2 msec ;; SERVER: 192.168.2.1#53(192.168.2.1) ;; WHEN: Mon Nov 04 21:56:31 CST 2019 ;; MSG SIZE rcvd: 57 real 0m1.083s user 0m0.057s sys 0m0.024s |
关于斐讯N1作为翻墙网关以及电视盒子的一系列教程到此为止,祝大家玩的愉快。
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~
