MacOS 下使用 DNSmasq 和 DNScrypt 防止污染

Intro

放假回家之后,感觉家里的网络状况实在令人堪忧。好在我还是可以科学上网,直到前几天突然连不上了github,which means 无法用 ssh 连上 github。无法用git push连接远程仓库,也无法ping通github。使用nslookup命令查询github.com地址,得到的是错误的地址。因此怀疑是 DNS 污染的问题。

不过急着想 push 上去,为了连上远程仓库,找到了两个解决方法:

  • 使用https连接远程仓库:

    1
    2
    3
    4
    # 首先移除ssh连接的远程仓库
    git remote remove origin
    # 再连上https
    git remote add origin https://github.com/<user_name>/<repo_name>
  • ssh via proxy
    使用 ssh 自带的Proxycommand就可以使用http代理连接 ssh
    首先使用homebrew安装corkscrew

    1
    brew install corkscrew

    然后配置 ssh

    1
    vim ~/.ssh/config

    在配置中写入

    1
    2
    Host *
    ProxyCommand corkscrew 127.0.0.1 1079 %h %p
后面的代理地址和端口根据自己情况修改即可。

防止DNS污染

接着就进入正文了,如何防止 DNS 污染。本来我对这些是没有’洁癖’的,我的科学上网需求也仅仅是 Google 和 Youtube 。然而这次 ISP 做得实在过分。于是下定决心清理一下。

DNSmasq

DNSmasq是一个小巧且方便地用于配置 DNS 和 DHCP 的工具,适用于小型网络,它提供了 DNS 功能和可选择的 DHCP 功能。使用它主要是为了让国内的网址使用国内的 DNS 解析,这样会使速度快很多。目前还不需要它的 DHCP 功能,不过默认也是关闭的,不需要担心。

安装和配置

在 mac 下的安装很方便

1
brew install dnsmasq

接着修改配置文件,其实只要打开几个注释,设定一下监听端口即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 不读取有关解析的配置文件,默认使用/etc/revolve.conf中的上游服务器地址进行解析
# 这里我们把地址直接写在配置文件里,所以不需要这个了
no-resolv
# 不检查有关解析的配置文件更新(原因同上)
no-poll
# 配置文件路径,加载dnsmasq-china-list的那两个配置文件
conf-dir=/usr/local/etc/dnsmasq.d
# 附加Hosts文件,可有可无,我觉得以后可能还是需要用到Hosts,就加入了这一行
addn-hosts=/usr/local/etc/dnsmasq.hosts
# 上游服务器设置成DNSCrypt
server=127.0.0.1#5353
# 缓存大小,默认是150,调大点应该没坏处
cache-size=1500

dnsmasq-china-list的配置文件在这个项目里,它维护了一份中国地区大部分网站的地址。这个列表来可以帮助DNSMasq判断应该把DNS请求发向哪里。

可以直接运行这个命令来下载到配置文件夹:

1
wget -4 --no-check-certificate -O /usr/local/etc/dnsmasq.d/accelerated-domains.china.conf https://raw.githubusercontent.com/felixonmars/dnsmasq-china-list/master/accelerated-domains.china.conf

这样,DNSmasq 就配置完成了。

DNScrypt

DNScrypt是 OpenDNS 出品的一个可以加密 DNS 请求的工具,让你的 DNS 请求能够像使用 ssl 一样加密地请求服务器,这样便从根本上杜绝了 DNS 污染问题。

安装和配置

同样使用 homebrew安装:

1
brew install dnscrypt-proxy

然后修改配置文件:

1
vim /usr/local/etc/dnscrypt-proxy.conf

这个配置文件改起来很简单,首先(跃入眼帘的是),ProviderName这一项。

OpenDNS 已经为我们准备好了公共 DNS,我们可以选择一个ping值比较小的,提供的 DNS 列表在:

1
cd /usr/local/opt/dnscrypt-proxy/share/dnscrypt-proxy

然后打开dnscrypt-resolvers.csv,选择一个 DNS server 然后将第一列的名字属性填到配置文件处即可。

接着需要修改的就是配置文件中的 LocalAddress,根据在 DNSmasq 中配置的 server端口,改成相同的即可。比如在这个例子中,应该改为:

1
LocalAddress 127.0.0.1:5353

另外,你或许需要将这一行修改为 yes

1
Daemonize yes

运行 DNSmasq 和 DNScrypt

配置结束之后就可以愉快地使用了。

1
2
sudo brew services start dnscrypt-proxy
sudo brew services start dnsmasq

记得一定要加上sudo!之前改配置之后没有加sudo就 start stop 出现了各种配置没用的情况。

还有最后一步就完成了:

在网络的高级选项中将系统的 DNS 地址改成 127.0.0.1。当然,你也可以使用命令行直接设置:

1
sudo networksetup -setdnsservers "Wi-Fi" 127.0.0.1

如果你不放心,可以先看一下DNSmasq 和 DNScrypt有没有在工作:

1
2
3
4
# 查看 dnsmasq
sudo lsof -Pni UDP:53
# 查看 dnscrypt
sudo lsof -Pni UDP:5353

自建 DNS 服务器

不知道为什么,我在本地搭建好环境,使用远程DNS之后,还是会出现被污染的情况。我猜测可能是因为 OpenDNS 的目标太大了,提供的 DNS 服务器或许会成为 GFW 的目标。当然也有一个可能,是我当时没用 sudo 运行导致了一些奇怪的问题。不过当时实在烦躁的不行,因此直接在 VPS 上自建了一个。

DNScrypt-wrapper —— DNScrypt的服务端

虽然 dnscrypt-proxy 是官方开源的,但是 server 端并没有开源出来,好在有大神 Cofyc 参照 client 写了一个开源的版的 server , dnscrypt-wrapper

所以我们只需要把它部署在 VPS 上,然后本地的 DNScrypt 配置文件改成连到自己的 VPS 上即可。

DNScrypt-wrapper 的安装

这个安装和之前比起来要麻烦一些,因为它需要依赖 libsodiumlibevent2。我的 VPS 的环境是 Ubuntu16.04。以下是我安装的一些包:(其实按照报错来按需安装即可)

1
2
3
4
5
apt-get update && apt-get upgrade
apt-get install libevent-dev
apt-get install build-essential
sudo apt-get install autoconf
apt-get install libsodium*

安装 DNScrypt-wrapper:

1
2
3
4
5
6
7
8
9
10
git clone --recursive git://github.com/Cofyc/dnscrypt-wrapper.git
cd dnscrypt-wrapper
make configure
./configure
make
make install

# 安装完成会出现如下提示:
install -d -m 755 ‘/usr/local/bin’
install -p dnscrypt-wrapper ‘/usr/local/bin’

DNScrypt-wrapper 的使用

新建一个文件夹用于保存证书和密钥对,同时还需要产生一个临时的密钥来运行。

1
2
3
4
5
6
7
cd ~
mkdir dnskey
# 新建一个目录来存放证书
cd dnskey
dnscrypt-wrapper --gen-provider-keypair \
--provider-name=2.dnscrypt-cert.<yourdomain> --ext-address=<external server ip>
# 生成提供商密钥对

<yourdomain> 就是可以随意取了,比如 2.dnscrypt-cert.ex.com,然后 <external server ip> 填 VPS 的 IP 地址。

接着就会产生一个指纹信息,这个信息就是客户端配置时候需要的 provider_public_key

然后,我们使用命令生成有时限的加密密钥对以及生成预签名证书:

1
2
dnscrypt-wrapper --gen-crypt-keypair --crypt-secretkey-file=1.key
dnscrypt-wrapper --gen-cert-file --crypt-secretkey-file=1.key --provider-cert-file=1.cert

使用命令来运行 dnscrypt-wrapper,用“-VV”来显示比较详细的 debug 信息:

1
2
3
dnscrypt-wrapper --resolver-address=8.8.8.8:53 --listen-address=<external server ip> \
--provider-name=2.dnscrypt-cert.ex.com \
--crypt-secretkey-file=1.key --provider-cert-file=1.cert

我在这里选择是 Google 的 公共 DNS,你可以使用其它的。使用 -d 即可在后台运行。

修改本地 DNScrypt 的配置

打开配置文件,然后将 ResolverName 一栏注释掉,在配置文件中加入:

1
2
3
ProviderName    2.dnscrypt-cert.ex.com
ProviderKey <your_key>
ResolverAddress <your_add>

修改完之后重启 DNScrypt 即可。

1
sudo brew services restart dnscrypt-proxy

新的变化

在学校里突然发现也存在DNS污染的问题,于是重新启用 DNScrypt,但是用 homebrew 更新了一下,然后发现配置文件和以前不太一样了,server端也有了更方便的配置方法。因此也做了一下记录。

DNScrypt 2

新的文档在这里

Server端

Server端已经方便了非常多,文档也讲的相当详细。只要在VPS上安装 docker,然后运行以下命令:

1
2
docker run --name=dnscrypt-server -p 443:443/udp -p 443:443/tcp --net=host \
jedisct1/dnscrypt-server init -N example.com -E 51.15.38.62:443

不要忘了将上面命令中的ip改为自己的VPS ip地址。还记得要记录运行之后打印出来的 stamp,在配置客户端的时候需要用到。

然后便可以运行以下命令使得 server 端运行并开机自启动。

1
2
docker start dnscrypt-server
docker update --restart=unless-stopped dnscrypt-server

客户端

用 homebrew 安装的话,配置文件的路径是/usr/local/etc,打开dnscrypt-proxy.toml,与之前相同,将端口改为 dnsmasq 的监听端口,可以把 ipv6 的地址删掉(一般不会用到,可以按需添加。

接着将拉到文件的最底部,修改[static]字段,名字可以随便取,stamp 为之前在 server 端得到的 stamp。

1
2
[static.'your_name']
stamp = 'your_stamp'

之后将最上面的server_names字段里的值改为你自己刚刚取的名字。最后重启一下服务就 ok 了。

参考链接