unbound 是支持提供 DNS-over-HTTPS 和 DNS-over-TLS 的,但是为了能够用上 subnetcache 这个模块来给国内用户提供正确的国内IP结果(附上 edns client subnet 并发送给国内的公共DNS),如果处于 Caddy 后面做 upstream server,就不得不使用 proxy_protocol 了,否则 ecs 地址不是 client 的 IP 地址。
首先最重要的是要用 Caddy,而且是构建了 caddy-l4 这个插件的 Caddy,因为 nginx 不支持对 upstream 发起proxy_protocol v2。此外,对于用作DoT的域名,l4这个插件的 handle 部分需要先 terminate TLS 然后再 proxy v2,如下所示:
1 | { |
因为要terminate TLS,所以要单独写一个 tls 来加载证书。至于如何把Caddyfile转成json然后跟这个json做合并,再启动 Caddy,就不在本文范围内了(问 ChatGPT 怎么用 Python 手动合并两个 json ,而且某个 key 需要进行 append 而不是 replace ,它就会给你答案)。
但是根据 DoT 的定义以及 unbound 的配置文件,难道不是应该直接让 Caddy 把数据流 proxy 到 unbound 的 proxy-protocol-port:
并且设置 tls-port:
才对吗?答案是不对,虽然这配置看起来对,但是结果就是 unbound 疯狂报错 SSL wrong version number (0A00010B),意思就是 unbound 并没有期待 TLS 但是 client 在向它发送 TLS 数据流。后来我悟了,DoT 本质上是把 DNS 数据包封装在 TLS 里面,所以如果 unbound 拒绝处理 TLS 那我们就让 Caddy 把 TLS 层去了,再 proxy 给 unbound。
unbound 的示范配置如下:
1 | server: |
不需要设置 tls-port 和 tls证书。这样就可以顺利实现 DoT 跟普通 HTTPS 共用 443 端口而且 TLS 指纹都是 Caddy 了。