使用 CoreDNS 反向代理 CloudFlare DoH

avatar
2021-10-23T14:15:48+08:00

DNS 是互联网的重要基础服务之一,但在近几十年被发现一些安全和隐私层面的设计缺陷,而 DoTDoH 的出现就是为了解决这些问题。

DoT 与 DoH 的区别

从 DoT(DNS over TLS)和 DoH(DNS over HTTPS)的名字中不难看出它们之间最大的区别:分别基于 TLSHTTPS

虽然 HTTPS 基于 TLS,但 HTTPS 是 TLS 与 HTTP 相加而成的产物,所以 DoT 和 DoH 并不直接兼容。并且,在它们各自的 RFC 文档中定义的默认端口也不相同,DoT 使用 853 端口,而 DoH 使用与 HTTPS 服务相同的 443 端口。

DoH 的优势

从隐私的角度来讲,当一个请求的目标端口是 853 时,ISP 不难看出你是在访问一个 DoT 服务器,而使用 DoH 却能够与访问普通网页的行为产生一定程度的混淆。

所以,我选择使用 DoH。

CloudFlare DNS

CloudFlare DNS 1.1.1.1 是 CloudFlare 与 APNIC 合作推出的公共 DNS,它的优点是查询速度快和安全,以及尊重用户的隐私,并且提供 DoH 和 DoT。

虽然在国内也有许多支持 DoH 和 DoT 的公共 DNS,例如阿里 DNS 223.5.5.5,但其解析结果仍然存在 DNS 污染的情况。

在中国大陆内提供公共递归解析 DNS 需要编码和规程转换业务(DNS)许可证。

如果直接在国内使用 CloudFlare DNS,可能会受到伪造报文的干扰或 TCP 重置,因此需要通过部署反向代理服务来转发 DNS 请求。

如果只需要反向代理 DoH,那么 Nginx 就可以做到。但我想在提供 DoH 的同时也提供 53 端口的 DNS 服务,所以需要用到 CoreDNS。

CoreDNS

CoreDNS 是一款使用 Go 编写的开源 DNS 服务端,它还支持官方或自定义插件,也是 Kubernetes 的服务发现组件之一。

下载 CoreDNS

GitHub Releases 下载最新版本的 CoreDNS。

编写 CoreDNS 配置

CoreDNS 会在启动它时所处的路径下自动查找名称为 Corefile 的配置文件。如果需要指定配置文件路径,可以用 -conf 参数。

首先,需要配置一个 443 端口的 DoH 服务,这一步的前提是需要拥有 TLS 证书。如果没有,可以使用 acme.sh 自行选择一个 CA 机构进行签发。

在成功签发 TLS 证书后,新建 Corefile 文件,启用 CoreDNS 的 tls 插件并以 tls CERT KEY [CA] 的格式编写配置。

https://. {
    tls /etc/letsencrypt/live/kallydev.com/fullchain.pem /etc/letsencrypt/live/kallydev.com/privkey.pem /etc/letsencrypt/live/kallydev.com/chain.pem
}

执行 coredns -conf Corefile,可以看到 CoreDNS 在 443 端口正常运行。

https://.:443
CoreDNS-1.8.6
darwin/amd64, go1.17.1,

最后,启用 forwardlog 插件,从 tls://1.1.1.1 转发 DNS 请求并记录日志。

https://. {
    tls /etc/letsencrypt/live/kallydev.com/fullchain.pem /etc/letsencrypt/live/kallydev.com/privkey.pem /etc/letsencrypt/live/kallydev.com/chain.pem
    forward . tls://1.1.1.1
    log
}

转发本地 53 端口

这部分仅作为编写 CoreDNS 配置的扩展讲解内容,实际作用不大。与 DoH 的区别在于去掉 https:// 协议以及 tls 配置。

. {
    bind 127.0.0.1
    forward . 1.1.1.1
    log
}

CoreDNS 可以同时提供 DoH 和常规 DNS,只需要将它们的配置合并在一起。

. {
    bind 127.0.0.1
    forward . 1.1.1.1
    log
}

https://. {
    tls /etc/letsencrypt/live/kallydev.com/fullchain.pem /etc/letsencrypt/live/kallydev.com/privkey.pem /etc/letsencrypt/live/kallydev.com/chain.pem
    forward . tls://1.1.1.1
    log
}

执行 coredns -conf Corefile,可以看到 CoreDNS 运行在 53 和 443 端口。

.:53 on 127.0.0.1
https://.:443
CoreDNS-1.8.6
darwin/amd64, go1.17.1,

使用 dig 命令进行验证,dig @127.0.0.1 www.google.com

; <<>> DiG 9.10.6 <<>> @127.0.0.1 www.google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45334
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.google.com.            IN  A

;; ANSWER SECTION:
www.google.com.     152 IN  A   108.160.163.117

;; Query time: 57 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sat Oct 23 18:06:04 CST 2021
;; MSG SIZE  rcvd: 73

同时,CoreDNS 也会输出这次请求的日志。

[INFO] 127.0.0.1:56142 - 45334 "A IN www.google.com. udp 43 false 4096" NOERROR qr,rd,ra 62 0.054863013s