用 Raspberry Pi 架设加密 DNS 客户端

dig through DNS-over-HTTPS

Cloudflare 宣布使用 1.1.1.1 做为 DNS,而且强调隐私保护。因为 Cloudflare DNS 支持 DNS-over-TLS 和 DNS-over-HTTPS,这使得加密 DNS 成为了热门话题linux

由于操做系统每每不支持加密 DNS,因此要使用加密 DNS 必须使用一个加密 DNS 的客户端,而后这个客户端同时做为一个明文 DNS 服务器向操做系统提供正常的 DNS 服务。我能够选择在每一台我使用的设备上安装一个加密 DNS 客户端(对于 iOS 来讲则是 NetworkExtension),我也能够选择在家里假设一个加密 DNS 客户端而后把路由器 DNS 指向过去,以后家里全部设备的 DNS 都会跟着变。我选择了后者,由于这样作比较方便,也为我提供了一个折腾 Raspberry Pi 的借口——我须要把加密 DNS 客户端部署到 Raspberry Pi 上让它长期为家里的局域网提供 DNS 服务。git

(为何不用 OpenWRT 呢?由于我家里已经在用 Eero 来作路由器了,它能够经过 mesh Wi-Fi 来提供更好的覆盖。若是我要多买一个 OpenWRT 路由放在 Eero 前面,那我还不如买个 Raspberry Pi 来玩玩呢。)github

Raspberry Pi

我买了这个 Raspberry Pi 套装,由于它自带盒子和电源。电源不重要,我家已经有不少 USB 电源,可是我总不能一块电路板随便一放吧,因此必须买个盒子。而后我还买了张 64GB 的 microSD。由于我全部 microSD 都是 64GB 的,因此我只买 64GB 的方便有须要时随意替换。安全

收到 Raspberry Pi 以后,我就按照官方 NOOBS 的指引下载和准备安装。然而 NOOBS 复制到 SD 卡后不管如何 Raspberry Pi 都没法正常启动,只亮红灯没有视频输出。搜索以后发现绿灯不亮就是没有读取 SD 卡进行启动。我开头怀疑是我下载的 NOOBS 有问题,因而换成 NOOBS Lite 和 Raspbian,但都是不行。我也怀疑过是否是下载的 zip 数据有问题,但 sha256 checksum 正确。服务器

实在找不到问题了,我就开始搜索到底 Raspberry Pi 是如何进行引导的,发现它必须从 FAT 分区进行引导。Raspberry Pi 本身的官方文档教你们使用一个叫作 SD Association’s Formatting Tool 的软件来格式化 SD 卡,但这个软件在面对超过 32GB 的卡时就会傻傻地使用 exFAT 来进行格式化。其实使用 Mac 内置的 Disk Utility 不就好咯,就算是超过 32GB 的 SD 卡也能够选择格式化为 FAT。网络

把 SD 格为 FAT 后,全部问题都解决了。NOOBS 可以正常启动,接着 Raspbian 也可以顺利装上。Raspberry Pi 安装好以后我尝试启用 VNC 以便我用 Mac 远程控制,结果那上面装的 VNC 和 Mac 自带的 Screen Sharing 客户端不兼容,我只好降级到用 SSH,不过也能完成绝大多数操做了。架构

启用 SSH 后 Raspbian 会提醒你改默认密码,没有改的话记得改掉,不然太不安全了。由于 Raspbian 连 dig 这么基本的命令都没有,须要经过 apt-get 来安装,因此咱们须要先更新一下而后把 dig 装上:测试

sudo apt-get update
sudo apt-get install dnsutils

DNS-over-HTTPS

我基本上就是按照 Cloudflare 的 DNS-over-HTTPS 指引 来作的。一开始我以为 Raspbian 既然是 Debian 系的就下载了 Debian 的安装包,结果发现安装不上去。接着尝试用 Linuxbrew 来装 homebrew 的版本,结果装上后发现不能执行。看到「exec format error」而且搜索后才忽然明白到,Raspberry Pi 不是基于 x86/x64 架构的,而是基于 ARM 架构的。那到底 Raspberry Pi 是 32 位仍是 64 位的呢?理论上 Raspberry Pi 3 B+ 是 64 位的 CPU,但在 Raspbian 上执行 uname -a 的话会显示:网站

Linux raspberrypi 4.9.80-v7+ #1098 SMP Fri Mar 9 19:11:42 GMT 2018 armv7l GNU/Linux

因此其实不是 64 位的,若是要选正确的版本那必须选 32 位的 ARM。只要选择正确的版本,Cloudflared 和 Dnscrypt-Proxy 都是能够用的。我两个都装了,都能在 localhost:53 上跑起来,最后选择了 Dnscrypt-Proxy 是由于配置方便。(Dnscrypt-Proxy 有配置文件模板,改改就能够用了,不须要对着文档写一个新的。)加密

Dnscrypt-Proxy 的安装跟着官方指引作就能够了,选择 Linux 版本 来下载。记得下载 Linux ARM 的版本,不要用 Android 或者 ARM64 的版本。(尽管 Dnscrypt-Proxy 是能够安装在 Pi-Hole 上面的,但我不想安装 Pi-Hole 来过滤广告因此选择了非 Pi-Hole 的版本。)尽管官方指引叫你检查一下是否有别的 DNS 服务正在使用 53 端口,但新装的 Raspbian 应该是不会有任何服务占用 53 端口的因此这一步能够略过。

Dnscrypt-Proxy 下载和解压好以后就能够开始配置了。假设咱们已经在 Dnscrypt-Proxy 解压好的目录里:

cp example-dnscrypt-proxy.toml dnscrypt-proxy.toml
sudo ./dnscrypt-proxy

这时候 Dnscrypt-Proxy 应该可以跑起来,在 Raspberry Pi 上用 dig 验证一下就知道了:

dig +short @127.0.0.1 cloudflare.com AAAA

这个验证必须在 Raspberry Pi 上作,由于 Dnscrypt-Proxy 的默认配置只监听 localhost:53 端口,从另一台机器连上来 53 端口是不行的。若是 Dnscrypt-Proxy 正常工做了,咱们就能够开始改配置了。打开 dnscrypt-proxy.toml,而后把 server_nameslisten_addresses 改掉。(在 SSH 上面,用 nanovi 均可以编辑 dnscrypt-proxy.toml。)

首先找到 server_names,把前面注释这一行的 # 删掉,而后把后面的内容改成你想要的服务。由于 Cloudflare 和 Google 都支持 DNS-over-HTTPS,并且都是可靠的大公司,因此我在这两家之间选。由于 Google 不强调隐私,有可能记录数据,因此我只用 Cloudflare 的,按照 Cloudflare 的文档把这一行改成这样子:

server_names = ['cloudflare', 'cloudflare-ipv6']

接着找到 listen_addresses,你会发现它只监听 IPv4 和 IPv6 的 localhost,因此其余机器不能用 Raspberry Pi 来作 DNS。这时候你要想办法把 Raspberry Pi 的 IP 绑上去。个人作法是这样子的:由于我家里路由器的 IP 是 192.168.0.1,而后 DHCP 范围是 192.168.0.10–192.168.0.199,因此 192.168.0.2–192.168.0.9 是不会被动态分配出去的。我把 Raspberry Pi 的有线网 IP 写死为 192.168.0.2,而后把它加到监听地址端口列表上:

listen_addresses = ['127.0.0.1:53', '[::1]:53', '192.168.0.2:53']

搞掂以后,能够再启动一下 Dnscrypt-Proxy:

sudo ./dnscrypt-proxy

而后从另一台机器使用 dig 测试一下:

dig +short @192.168.0.2 cloudflare.com AAAA

若是没有问题的话,就能够把 Dnscrypt-Proxy 当装系统服务启动了:

sudo ./dnscrypt-proxy -service install
sudo ./dnscrypt-proxy -service start
sudo systemctl enable dnscrypt-proxy

以后登陆到路由器,把路由器的 DNS 改成 192.168.0.2 就能够了,家里全部设备的 DNS 都会通过 Raspberry Pi 上的 Dnscrypt-Proxy 走 DNS-over-HTTPS 链接 Cloudflare 服务器。尽管 Dnscrypt-Proxy 的官方指引还说要把 Linux 上的 DNS 客户端指向 localhost,但由于我暂时不在 Raspberry Pi 上作别的事情因此不在乎 Raspberry Pi 自己发出的 DNS 请求是否加密。只要它做为 DNS 服务器服务好我家里的其余设备就行。

已知问题

上述作法是有一些已知问题的。首先,若是咱们请求使用 SNI 的 HTTPS 服务的话,咱们仍是会明文传输域名的,就算 DNS 加密了仍是会存在域名泄漏的状况。若是多个不一样证书的 HTTPS 域名要在一个 IP 上共处,那必须使用 SNI 不然 SSL 握手时没法决定用哪一个证书的密钥。所以 SNI 常见于跑在云平台上的服务,由于云平台每每在多个服务之间共享 IP,但每个服务来自不一样的客户有不一样的证书。对于大型网站来讲这不常见,由于不管一个大型网站旗下有多少域名,它均可以选择把全部域名放在同一个证书里面。

其次,我没有作 IPv6 的配置,只让 Dnscrypt-Proxy 绑定了一个 IPv4 地址。这时候若是 IPv6 分配了不同的 DNS,那使用 IPv6 DNS 查询时仍是会走明文的。若是你所处的网络彻底不使用 IPv6,那是没问题的。我知道 Comcast 是会分配 IPv6 地址和 IPv6 DNS 的,因此若是不在路由器上设置 IPv6 DNS(或者是不能设置)的话,那 IPv6 DNS 就有多是 Comcast 分配下来的,也就是明文 DNS。(其余 ISP 也同样。)

最后,若是你喜欢个人文章,欢迎经过邮件订阅个人博客。

相关文章
相关标签/搜索