现代网站越加重视网络安全和连接加密,尤其是在ECH不断推广受浏览器和网页服务器支持的现在和未来,防范DNS和SNI被监控和污染阻断是实现自由安全上网的一大步。
最近学习了HTTPS连接建立的过程(早就该学了只是太懒一直拖着没研究),随后发现连接建立过程中Client Hello默认不加密,这就给别有用心之人留下了利用SNI监控用户和阻断连接的把柄。
早些年开始推广ESNI,也就是加密Client Hello中的域名。近三年发展到ECH加密Client Hello中所有信息,很快就替代了ESNI作为主流浏览器默认启用的功能。再搭配HSTS限制客户端仅HTTPS连接,加密通信才算完全。唯一的遗憾就是无法加密也不可能加密连接的IP地址和端口,只能代理绕过了。
想要使用ECH,DoH/DoT是关键。ECH就是靠加密DNS提供信息指向正确的网站。目前主流的操作系统,包括Linux发行版、Android、Windows、MacOS都已支持启用和DoT相关配置。本文主要介绍如何在Fedora上配置Cloudflare Zero Trust提供的免费私人DoT服务。这个服务的域名、IP和443、853端口目前在部分地区没有被墙,之前折腾OpenWrt时也用它来提供DoH。
以下内容大部分翻译自 Configure DoT on systemd-resolved 略有增删。
Cloudflare Zero Trust添加默认DoT
- 进入 Zero Truest —— Gateway —— DNS位置。
- 点击 添加位置。
- 在 选择DNS终结点 处勾选 基于TLS的DNS(DoT),其他三个选项可选,建议全部勾选。
- 在 默认DNS位置 处勾选 启用EDNS客户端子网。如果这是你创建的唯一DNS配置,就把 设置为默认DNS位置 勾上。
- 点击 继续,如果想进一步限制哪些IP地址可用这个DNS配置,就勾选 DoT终结点筛选 后添加对应的IP,否则就忽略。
- 没问题就点击 完成。
- 复制 基于TLS的DNS 中的域名,比如
abc123.cloudflare-gateway.com,接下来就使用这个域名为操作系统配置全局DoT。
配置 systemd-resolved
首先你可以使用下面这个小脚本创建和更新DNS配置。注意把【abc123】换成你自己创建的真实子域名。
1 |
|
这个脚本查询 abc123.cloudflare-gateway.com 的所有IP地址,并在末尾附加 #abc123.cloudflare-gateway.com,以告知 systemd-resolved 使用的 SNI 名称。由于 DoT 还依赖CA证书,它将从远程证书中获取名称,并与#之后提供的名称进行验证。如果不正确,DoT将因不受信任而连接失败。如果未指定SNI名称,DoT将回退到它所拥有的信息——IP地址——以验证证书的有效性。
脚本的输出是 systemd-resolved 的配置,其中包含通过 DNS= 和 FallbackDNS= 设置的 DNS 服务器,使用 Domains=~. 将它们作为默认域处理器。使用 DNSOverTLS=yes,启用DoT;使用 DNSSEC=yes 强制 DNSSEC。这样确保全局默认设置安全,且实际使用 abc123.cloudflare-gateway.com。
如果遇到查询某些域名报错的情况,比如查询
juejin.cngitee.com有以下报错信息:
1 gitee.com: resolve call failed: DNSSEC validation failed: no-signature可以设置
DNSSEC=no禁用 DNS 签名。由于 TLS 协议会对数据进行加密,所以在 Cloudflare DNS 服务器与客户端之间的通信依然加密,只不过DNS服务器进行递归查询时有被篡改的风险。参考
下一步则利用上面创建的脚本配置 systemd-resolved:
1 | sudo mkdir -p /etc/systemd/resolved.conf.d/ |
路径 /etc/systemd/resolved.conf.d/ 可用于放置自定义配置,便于发行版更轻松地维护默认配置。使用 sudo tee … 可以将配置放入名为 cloudflare-private-dot.conf 的文件中,并在终端中获得相同的输出以进行验证。最后重启 systemd-resolved。如果 DNS 配置出现问题,只需删除该文件并重启守护进程即可恢复到默认设置。
配置 NetworkManager
要真正利用这个全局配置,必须禁用 NetworkManager 提供的每个接口配置。你可以添加另一个补充配置,但这次是针对 NetworkManager 的:
1 | sudo mkdir -p /etc/NetworkManager/conf.d/ |
dns=none 告诉 NetworkManager 不要修改 /etc/resolv.conf,因为你仍然希望在那里使用 systemd-resolved 服务。systemd-resolved=false 告诉 NetworkManager 停止与 systemd-resolved 通信,因此不再将任何自动获取自接口的 DNS 设置传递给它。
这种配置的缺点是,NetworkManager 中的 DNS 设置现在变得无效。但作为回报,所有接口都将使用全局配置,该配置指向 abc123.cloudflare-gateway.com。
想要检查结果是否正确,可以执行命令 resolvectl。
1 | Global |
在这些输出中你会注意到 enp0s25 仍然有自己的 DNS 服务器配置。执行 sudo resolvectl dns enp0s25 "" 移除它。
之后看起来像这样(不同版本的系统结果有所区别):
1 | Global |
这之后,如果你想,可以用 Wireshark 抓包验证结果。现在执行 resolvectl query cloudflare.com 你应该只会看见加密流量:
1 | cloudflare.com: 2606:4700::6810:84e5 -- link: enp0s25 |
结论
总的来说,到目前为止,这个方法效果非常好,不再有未加密的 DNS 网络流量。这不仅可以防止基于网络的 DNS 攻击,还能减少信息泄露。这是实现更安全网络通信的重要一步。DNS 几乎是互联网上所有应用的基础,因此即使你甚至没有注意到差异,在最好的情况下,它也有助于为未来更安全的互联网做好准备。请注意,攻击者或网络提供商仍然可以通过收集 SNI 头信息来跟踪您访问的网站,除非客户端和服务器支持ECH。
值得一提的是,这种设置在使用如 Docker 和 Podman 等容器技术时并不能完美运行,因为在使用网络隔离时,容器将无法联系 systemd-resolved。不过,这可以通过提供 --dns 参数或在配置中设置特定的常规 DNS 服务器来解决。当然,这意味着容器无法从安全 DNS 中受益。