家里的 DNS 一直是 AdGuardHome 在管。国内的明文 DNS 查某些域名时,会拿到一个看着正常、其实不对的 IP,网站就打不开。这篇记录我怎么换成自己的加密 DNS,以及中途踩的几个坑。
为什么要自己搭
换成加密 DNS 是第一步,答案在路上不会被改。但我试过的几个公共 DoH 在我的网络里都不稳:有的连接直接被重置,有的干脆连不上。与其一个个试,不如自己搭一个,域名是自己的,不在任何公共名单里。
搭一个 DoH
我用的是开源项目 CF-Workers-DoH,跑在 Cloudflare 的 Workers 或 Pages 上。部署后加三个变量:
DOH:它转发的上游,填dns.google或cloudflare-dns.comTOKEN:自己编一串随机字符,当作访问路径URL:填nginx,首页会伪装成一个 nginx 默认页
再给它绑一个自己域名下的子域名。最终的 DoH 地址长这样(下面都是占位符):
https://doh.example.com/你的TOKEN验证的时候要注意一件事:只看到 200 不算成功。路径写错了,它会返回伪装的网页,状态码一样是 200。要看返回的类型:
curl -s -H "accept: application/dns-message" -o /dev/null \ -w "%{http_code} %{content_type}\n" \ "https://doh.example.com/你的TOKEN?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB"输出里出现 application/dns-message,才说明 DoH 真的通了。
按域名分流
不是所有域名都需要绕一圈。国内网站走国内 DNS 又快又稳,只有会被污染的域名才交给自己的 DoH。AdGuardHome 支持用 upstream_dns_file 指向一个文件,每行是一个上游,也可以写成带域名前缀的规则:
223.5.5.52400:3200::1[/example.org/example.net/]https://doh.example.com/你的TOKEN没有前缀的是默认上游,带 [/域名/] 的只管这几个域名。域名清单我用的是别人维护的 gfwlist 派生列表,写了个脚本每天凌晨下载、转换格式、重启 AdGuardHome。
踩过的坑
文件里要自带默认上游。 我第一版文件里只有一条带域名的规则。启用之后,没命中规则的域名在日志里就去用了别的上游,表现很怪。把默认上游也写进文件之后正常了。
拼接域名时多了斜杠。 用脚本把一行一个域名拼成一行时,我写出了 /a.com//b.com/ 这样的双斜杠。AdGuardHome 没报错就启动了,但规则没有按预期生效。要拼成 /a.com/b.com/。
别留明文的 fallback。 Worker 一旦失败,fallback DNS 会悄悄接手,拿到的可能就是被污染的答案,而你以为一切正常。我把它清空了,这样出问题会直接解析失败,马上能发现。
测试前先清缓存。 AdGuardHome 会缓存答案,被污染的结果也会被缓存好几分钟。每次改配置之后重启容器,再测。
我的 IPv6 比 IPv4 好。 打开 bootstrap_prefer_ipv6 之后,原来一直失败的分流域名恢复了。我没有单独验证到底是不是这一项起的作用,但改完确实通了。
配置文件的路径。 容器里的 /opt/adguardhome/conf 对应宿主机上挂载的目录,改配置要改宿主机那一份。我一开始在错误的路径上忙活了好一阵。
怎么确认它真的在工作
dig @127.0.0.1 twitter.com +shortwhois 返回的IP | grep -i -E "netname|orgname"拿到 IP 之后,用 whois 看看它属于谁,别凭印象判断。归属对得上目标网站或它的 CDN,才算真的解析对了。AdGuardHome 网页里的 Query Log,点开一条记录能看到它用了哪个上游,这也是分流有没有命中最直接的证据。