需要两台服务器。
一台位于离我们近的机房,这个机房可以是位于有域名服务器缓存污染的防火墙内。(当然如果不是有污染的环境,也没有必要写这篇文章。)
一台位于没有域名服务器缓存污染的防火墙外。
我们把这两台服务器分别取名为 ns.example.net 和 ss-server.example.net。
不能直接使用防火墙外的域名服务器作为客户端的域名服务器,是因为当域名解析数据包经过防火墙时会被投毒。
使用 dnsmasq 来分流不同的域名解析请求,是因为如果全部定向到防火墙外的域名服务器,对某些使用 CDN 的网站,特别是防火墙内的网站将会被解析得到距离我们较远的服务器的 IP。
在 ns.example.net 上安装和运行 dnsmasq 和 ss-tunnel(包含在 shadowsocks-libev),在 ss-server.example.net 上安装和运行 ss-server(包含在 shadowsocks-libev)。
部署图如下:

版本信息
本文测试环境:
- Ubuntu 16.04.2 LTS (GNU/Linux 4.4.0-72-generic x86_64)
- shadowsocks-libev 2.6.3
ss-server.example.net 的安装
见:安装 shadowsocks-libev 服务器端。
ns.example.net 的安装
dnsmasq
轻量级 DNS 转发器、DHCP 和 TFTP 服务器。我们利用它的 DNS 转发功能。
安装
apt install dnsmasq
配置
确保文件 /etc/dnsmasq.conf 包含了如下代码,它通常位于最后一行:
# Include all files in a directory which end in .conf
conf-dir=/etc/dnsmasq.d/,*.conf
gfwlist2dnsmasq.sh
创建一个脚本文件 /usr/local/bin/gfwlist2dnsmasq.sh (chmod +x),内容如下:
#!/bin/sh
dnsmasqconf="/etc/dnsmasq.d/gfwlist.conf"
tmpconf="/tmp/gfwlist.conf.`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1`"
curl -s -o /tmp/gfwlist.txt \
https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt \
&& base64 -d /tmp/gfwlist.txt \
| awk -f $(cd "$(dirname "$0")"; pwd)/gfwlist2domainlist.awk \
- /usr/local/etc/user_rule.txt \
| grep -F -v -f "/usr/local/etc/skip_domain.txt" \
| awk -f $(cd "$(dirname "$0")"; pwd)/domainlist2dnsmasq.awk \
-v "noipset=noipset" \
- \
> "${tmpconf}" 2>/dev/null
diff -I '^#.*' "${dnsmasqconf}" "${tmpconf}" || \
(cp "${tmpconf}" "${dnsmasqconf}" && service dnsmasq restart)
rm "${tmpconf}"
该脚本会生成 dnsmasq 配置文件 /etc/dnsmasq.d/gfwlist.conf 让 dnsmasq 将 gfwlist 中的域名的解析转发到 127.0.0.1:5353 端口。
gfwlist2domainlist.awk 和 domainlist2dnsmasq.awk 可以从 gfwlist2dnsmasq.awk 获得。
/usr/local/etc/user_rule.txt 可以用来存放额外的想交由 127.0.0.1:5353 端口解析的域名列表,
/usr/local/etc/skip_domain.txt 可以用来存放在 gfwlist 列表中,但是不想交由 127.0.0.1:5353 端口解析的域名。
在 /etc/rc.local 的 exit 0 之前添加,使其在开机时运行一次:
/usr/local/bin/gfwlist2dnsmasq.sh
由于 gfwlist 是不断更新的,所以可以在 /etc/crontab 中添加,以便每天早上4点20分更新一次:
20 4 * * * root /usr/local/bin/gfwlist2dnsmasq.sh
ss-tunnel
安装
同:安装 shadowsocks-libev 服务器端。
启动
在 /etc/rc.local 的 exit 0 之前添加如下代码,以便开机时自动运行一个监听在 127.0.0.1:5353 端口,请求会通过隧道转到 8.8.8.8:53 上。
/usr/local/bin/ss-tunnel -c /usr/local/etc/shadowsocks-libev/config.json -l 5353 -L 8.8.8.8:53 -u > /dev/null 2>&1 &
测试
在 ns.example.net 上可以通过如下命令来测试 ss-tunnel 创建的隧道是否正常工作:
root@ns:~# nslookup
> server 127.0.0.1
Default server: 127.0.0.1
Address: 127.0.0.1#53
> set port=5353
> www.google.com
Server: 127.0.0.1
Address: 127.0.0.1#5353
Non-authoritative answer:
Name: www.google.com
Address: 172.217.25.4
> exit
#dnsmasq, #gfwlist, #gfwlist2dnsmasq-awk, #shadowsocks-libev, #ss-server, #ss-tunnel, #ubuntu