搭建智能的 DNS 服务器
众所周知,DNS 污染是较常遭遇的攻击手段之一。
ChinaDNS
以 ChinaDNS 为代表,目前自动解决这个问题的思路是多 DNS 对比:同时查询较快的本地 DNS(通常为 ISP 的 DNS)和较慢的可信 DNS(通常为经过加密传输的 Google 或 OpenDNS 等),对比返回的结果,若有差异,说明本地 DNS 返回的结果大概率被污染。
以此为基础,配合国内网站和国内 CDN 的白名单(直接使用本地 DNS)、被封锁网站的黑名单(直接使用可信 DNS)基本上达到了兼顾功能(反污染)和效率(国内跳过)的效果。
这个思路的缺点也很明显:
- 如果使用白名单,截至今天(2018.01.26),白名单 accelerated-domains.china.conf 已经包含了 39881 条记录,这对很多路由器上的 dnsmasq 造成了不小的压力。而实际上,在这么一份大而全、更新飞快的列表中,大部分人使用的只是很小一部分。
- 如果不使用白名单,仅使用黑名单,对于非重度网络使用者,其实是一个可以接受的方案。
- 如果不使用白名单也不使用黑名单,结果更糟糕。这种情况下一般会启用类似
GEO IP: CN => DIRECT; FINAL => PROXY
的自动规则,由于目前绝大部分大中型网站均在多个区域部署 CDN 进行加速,对这些网站查询可信 DNS 有非常大的概率会返回一个对本地来说负优化的结果(为远端 VPS 或者代理优化,解析到一个对本地较慢的 CDN 节点),甚至网站会根据 GEO IP 的结果强制跳转(如淘宝海外站)。 - 路由器版本的 ChinaDNS 有时会出一些莫名其妙的问题导致不能正常解析,非常……影响心情。
改进思路
为了弥补这些缺点,我们提出以下需求:
- 只查询一个本地 DNS,或者,一个快的远端 可信 DNS(考虑到大部分地区到
114.114.114.114
和119.29.29.29
的延迟,可以认为 30-45 左右的延迟是可以接受的)。 - 不使用庞大的白名单和黑名单。
- 确保解析到的结果为本地优化。
针对后两点,技术上其实已经有了解决方案,那就是 RFC 7871 (Client Subnet in DNS Queries, aka edns-client-subnet, ECS),RFC 文档见此,还可参考 Google 的帮助。
ECS 允许 DNS 解析的请求放附带一个网络地址,要求 DNS 服务器做出针对这个地址优化的解析响应。
但是,ECS 目前的实施还是非常不接地气的。国内大厂多有成熟的智能解析方案,国外大厂更由于隐私等诸多问题对此动力不足。即便是目前对 ECS 支持的最好的 Google Public DNS,发过去的请求包也只有一半可以得到正确的 ECS 响应。
因此 Google 提供了一种迂回的解决方案:DNS-over-HTTPS(文档)。不使用不能稳定得到 ECS 响应的 DNS 协议,通过 HTTPS 协议可以稳定获取 ECS 响应。
我们可以从这个方案中得到一个新思路,将 DNS 请求转化为 HTTPS 请求,再将收到的响应转化为 DNS 响应返回(事实上会小幅度增加解析耗时)。
部署
实现这个思路有两种部署方案:
本地部署
下载 google-https-dns (Go 语言,支持包括 ARM 在内的多种 CPU),参照作者的说明安装在本地路由器或其他设备上为局域网提供服务,通过前置的代理(支持 socks 或影梭)访问 Google 的 DNS over HTTPS。
远端部署
推荐以容器的形式部署。
远端部署的优势是可以共享自建 DNS。
这种方式将 google-https-dns 作为后端提供服务,同时在前端放置一个支持 ECS 缓存的 DNS 代理(推荐使用 Unbound)以获得更高性能。
部署方式如下,也可以参考 这份 Gist:
- 创建 google-https-dns 的容器:
docker run -d --name dns-google --log-opt max-size=1m --restart=unless-stopped tarot13/google-https-dns
- 准备 Unbound 的配置文件
unbound.conf
(见后文)和 Root Hintsroot.hints
(可以从ftp://FTP.INTERNIC.NET/domain/named.cache
下载) - 创建 Unbound 的容器:
docker run -d --name dns-unbound -v $HOME/unbound:/etc/unbound -p 53:53/tcp -p 53:53/udp --link=dns-google:dns-google --log-opt max-size=1m --restart=unless-stopped tarot13/unbound
供参考的 unbound.conf
配置:
需要注意的是其中的两项:
- ECS 缓存(
subnetcache
)必须在模块配置中启用:module-config: "subnetcache iterator"
- 最好指定允许发送 ECS 信息的上游 DNS 网段(即 google-https-dns 的地址):
send-client-subnet: 172.16.0.0/12
server:
username: "root"
interface: 0.0.0.0
verbosity: 1
do-daemonize: no
access-control: 0.0.0.0/0 allow
root-hints: "/etc/unbound/root.hints" # Root Hints: ftp://FTP.INTERNIC.NET/domain/named.cache
auto-trust-anchor-file: "/etc/unbound/root.key" # Auto generated
do-ip4: yes
do-ip6: no
do-udp: yes
do-tcp: yes
hide-identity: yes
hide-version: yes
harden-glue: yes
use-caps-for-id: yes
cache-max-ttl: 3600
prefetch: yes
num-threads: 4
msg-cache-size: 64m
rrset-cache-size: 128m
module-config: "subnetcache iterator"
unwanted-reply-threshold: 10000000
do-not-query-localhost: no
send-client-subnet: 172.16.0.0/12
minimal-responses: yes
forward-zone:
name: "."
forward-host: dns-google
使用
对于本地部署,局域网内的其他设备可以直接使用。
对于远端部署,可以选择通过非 53 端口转发,或者通过任意方法加密传输。
测试记录:
- dig with subnet (web), mainland IPs:
; <<>> DiG 9.10.6 <<>> @127.0.0.1 www.taobao.com +subnet=114.114.114.114
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9065
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 114.114.114.114/32/24
;; QUESTION SECTION:
;www.taobao.com. IN A
;; ANSWER SECTION:
www.taobao.com. 416 IN CNAME www.taobao.com.danuoyi.tbcache.com.
www.taobao.com.danuoyi.tbcache.com. 162 IN A 58.215.145.110
www.taobao.com.danuoyi.tbcache.com. 162 IN A 58.218.215.155
www.taobao.com.danuoyi.tbcache.com. 162 IN A 180.96.11.188
www.taobao.com.danuoyi.tbcache.com. 162 IN A 222.186.49.177
;; Query time: 76 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Wed Jan 03 17:29:12 China Standard Time 2018
;; MSG SIZE rcvd: 317
- dig without subnet, HK IP:
; <<>> DiG 9.10.6 <<>> @127.0.0.1 www.taobao.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39972
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;www.taobao.com. IN A
;; ANSWER SECTION:
www.taobao.com. 392 IN CNAME www.taobao.com.danuoyi.tbcache.com.
www.taobao.com.danuoyi.tbcache.com. 179 IN A 205.204.104.227
;; Query time: 92 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Wed Jan 03 17:29:18 China Standard Time 2018
;; MSG SIZE rcvd: 144