这篇是一次 OpenWrt DNS 折腾记录,场景比较具体:京东云雅典娜 AX6600 上跑 OpenWrt,用 PassWall 保留国内外分流,再接两套 AdGuardHome 分别处理国内和国外 DNS。
本文记录本次在京东云雅典娜 AX6600 OpenWrt 路由器上,配置 PassWall 配合双 AdGuardHome 实现国内外 DNS 分流去广告的完整过程、正确配置、踩坑原因和相关知识点。
1. 目标
本次目标是:
- 在 OpenWrt 中接入 AdGuardHome 做 DNS 广告过滤。
- 保留 PassWall 现有国内外分流能力。
- 使用两个 AdGuardHome 分别处理国内和国外 DNS 分支:
- 本地 OpenWrt 服务版 AdGuardHome:处理国内域名广告过滤。
- Docker 版 AdGuardHome:处理国外域名广告过滤。
- 避免 DNS 环路、端口冲突、防火墙转发问题。
- 形成一套以后重装软路由可复用的配置流程。
最终目标链路:
客户端
-> 路由器 10.0.0.1:53
-> PassWall DNS 重定向
-> PassWall dnsmasq:11400
-> chinadns-ng
├─ 国内域名 -> 本地 AGH:127.0.0.1:5333 -> 国内 DNS
└─ 国外域名 -> Docker AGH:127.0.0.1:5336 -> tls://1.1.1.1:853
2. 当前环境关键点
设备和系统:
设备:JDCloud RE-CS-02 / 京东云雅典娜 AX6600
系统:Kwrt 25.12-SNAPSHOT
内核:6.12.62
LAN 地址:10.0.0.1/24
上游网络:海南大学校园网 WiFi
原始 DNS 相关服务:
主 dnsmasq:10.0.0.1:53
PassWall dnsmasq:11400
chinadns-ng:15355 / 15353,具体端口由 PassWall 模式生成
本地 AdGuardHome:127.0.0.1:5333
Docker AdGuardHome:127.0.0.1:5336
Docker 数据目录:
Docker Root Dir:/opt/docker
国外 AGH 数据目录:/opt/agh-foreign
3. 正确配置流程
3.1 配置本地 AdGuardHome 处理国内分支
本地 AGH 是 OpenWrt 服务,不是 Docker 容器。
关键配置:
WebUI:10.0.0.1:3000
DNS:127.0.0.1:5333
重定向:无
本地 AGH 上游建议:
210.37.40.4
210.37.40.5
119.29.29.29
223.5.5.5
不要在国内 AGH 中混入国外 DoH 或国外公共 DNS,例如:
https://dns10.quad9.net/dns-query
https://dns.google/dns-query
1.1.1.1
8.8.8.8
原因:
本地 AGH 负责国内分支,应优先稳定、低延迟、直连。
国外 DNS 或 DoH 容易导致超时、污染或路径变复杂。
PassWall 中设置:
DNS 分流:ChinaDNS-NG
直连 DNS:127.0.0.1:5333
直连 DNS 请求协议:通过 TCP 请求 DNS
远程 DNS:先保持 1.1.1.1,等 Docker AGH 稳定后再改
DNS 重定向:开启
国内链路:
国内域名
-> chinadns-ng
-> 本地 AGH:5333
-> 国内 DNS
-> 返回国内 IP
3.2 部署 Docker 版 AdGuardHome 处理国外分支
创建目录:
mkdir -p /opt/agh-foreign/work /opt/agh-foreign/conf
推荐容器启动命令:
docker run -d \
--name adguardhome \
-v /opt/agh-foreign/work:/opt/adguardhome/work \
-v /opt/agh-foreign/conf:/opt/adguardhome/conf \
-p 3001:3000 \
-p 5336:5336/tcp \
-p 5336:5336/udp \
--restart always \
adguard/adguardhome
初始化页面填写:
网页管理界面:
监听接口:所有接口
端口:3000
DNS 服务器:
监听接口:所有接口
端口:5336
这个配置的对应关系:
宿主机 3001 -> 容器 3000
宿主机 5336/tcp -> 容器 5336/tcp
宿主机 5336/udp -> 容器 5336/udp
访问 Docker AGH:
http://10.0.0.1:3001
3.3 配置 Docker AGH 上游 DNS
国外 AGH 上游建议使用 DoT,避免 DNS 53 端口环路:
tls://1.1.1.1
tls://1.0.0.1
Bootstrap DNS 可以填:
210.37.40.4 //这是海南大学DHCP服务器下放的DNS服务器解析地址
210.37.40.5 //这是海南大学DHCP服务器下放的DNS服务器解析地址
Fallback DNS 留空。
不要混用这些上游:
1.1.1.1
8.8.8.8
tcp://1.1.1.1
tcp://8.8.8.8
https://cloudflare-dns.com/dns-query
https://dns.google/dns-query
原因:
- 裸 IP 默认走普通 DNS 53,被 PassWall DNS 重定向抓回。
tcp://1.1.1.1仍然是 53 端口,触发环路。- 域名 DoH 需要先解析 DoH 服务器域名,链路更复杂。
tls://1.1.1.1走 853 端口,不会被 PassWall 的 DNS 重定向规则抓回 11400。
3.4 添加 LAN 到 Docker 的防火墙转发
电脑从 LAN 访问 Docker 容器映射端口时(你看ADH的配置页需要访问docker内部的ADH),NAT 会把目标改为 Docker 网桥地址,例如:
10.0.0.1:3001 -> 172.17.0.2:3000
这时包需要从:
br-lan -> docker0
OpenWrt 默认只有:
lan -> wan
docker -> wan
缺少:
lan -> docker
所以需要添加规则。
修改前备份:
cp -a /etc/config/firewall /etc/config/firewall.bak.$(date +%Y%m%d%H%M%S)
添加规则:
uci -q delete firewall.lan_to_docker
uci set firewall.lan_to_docker='forwarding'
uci set firewall.lan_to_docker.src='lan'
uci set firewall.lan_to_docker.dest='docker'
uci commit firewall
/etc/init.d/firewall reload
验证:
uci show firewall.lan_to_docker
nft list chain inet fw4 forward_lan
预期看到:
firewall.lan_to_docker=forwarding
firewall.lan_to_docker.src='lan'
firewall.lan_to_docker.dest='docker'
以及:
jump accept_to_docker comment "!fw4: Accept lan to docker forwarding"
3.5 最后接入 PassWall 远程 DNS
确认 Docker AGH 单独稳定:
nslookup youtube.com 127.0.0.1:5336
nslookup google.com 127.0.0.1:5336
docker logs adguardhome --tail 50
确认无大量 timeout 后,再进入 PassWall:
PassWall -> 基本设置 -> DNS
设置:
DNS 分流:ChinaDNS-NG
直连 DNS:127.0.0.1:5333
直连 DNS 请求协议:通过 TCP 请求 DNS
远程 DNS:127.0.0.1:5336
过滤模式:通过 TCP 请求 DNS
默认 DNS:远程 DNS
DNS 重定向:开启
最终国外链路:
国外域名
-> PassWall dnsmasq:11400
-> chinadns-ng
-> Docker AGH:127.0.0.1:5336
-> tls://1.1.1.1:853
-> 返回国外 IP
-> PassWall 按规则代理连接
4. 本次踩坑和原因
4.1 误以为 Docker 映射 3001:3000 一定长期有效
AGH 初始化时 WebUI 默认监听 3000,但初始化页面可以把正式 Web 端口改成 80。
如果 Docker 映射是:
3001 -> 3000
但初始化后 AGH 改成监听容器内 80,外部访问 3001 就会失效。
解决:
- 如果 Docker 映射
3001:3000,初始化页面 Web 端口就必须填3000。 - 如果初始化页面 Web 端口填
80,Docker 映射就必须是3001:80。
4.2 只映射 TCP,没有映射 UDP
错误示例:
-p 5336:5336
这通常只映射 TCP。
DNS 需要同时支持 UDP 和 TCP:
-p 5336:5336/tcp
-p 5336:5336/udp
4.3 LAN 访问 Docker 映射端口失败
现象:
路由器本机 curl http://10.0.0.1:3001 成功
电脑访问 http://10.0.0.1:3001 显示拒绝连接或超时
原因:
路由器本机访问走 INPUT/本机路径。
电脑访问会被 Docker DNAT 到 172.17.0.2,需要 br-lan -> docker0 转发。
OpenWrt fw4 里缺少 lan -> docker forwarding。
解决:
uci set firewall.lan_to_docker='forwarding'
uci set firewall.lan_to_docker.src='lan'
uci set firewall.lan_to_docker.dest='docker'
uci commit firewall
/etc/init.d/firewall reload
4.4 Docker AGH 上游使用 1.1.1.1:53 造成 DNS 环路
错误链路:
PassWall 远程 DNS
-> Docker AGH:5336
-> 1.1.1.1:53
-> 被 PassWall DNS 重定向抓回 11400
-> chinadns-ng
-> PassWall 远程 DNS
-> Docker AGH:5336
-> 无限循环
原因:
PassWall 的 DNS 重定向规则会抓:
udp dport 53 -> redirect to :11400
tcp dport 53 -> redirect to :11400
所以 Docker AGH 再去访问 1.1.1.1:53 时,会被重新拉回 PassWall DNS 入口,形成环路。
解决:
Docker AGH 上游改为 tls://1.1.1.1
即走 DNS-over-TLS 的 853 端口。
4.5 DoH 域名上游不稳定
曾尝试:
https://cloudflare-dns.com/dns-query
https://dns.google/dns-query
日志出现:
failed to init http client: timeout exceeded
Client.Timeout exceeded while awaiting headers
原因:
- DoH 域名上游需要先解析
cloudflare-dns.com、dns.google。 - 解析上游域名又依赖 Bootstrap DNS。
- 在校园网 + PassWall + Docker bridge 环境下,链路复杂且容易超时。
解决:
优先使用 IP 形式的 DoT:tls://1.1.1.1
4.6 Bootstrap DNS 不等于普通查询上游
Bootstrap DNS 的作用:
当上游是域名时,用来解析这个上游服务器的 IP。
例如:
https://dns.google/dns-query
AGH 需要先知道 dns.google 的 IP,这时才用 Bootstrap DNS。
如果上游是:
tls://1.1.1.1
则不需要解析上游服务器域名,Bootstrap 基本不参与普通网站解析。
5. 值得记录的知识点
5.1 上游 DNS 是相对概念
上游 DNS 指的是:
当前 DNS 程序自己答不出来时,把请求转发给谁。
例如:
客户端的上游 DNS = 路由器 10.0.0.1
PassWall dnsmasq 的上游 = chinadns-ng
chinadns-ng 的国内上游 = 本地 AGH
chinadns-ng 的国外上游 = Docker AGH
Docker AGH 的上游 = tls://1.1.1.1
5.2 0.0.0.0 的含义
0.0.0.0 表示监听所有接口。
在容器内部:
0.0.0.0:3000
表示容器内所有网卡都监听 3000。
外部是否能访问,由 Docker 端口映射和 OpenWrt 防火墙决定。
5.3 tls://1.1.1.1 的含义
tls:// 表示 DNS-over-TLS,简称 DoT。
tls://1.1.1.1
等价于:
用 TLS 加密连接 1.1.1.1:853,并在加密连接里进行 DNS 查询。
它的特点:
- 不走 53 端口。
- DNS 查询内容加密。
- 校园网仍然能看到你连接了
1.1.1.1:853。 - 校园网看不到具体查询了
youtube.com还是google.com。
5.4 DNS 加密不等于完全隐身
使用 DoT 后,校园网仍然可以看到:
你的路由器连接了 1.1.1.1:853(但是passwall应该代理了连接,走的是加密)
连接时间
流量大小
校园网看不到:
具体 DNS 查询内容
DNS 返回记录
5.5 双 AGH 的意义
单 AGH:
先过滤,再交给 PassWall 分流
双 AGH:
PassWall 先分流
国内域名 -> 国内 AGH
国外域名 -> 国外 AGH
双 AGH 的价值:
- 国内外规则分开。
- 国内外上游 DNS 分开。
- 日志更容易区分。
- 国内 DNS 不走国外,国外 DNS 不走国内。
代价:
- 链路复杂。
- 更容易产生环路。
- Docker、fw4、PassWall DNS 重定向都要配合正确。
6. 推荐最终配置摘要
本地 AGH:
WebUI:10.0.0.1:3000
DNS:127.0.0.1:5333
上游:210.37.40.4 / 210.37.40.5 / 119.29.29.29 / 223.5.5.5
重定向:无
Docker AGH:
WebUI:10.0.0.1:3001
DNS:127.0.0.1:5336
上游:tls://1.1.1.1 / tls://1.0.0.1
Bootstrap:210.37.40.4 / 210.37.40.5
Docker run:
docker run -d \
--name adguardhome \
-v /opt/agh-foreign/work:/opt/adguardhome/work \
-v /opt/agh-foreign/conf:/opt/adguardhome/conf \
-p 3001:3000 \
-p 5336:5336/tcp \
-p 5336:5336/udp \
--restart always \
adguard/adguardhome
PassWall:
DNS 分流:ChinaDNS-NG
直连 DNS:127.0.0.1:5333
直连 DNS 请求协议:通过 TCP 请求 DNS
远程 DNS:127.0.0.1:5336
过滤模式:通过 TCP 请求 DNS
默认 DNS:远程 DNS
DNS 重定向:开启
防火墙:
lan -> docker forwarding 必须存在
验证命令:
nslookup baidu.com 127.0.0.1:5333
nslookup youtube.com 127.0.0.1:5336
nslookup youtube.com 127.0.0.1:11400
nft list chain inet fw4 forward_lan
docker logs adguardhome --tail 50
7. 故障排查速查
电脑打不开 Docker AGH WebUI:
检查 docker ps 端口映射
检查是否缺 lan -> docker forwarding
检查初始化后 Web 端口是否和 Docker 映射一致
国外网站打不开:
先把 PassWall 远程 DNS 改回 1.1.1.1 恢复通信
单独测试 nslookup youtube.com 127.0.0.1:5336
查看 docker logs adguardhome 是否有 timeout
确认 Docker AGH 上游不要走 53
AGH 日志出现 UDP timeout:
说明上游用了裸 IP 普通 DNS,例如 1.1.1.1
改为 tls://1.1.1.1
AGH 日志出现 failed to init http client:
说明 DoH HTTP 客户端初始化超时
优先避免域名 DoH,上游改为 IP DoT
防火墙 reload 出现 PassWall include 警告:
不一定表示 lan_to_docker 失败
用 nft list chain inet fw4 forward_lan 验证最终规则