利用本地 dnsmasq 和 Cloudflare DoH 解决 DNS污染

这几天论坛上总是有人发帖询问有关 GitHub 访问困难的事情,期初我以为又是一次 DNS污染 ,但是深究之后发现是 GitHub 自己对亚洲地区的网络结构进行了调整,在亚洲区启用了 Akamai 的新加坡 CDN。其实这样对于整个亚洲用户来说是好事,但是对于神奇的中国用户来说,大概只能算是一次负优化了。不过令人庆幸的是,这次不是令人讨厌的 DNS污染 了。这次我就来分享一下我 个人目前 认为解决 DNS污染 的方案。

tl;dr

长话短说,其实这个方案的核心就是——对国内和国外的域名分开解析。国内的网站,使用 ISP 或者 各大公司提供的 DNS 来解析;国外的网站,使用 Cloudflare 提供的 DNS over HTTPS 来解析。这其中如何区分哪些域名是国内的,哪些域名是国外的?则需要用到一个开源项目 dnsmasq-china-list 。这个项目尽可能详细的整理了所有服务器在国内的网站,相当于是一个白名单,它的配置文件支持 dnsmasq/unbound/bind 这三种 DNS 服务器软件。

安装 cloudflared

cloudflared 的安装其实非常简单,在 Cloudflare 的开发者网站有着很详细的 描述,我这里也就不多说了。这个工具最主要的两个参数就是以 --proxy-dns 开头的参数,这几个参数指定了 cloudflared 监听的地址以及端口。安装完成后,可以搭配 supervisor 来保持后台常驻。

安装/配置 DNSMASQ

对于 Linux 以及 macOS 用户来说是个非常简单的事情,Linux 和 macOS 都有着相对不错的包管理器。以 macOS 举例,只需要 brew install dnsmasq 就可以了。然后是更改 dnsmasq 的配合文件,在 macOS 上,通过 Homebrew 安装的 dnsmasq 配置文件位于 /usr/local/etc/dnsmasq.conf 。这个文件里,我们需要更改的部分很少:

1
2
3
4
5
6
···省略默认配置文件内容,去掉下面这几行的注释,并且添加对应的参数
no-resolve # 不让 dnsmasq 以 /etc/resolve.conf 中的服务器为上游服务器
no-poll # 不让 dnsmasq 向 /etc/resolve.conf 中的服务器发起查询请求
server=ip:port # ip 是 cloudflared 监听的 IP,一般是本地 IP;port 是 cloudflared 监听的端口
conf-dir=/usr/local/etc/dnsmasq.d/,*.conf # dnsmasq-china-list 的配置文件将要存放在 dnsmasq.d 中,引入配置文件
···

安装 dnsmasq-china-list

按照 dnsmasq-china-list README 的描述,可以轻而易举的生成你所需要的配置文件,然后把生成的文件放到 dnsmasq.d 中。

这就是全部的步骤了,接下来,以 macOS 为例,只需要 sudo brew services start dnsmasq ,然后启动 supervisor 的 cloudflared 进程就可以了。当然,别忘了把你的 DNS 改成 127.0.0.1。

后话

为什么我觉得这是我目前最认可的方案?首先,我在之前折腾过很多的方法,比如 chinadns/neatdns 这两个方案。chinadns 最近因为 OpenDNS 的特殊端口查询失效,也就凉了;neatdns 对于 Linux 用户来说是十分友好的,毕竟只需要安装了 Docker 就可以了,非常方便;但是对于我这个内存有限,并且时常要开虚拟机的 Mac 用户来说,就有点奢侈了,开一个 Docker for Mac 少说也要 1G 内存。但是如果把 neatdns 部署到国内的云服务器上,由于云服务器是有多条线路的,所以并不能做到 CDN 友好,还是不尽如人意。

可能你会觉得 DNS Over HTTPS 这个方案效率会非常的低,毕竟 HTTPS 是一个很耗时的协议。但是不必担心, Cloudflare 的 DoH 是建立在 HTTP/2 的基础上的,他使用了长连接的方式,来让中间最为耗时的握手部分只进行一次,后面只需要发送接收数据就可以了。以我个人的体验,江苏电信,解析一个国外域名大概在 100ms 左右,效果还是不错的。