前言
基于 ImmortalWrt 24.10.1 实际调试经验,适用于 mosdns 分流 + OpenClash fake-ip 模式的透明代理架构。
解决了 DNS 泄露、fake-ip 连接阻塞、DoT 端口封锁等常见问题。
一、架构总览
1.1 整体架构示意图
访问 google.com
访问 baidu.com
NTP / Apple 等
cachesize=0, noresolv=1
server=127.0.0.1#5335
enable_redirect_dns=1
198.18.0.1/16
127.0.0.1:5335
→ 国内 DoH 直连
→ 国外 DoH 经代理
→ reject 广告拦截
GEOIP,CN 直连
MATCH,Others 代理隧道
阿里 DoH
腾讯 DoH
Google DoH
CF DoH
1.2 DNS 数据查询流转图
场景一:访问国内网站(如 baidu.com)
digraph dns_cn {
rankdir=TB;
node [shape=box, style="rounded,filled", fillcolor="#e0f2fe", color="#0284c7", fontname="Helvetica", fontsize=11, margin="0.2,0.15"];
edge [color="#64748b", fontname="Helvetica", fontsize=9, fontcolor="#64748b"];
bgcolor="transparent";
browser [label="浏览器请求 baidu.com", fillcolor="#dbeafe", color="#3b82f6"];
dnsmasq [label="dnsmasq (:53)\n转发到 OpenClash DNS"];
clash_dns [label="OpenClash DNS (:7874)\n委托 mosdns 查询"];
mosdns [label="mosdns (:5335)", fillcolor="#e0e7ff", color="#6366f1"];
match [label="匹配 geosite_cn\n命中国内域名", fillcolor="#ecfdf5", color="#10b981"];
doh_local [label="forward_local\n国内 DoH 服务器\nhttps://dns.alidns.com/dns-query", fillcolor="#ecfdf5", color="#10b981"];
proxy [label="OpenClash 透明代理\n匹配 GEOIP,CN → DIRECT", fillcolor="#f5f3ff", color="#7c3aed"];
real_ip [label="阿里 DNS 返回真实 IP\n例: x.x.x.x", fillcolor="#ecfdf5", color="#10b981"];
direct [label="浏览器直连真实 IP\n访问 baidu.com", fillcolor="#dbeafe", color="#3b82f6"];
result [label="完成\nISP 只看到 HTTPS 流量", fillcolor="#d1fae5", color="#059669", shape=oval];
browser -> dnsmasq [label="DNS 查询 :53"];
dnsmasq -> clash_dns [label="转发"];
clash_dns -> mosdns [label="实际查询"];
mosdns -> match;
match -> doh_local [label="国内域名"];
doh_local -> proxy [label="DoH :443"];
proxy -> real_ip [label="直连返回"];
real_ip -> direct [label="返回真实 IP"];
direct -> result;
}
场景二:访问国外网站(如 github.com)
digraph dns_foreign {
rankdir=TB;
node [shape=box, style="rounded,filled", fillcolor="#e0f2fe", color="#0284c7", fontname="Helvetica", fontsize=11, margin="0.2,0.15"];
edge [color="#64748b", fontname="Helvetica", fontsize=9, fontcolor="#64748b"];
bgcolor="transparent";
browser [label="浏览器请求 github.com", fillcolor="#dbeafe", color="#3b82f6"];
dnsmasq [label="dnsmasq (:53)\n转发到 OpenClash DNS"];
clash_dns [label="OpenClash DNS (:7874)\n委托 mosdns 查询"];
mosdns [label="mosdns (:5335)", fillcolor="#e0e7ff", color="#6366f1"];
match [label="匹配 geosite_!cn 或 fallback\n命中国外域名", fillcolor="#fef3c7", color="#d97706"];
doh_remote [label="forward_remote\n国外 DoH 经代理隧道\ndns.google / cloudflare-dns.com", fillcolor="#fef3c7", color="#d97706"];
fakeip [label="OpenClash 返回 fake-ip\ngithub.com → 198.18.x.x", fillcolor="#fce7f3", color="#db2777"];
connect [label="浏览器连接 198.18.x.x:443", fillcolor="#e0f2fe", color="#0284c7"];
intercept [label="mihomo 拦截\n查找 fake-ip 映射 → github.com\n匹配规则 → PROXY", fillcolor="#fce7f3", color="#db2777"];
proxy [label="代理节点连接真实 IP\n返回数据给客户端", fillcolor="#f5f3ff", color="#7c3aed"];
result [label="完成\nISP 看不到域名和 DNS", fillcolor="#d1fae5", color="#059669", shape=oval];
browser -> dnsmasq [label="DNS 查询 :53"];
dnsmasq -> clash_dns [label="转发"];
clash_dns -> mosdns [label="实际查询"];
mosdns -> match;
match -> doh_remote [label="国外域名"];
doh_remote -> fakeip [label="返回真实 IP\n但 OpenClash\n转为 fake-ip"];
fakeip -> connect;
connect -> intercept [label="透明代理拦截"];
intercept -> proxy [label="代理隧道"];
proxy -> result;
}
二、核心组件详解
2.1 dnsmasq — DNS 入口
角色:局域网 DNS 入口,不做任何解析,仅转发。
# /etc/config/dhcp
config dnsmasq
option noresolv '1' # 不读取系统 /etc/resolv.conf
list server '127.0.0.1#5335' # 所有查询转发给 mosdns
option cachesize '0' # 不缓存(由 mosdns 管理)
option rebind_protection '0' # 关闭 rebind 保护(fake-ip 会触发)
option localuse '1' # 路由器自身也走 dnsmasq
关键配置说明:
| 配置项 | 值 | 原因 |
|---|---|---|
noresolv |
1 |
不使用系统 DNS,确保所有查询走 mosdns |
server |
127.0.0.1#5335 |
转发目标为 mosdns |
cachesize |
0 |
禁用 dnsmasq 缓存,避免与 mosdns 缓存冲突 |
rebind_protection |
0 |
fake-ip 返回的 198.18.x.x 会触发 rebind 保护导致解析失败 |
2.2 mosdns — DNS 分流引擎
角色:根据域名列表决定 DNS 查询走国内还是国外。
# /etc/config/mosdns
config mosdns
option listen_port '5335'
option bootstrap_dns '223.5.5.5' # 国内 UDP DNS,用于预解析 DoH 域名
option cache_size '8000'
option lazy_cache_ttl '86400' # 惰性缓存 24 小时
option dns_leak '1' # 防泄露模式
option redirect '0' # 不劫持 DNS(由 OpenClash 处理)
# 国内 DNS — 加密查询,直连
list local_dns 'https://dns.alidns.com/dns-query'
list local_dns 'https://doh.pub/dns-query'
list local_dns 'https://doh.360.cn/dns-query'
# 国外 DNS — 加密查询,经代理隧道
list remote_dns 'https://dns.google/dns-query'
list remote_dns 'https://cloudflare-dns.com/dns-query'
关键配置说明:
| 配置项 | 值 | 原因 |
|---|---|---|
bootstrap_dns |
223.5.5.5 |
最关键。必须用国内 UDP DNS,用于解析 DoH 服务器域名。绝不能设为 8.8.8.8(需要加密连接,而 853 端口被封) |
local_dns |
国内 DoH | 阿里/腾讯/360 的 DoH 服务,ISP 只能看到 HTTPS 流量 |
remote_dns |
国外 DoH | 只用 DoH,不用 DoT。DoT 端口 853 被国内 ISP 封锁 |
redirect |
0 |
不劫持 DNS 流量,由 OpenClash 统一处理 fake-ip |
dns_leak |
1 |
fallback 时不会回退到国内 DNS,防止泄露 |
分流逻辑:
digraph dns_routing {
rankdir=TB;
node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=10, margin="0.15,0.1"];
edge [color="#64748b", fontname="Helvetica", fontsize=9, fontcolor="#64748b"];
bgcolor="transparent";
query [label="收到 DNS 查询", fillcolor="#dbeafe", color="#3b82f6"];
geo_cn [label="geosite_cn\n国内域名", fillcolor="#ecfdf5", color="#10b981"];
geo_ncn [label="geosite_!cn\n国外域名", fillcolor="#fef3c7", color="#d97706"];
grey [label="greylist\n灰名单", fillcolor="#fef3c7", color="#d97706"];
block [label="blocklist / adlist\n广告拦截", fillcolor="#fee2e2", color="#dc2626"];
apple [label="geosite_apple\nApple 域名", fillcolor="#ede9fe", color="#7c3aed"];
fallback [label="未匹配\nfallback", fillcolor="#fef3c7", color="#d97706"];
fwd_local [label="forward_local\n国内 DoH 直连\n阿里 / 腾讯 / 360", fillcolor="#d1fae5", color="#059669"];
fwd_remote [label="forward_remote\n国外 DoH 经代理\ndns.google / cloudflare", fillcolor="#fde68a", color="#b45309"];
reject [label="reject\n返回 NXDOMAIN", fillcolor="#fecaca", color="#b91c1c"];
apple_fb [label="apple_domain_fallback\n先国内查 → 非中国 IP\n则用 UDP 兜底", fillcolor="#ddd6fe", color="#6d28d9"];
query -> geo_cn [label="匹配"];
query -> geo_ncn [label="匹配"];
query -> grey [label="匹配"];
query -> block [label="匹配"];
query -> apple [label="匹配"];
query -> fallback [label="未匹配"];
geo_cn -> fwd_local;
geo_ncn -> fwd_remote;
grey -> fwd_remote;
block -> reject;
apple -> apple_fb;
fallback -> fwd_remote;
}
fallback 机制(防泄露核心):
{
"primary": "forward_remote_upstream",
"secondary": "forward_remote_upstream",
"threshold": 500,
"always_standby": true
}
primary 和 secondary 都使用同一个远程上游(国外 DoH),这是有意为之:
- 如果 secondary 用国内 DNS,fallback 时就会泄露国外域名查询
- 容错靠
forward_remote内部concurrent: 2并发查询两个国外 DoH 服务器 threshold: 500:如果第一个响应超过 500ms,继续等待另一个结果
2.3 OpenClash — 透明代理 + Fake-IP
角色:透明代理流量转发、fake-ip 地址分配、规则匹配分流。
# /etc/config/openclash
config openclash
option en_mode 'fake-ip' # fake-ip 模式
option fakeip_range '198.18.0.1/16' # fake-ip 地址池
option store_fakeip '1' # 持久化 fake-ip 映射
option dns_port '7874' # OpenClash DNS 端口
option enable_redirect_dns '1' # 启用 DNS 劫持
option redirect_dns '1' # 将客户端 DNS 劫持到 7874
option enable_custom_dns '1' # 启用自定义 DNS
DNS 上游配置:
# OpenClash DNS 上游指向 mosdns
config dns_servers
option type 'udp'
option ip '127.0.0.1:5335' # mosdns
option enabled '1'
option group 'nameserver' # 必须是 nameserver,不能是 default
关键配置说明:
| 配置项 | 值 | 原因 |
|---|---|---|
en_mode |
fake-ip |
返回虚假 IP,让所有流量经过代理层,实现按域名匹配规则 |
enable_redirect_dns |
1 |
劫持客户端 DNS 到 OpenClash,确保返回 fake-ip |
dns_port |
7874 |
OpenClash DNS 监听端口 |
| DNS 上游 | 127.0.0.1:5335 |
指向 mosdns,保留防泄露分流能力 |
store_fakeip |
1 |
持久化映射,重启不丢失 |
fake-ip 工作原理:
digraph fakeip_flow {
rankdir=TB;
node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=10, margin="0.15,0.1"];
edge [color="#64748b", fontname="Helvetica", fontsize=9, fontcolor="#64748b"];
bgcolor="transparent";
s1 [label="客户端查询 github.com", fillcolor="#dbeafe", color="#3b82f6"];
s2 [label="OpenClash DNS 返回\n198.18.x.x (fake-ip)", fillcolor="#fce7f3", color="#db2777"];
s3 [label="客户端连接 198.18.x.x:443", fillcolor="#e0f2fe", color="#0284c7"];
s4 [label="mihomo 拦截\n查找映射:198.18.x.x → github.com", fillcolor="#fce7f3", color="#db2777"];
s5 [label="按 github.com 匹配规则\n→ 走代理节点", fillcolor="#f5f3ff", color="#7c3aed"];
s6 [label="代理节点连接真实 IP\n数据回传客户端", fillcolor="#d1fae5", color="#059669", shape=oval];
s1 -> s2 [label="DNS 查询"];
s2 -> s3 [label="返回虚假 IP"];
s3 -> s4 [label="TCP 连接"];
s4 -> s5 [label="域名映射"];
s5 -> s6 [label="代理转发"];
}
为什么需要 fake-ip:
| 模式 | 规则匹配依据 | 优点 | 缺点 |
|---|---|---|---|
| redir-host | 真实 IP | 简单 | 只能按 IP 匹配,CDN 域名可能匹配错误 |
| fake-ip | 域名 | 精确匹配域名规则 | 需要 DNS 劫持配合 |
2.4 OpenClash 规则
rules:
# LAN 直连
- GEOIP,lan,DIRECT,no-resolve
# 代理节点自身直连(确保代理通道可用)
- DOMAIN,ws.proxy.domain,proxy
- IP-CIDR,代理服务器IP/32,DIRECT
# 分流规则
- GEOIP,google,PROXY
- GEOIP,telegram,PROXY
- GEOIP,twitter,PROXY
- GEOIP,CN,DIRECT
# 兜底 — 未匹配走代理
- MATCH,Others
关键原则:
- 不要给
dns.google、cloudflare-dns.com等 DNS 服务器域名加 DIRECT 规则 - 让它们走
MATCH,Others→ PROXY,DoH 流量经代理隧道发出 - 如果加了 DIRECT,会形成死循环:OpenClash 解析 dns.google → 需要 DNS → 再去查 → 死循环
三、常见问题与解决方案
3.1 DoT 端口 853 被封锁
现象:dial tcp 8.8.8.8:853: i/o timeout
原因:国内运营商普遍封锁 853 端口(DNS over TLS)。
解决:
- mosdns
remote_dns只用 DoH(https://),不用 DoT(tls://) - OpenClash DNS 上游不要使用
tls://类型的服务器
# 错误 ❌
list remote_dns 'tls://8.8.8.8'
list remote_dns 'tls://1.1.1.1'
# 正确 ✅
list remote_dns 'https://dns.google/dns-query'
list remote_dns 'https://cloudflare-dns.com/dns-query'
3.2 Bootstrap DNS 循环依赖
现象:所有 DNS 查询全部失败
原因:bootstrap 设为 8.8.8.8,但连接 8.8.8.8 需要加密(DoT/DoH),而加密连接又需要先解析 DNS → 死循环。
解决:bootstrap 必须是可靠的、直连可达的 UDP DNS。
# 错误 ❌
option bootstrap_dns '8.8.8.8'
# 正确 ✅
option bootstrap_dns '223.5.5.5'
option bootstrap_dns '119.29.29.29'
3.3 Fake-ip 连接阻塞(Pending)
现象:浏览器开发者工具中某些资源(如 avatars.githubusercontent.com)一直显示 pending。
原因:DNS 查询绕过了 OpenClash 的 fake-ip 机制,返回真实 IP。mihomo 无法正确按域名匹配规则,导致连接状态异常。
解决:
- 启用 OpenClash DNS 劫持(
enable_redirect_dns='1') - OpenClash DNS 上游指向 mosdns
- 禁用 mosdns 的 DNS 劫持(
redirect='0'),避免冲突
修复前:客户端 → mosdns → 真实 IP → 连接状态异常
修复后:客户端 → OpenClash DNS → fake-ip → mihomo 正确处理
3.4 DoH 直连死循环
现象:TLS handshake timeout,国外域名全部解析失败。
死循环过程:
digraph dns_loop {
rankdir=TB;
node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=10, margin="0.15,0.1"];
edge [color="#64748b", fontname="Helvetica", fontsize=9, fontcolor="#64748b"];
bgcolor="transparent";
s1 [label="mosdns 查询 google.com\n→ forward_remote", fillcolor="#e0f2fe", color="#0284c7"];
s2 [label="需要解析 dns.google 的 IP\n→ bootstrap 返回 8.8.8.8", fillcolor="#fef3c7", color="#d97706"];
s3 [label="mosdns 直连 8.8.8.8:443\n发送 DoH 请求", fillcolor="#e0f2fe", color="#0284c7"];
s4 [label="流量被 OpenClash 透明代理拦截\n匹配规则 → DIRECT", fillcolor="#fee2e2", color="#dc2626"];
s5 [label="OpenClash 走 DIRECT\n需要解析 dns.google\n→ 再次触发 DoH 请求", fillcolor="#fee2e2", color="#dc2626"];
loop [label="死循环!\n所有国外 DNS 查询失败", fillcolor="#fecaca", color="#b91c1c", shape=oval, fontcolor="#7f1d1d"];
s1 -> s2;
s2 -> s3;
s3 -> s4;
s4 -> s5;
s5 -> loop [style=bold, color="#dc2626", penwidth=2];
loop -> s1 [label="循环", style=dashed, color="#dc2626", constraint=false];
}
解决:OpenClash 规则中不要给 DNS 服务器域名加 DIRECT。
3.5 OpenClash DNS 泄露(nameserver 组配置错误)
现象:ipleak.net 检测到 DNS 泄露,显示 114.114.114.114 / 8.8.8.8 等公共 DNS。
原因:OpenClash 的 DNS 上游条目 group 设为了 default,实际写入了 default-nameserver(bootstrap DNS),而 nameserver 组为空。mihomo 检测到 nameserver 为空后自动填充硬编码默认值 ['114.114.114.114', '119.29.29.29', '8.8.8.8', '1.1.1.1'],导致明文 DNS 泄露。
解决:将 mosdns 条目的 group 从 default 改为 nameserver。
# 查找mosdns条目(通常是最后一个dns_servers条目)
uci show openclash | grep '127.0.0.1:5335'
# 修改group为nameserver
uci set openclash.cfg536193.group='nameserver' # 替换为实际section名
uci commit openclash
/etc/init.d/openclash restart
验证:检查生成的配置中 nameserver 组只包含 mosdns:
grep -A 5 'nameserver:' /etc/openclash/config.yaml
# 应该只有:
# nameserver:
# - 127.0.0.1:5335#disable-ipv6=true
# 不应该有 114.114.114.114 / 8.8.8.8 等
3.6 阿里/腾讯 DoH 服务 502 错误
现象:bad http status codes 502 with body [nginx]
原因:国内 DoH 服务端短暂异常。
影响:间歇性,通常几秒后自动恢复。
缓解:配置多个国内 DoH 上游(阿里 + 腾讯 + 360),mosdns 并发查询时一个失败另一个还能返回。
四、防 DNS 泄露原理
什么是 DNS 泄露?
当你通过代理访问国外网站时,如果 DNS 查询被发送到了国内 DNS 服务器(如 114.114.114.114),ISP 就能看到你在查询什么域名。即使网站流量走了代理,DNS 查询本身已经暴露了你的访问意图。
本方案如何防泄露
ISP 只看到 HTTPS 流量,看不到 DNS 内容
ISP 完全看不到 DNS 查询
对比:有泄露 vs 无泄露
| 场景 | 有 DNS 泄露 | 本方案(无泄露) |
|---|---|---|
| 访问 google.com | DNS 查询发到 114.114.114.114,ISP 看到 google.com |
DNS 查询经代理隧道发到 dns.google,ISP 看不到 |
| DoH 流量 | 直连 dns.google:443,可能被干扰 | 经代理隧道,ISP 只看到代理连接 |
| fallback | 回退到国内 DNS,泄露国外域名 | 只用国外上游,永不泄露 |
五、配置检查清单
| 检查项 | 正确配置 | 错误配置 | 影响 |
|---|---|---|---|
| bootstrap_dns | 223.5.5.5 / 119.29.29.29 |
8.8.8.8 / 1.1.1.1 |
所有 DNS 失败 |
| mosdns remote_dns | https://dns.google/dns-query |
tls://8.8.8.8 |
853 端口被封 |
| OpenClash DNS 上游 | 127.0.0.1:5335 group=nameserver |
group=default 或直接用国内 DNS |
DNS 泄露(114/8.8 等明文查询) |
| OpenClash DNS 劫持 | enable_redirect_dns='1' |
'0' |
fake-ip 不生效 |
| mosdns DNS 劫持 | redirect='0' |
'1' |
与 OpenClash 冲突 |
| OpenClash DNS 域名规则 | 不加 DIRECT,走 MATCH/Others | 加了 DIRECT | 死循环 |
| dnsmasq rebind_protection | 0 |
1 |
fake-ip 解析失败 |
| dnsmasq cachesize | 0 |
正数 | 与 mosdns 缓存冲突 |
| dnsmasq server | 127.0.0.1#5335 |
未设置 | DNS 不走 mosdns |
六、最终配置参考
dnsmasq (/etc/config/dhcp)
config dnsmasq
option noresolv '1'
list server '127.0.0.1#5335'
option cachesize '0'
option rebind_protection '0'
option localuse '1'
option ednspacket_max '1232'
mosdns (/etc/config/mosdns)
config mosdns
option listen_port '5335'
option bootstrap_dns '223.5.5.5'
option cache_size '8000'
option lazy_cache_ttl '86400'
option dns_leak '1'
option redirect '0'
option local_dns_redirect '0'
option concurrent '2'
list local_dns 'https://dns.alidns.com/dns-query'
list local_dns 'https://doh.pub/dns-query'
list local_dns 'https://doh.360.cn/dns-query'
list remote_dns 'https://dns.google/dns-query'
list remote_dns 'https://cloudflare-dns.com/dns-query'
OpenClash (/etc/config/openclash)
config openclash
option en_mode 'fake-ip'
option fakeip_range '198.18.0.1/16'
option store_fakeip '1'
option dns_port '7874'
option enable_redirect_dns '1'
option redirect_dns '1'
option enable_custom_dns '1'
# DNS 上游指向 mosdns(group 必须是 nameserver)
config dns_servers
option type 'udp'
option ip '127.0.0.1:5335'
option enabled '1'
option group 'nameserver'
option disable_ipv6 '1'
七、适用环境
- 系统:OpenWrt / ImmortalWrt 24.10+
- mosdns 版本:5.x+
- OpenClash 核心:Meta(mihomo)
- 代理模式:fake-ip + rule
- ISP 环境:国内运营商(853/DoT 端口被封锁)






