Document encrypted DoH upstream for mosdns foreign queries and note that DoH traffic goes direct to hk2, not via OpenClash proxy.
241 lines
17 KiB
Markdown
241 lines
17 KiB
Markdown
# 内网 DNS 架构调研与优化建议
|
|
|
|
> 状态:2026-08-12 调研定稿,Linear **W1N-56**。**Phase 0 验证(2026-08-12)全部完成,最终裁决(用户定稿)已对齐**;本期未改动任何生产 DNS 路径。
|
|
> 2026-08-12 现场核查修正:gfw 上 mosdns **不是闲置**——它是 OpenClash clash 的
|
|
> `nameserver`/`default-nameserver`(DIRECT 规则真实 IP 解析),处于活动链路,裁决第 5 条
|
|
> 的"删除闲置 mosdns"前提不成立,处置改为"正式纳管并文档化"。
|
|
> 相关:`docs/lan-overview.md`、`hosts/dns.windy.lan.md`、`hosts/gfw.windy.lan.md`、W1N-40。
|
|
> 2026-08-13 (W1N-62):gfw mosdns 国外分支已从"明文国内公网 DNS"改为**加密 DoH**
|
|
> (自建 `https://adg.chans.xyz/dns-query`,hk2),新增 `foreign_upstream`/`foreign_fallback`
|
|
> (primary=DoH, secondary=明文国内 DNS, threshold 1000ms),`bootstrap` 用现有国内公网 IP
|
|
> (防自举循环)。实测:mosdns 平面国外域名 A/AAAA 恢复(google AAAA `2607:f8b0…`)、
|
|
> `dup.baidustatic.com`→`0.0.0.0`(AGH 拦截保留)、clash 7874 fake-ip 平面不变。
|
|
> **重要修正(定稿)**:DoH 流量实测为 **gfw→hk2 直连,未经 clash 代理**——nft output 链
|
|
> (mangle mark/tcp redirect)计数为 0、`/proc/net/tcp` 存在到 hk2:443 的 established
|
|
> 连接,路由自身 TCP 输出当前并未被 OpenClash 重定向,故"经代理访问加密 DNS"的假设不成立。
|
|
> **最终决策:接受直连,不强行走代理**——`foreign_upstream` 唯一指向自建解析器
|
|
> `adg.chans.xyz`(hk2),自有 VPS 直连即可达、无被墙/污染问题、DoH/TLS 已加密、应答干净,
|
|
> 走代理毫无增益反而把 DNS 平面耦合进 clash;实测 kill clash 期间国外查询 0.01s 正常应答,
|
|
> watchdog 自动拉起,直连使 DNS 平面独立于代理(优于过代理)。
|
|
> 备份:`config.yaml.bak-foreign-doh-20260813-103746` / `config.yaml.bak-foreign-doh-20260813-103813`。
|
|
|
|
## 1. 现状(实测 2026-08-12)
|
|
|
|
| 角色 | 部署 | 职责 | 是否在活动路径 |
|
|
|------|------|------|----------------|
|
|
| **AdGuard Home** | `192.168.66.36`(PVE VM 120,Docker host 网络) | EdgeRouter DHCP 通告给 LAN55/66 客户端的唯一 DNS;广告/过滤、查询统计、Web 面板 | ✅ **是(LAN 客户端唯一入口)** |
|
|
| **mosdns** | `192.168.66.1`(gfw OpenWrt)监听 `127.0.0.1:6052`(仅本机) | clash 的 `nameserver`/`default-nameserver`:DIRECT 规则真实 IP 分流(国内 → AGH `.36:53`,国外 → `223.5.5.5`/`119.29.29.29`) | ✅ 网关侧(clash 消费,不面向客户端) |
|
|
| **OpenClash / clash(meta)** | `192.168.66.1`(gfw) | gateway 自身/被劫持流量的 fake-ip + 代理;DNS 走 dnsmasq→clash `#7874` | 仅网关侧与 VLAN10 |
|
|
|
|
**关键事实(全部实测):**
|
|
- LAN 客户端 DNS 直连 `66.36`,**不经过** gfw(EdgeRouter `service dns forwarding` cache 512,通告 `.36`)。
|
|
- AGH 上游:DoH `dns.alidns.com`(→`223.5.5.5`/`223.6.6.6`)+ `doh.pub`(→`120.53.53.53`/`1.12.12.12`),
|
|
`upstream_mode: load_balance`、`fastest_timeout: 1s`、`upstream_timeout: 10s`;bootstrap
|
|
`223.5.5.5`/`223.6.6.6`(公网 IP,无自举循环);兜底 DoH `adg.chans.xyz`(→`hk2.chans.xyz`→`154.36.174.161`)。
|
|
DNS 监听 UDP/TCP **v4 only(`0.0.0.0:53`)**,无 v6 监听;`enable_dnssec: false`;cache 4 MB;ratelimit 20。
|
|
- AGH 宿主出网:默认路由 `via 192.168.66.254`(EdgeRouter),**直连,不经 gfw**;DoH 实测可达
|
|
(223.5.5.5:443 → HTTP 400/0.05s,120.53.53.53 → 502/0.06s,adg.chans.xyz 冷连接 ~3.5s)。
|
|
- gfw 自身出网:OpenClash `openclash_mangle_output` 对非本地区域/国内 IP 流量统一
|
|
`mark 0x162 → tproxy 127.0.0.1:7895`(**gfw 自身流量默认走代理**,含 clash 的
|
|
nameserver-policy DoH);mosdns 的上游(AGH `.36` 本地区域、`223.5.5.5` 国内 IP)均被 bypass,保持直连。
|
|
- gfw DNS 链:`server=127.0.0.1#7874`(dnsmasq)→ clash:`nameserver: [127.0.0.1:6052]`(mosdns)、
|
|
`default-nameserver: [127.0.0.1:6052]`、`nameserver-policy` 国外域名 → DoH `https://1.1.1.1/dns-query`、
|
|
`enhanced-mode: fake-ip`(198.18.0.1/16)、`ipv6: false`;nft 有 UDP/53 hijack → dnsmasq。
|
|
- **mosdns 配置缺陷(2026-08-12 发现并修复)**:`main` sequence 的国内分支
|
|
(`matches: qname $domestic_domains → exec: $domestic_upstream`)之后**缺少
|
|
`matches: has_resp → accept` 守卫**。mosdns v5 的 `sequence` 在 `forward` 成功后不会停止,
|
|
只有 `accept`/`reject`/`return` 或错误会终止——因此命中 `geosite_cn` 的查询会被转发**两次**
|
|
(AGH 与 223.5.5.5/119.29.29.29),最终应答来自最后一个 forward(国内公网 DNS),**AGH 的
|
|
拦截/rewrite 对 DIRECT 国内域名静默失效**。实测证据:`dup.baidustatic.com`(在 `geosite_cn`
|
|
且在 AGH 拦截表)经 mosdns 返回真实 IP `183.60.227.49` 而非 `0.0.0.0`。已修复(备份
|
|
`/etc/mosdns/config.yaml.bak-20260812`),修复后同一域名返回 `0.0.0.0`,taobao/google 解析
|
|
与 clash 链均无回归。**§6 的二期示例同款缺陷已一并修正。**
|
|
同日追加加固:`domestic_fallback`(fallback 插件:`primary: domestic_upstream`(AGH)、
|
|
`secondary: default_upstream`(223.5.5.5/119.29.29.29)、`threshold: 500ms`)使 AGH 宕机时
|
|
DIRECT 国内真实 IP 查询回退国内公网 DNS,不再直接报错;实测:AGH 停止时缓存未命中查询由
|
|
fallback 应答(NXDOMAIN/真实 IP),AGH 恢复后主路径即时应答且拦截(`0.0.0.0`)恢复。
|
|
备份:`/etc/mosdns/config.yaml.bak-fallback-20260812`。
|
|
- AGH rewrites(实测):`hass.windy.lan`/`hass.local` → `192.168.55.11`;`dns.windy.lan` → `.36`;
|
|
`ubnt.windy.lan` → `.46`;`gfw.windy.lan` → `.1`;`nas.windy.local` → `.32`。
|
|
- 拦截:仅启用 **AdGuard DNS filter**(filter_1);实测 `doubleclick.net`/`googleadservices.com` → `0.0.0.0`。
|
|
|
|
**现状缺口(实测确认):**
|
|
1. AGH 上游是固定 DoH,**无"国内/国外分流"能力**;国外域名解析质量依赖唯一兜底路径。
|
|
2. **兜底失效**(kill-test 证实):主上游黑洞时,兜底 `adg.chans.xyz` 在客户端 15s 窗口内不生效
|
|
(`upstream_timeout: 10s` + TCP 重试行为),缓存未命中查询无有界降级——见 §8。
|
|
3. 国外域名 AAAA 经国内路径全部置空(见 §8),v6 解析缺位。
|
|
|
|
## 2. 最终裁决对齐(用户定稿 2026-08-12)
|
|
|
|
| # | 裁决 | 本issue处理 |
|
|
|---|------|------------|
|
|
| 1 | **保留 AGH `.36` 为唯一 LAN DNS 入口**(现有方案增强版),不改 EdgeRouter DHCP 通告 | ✅ 现状保持;本期零改动 |
|
|
| 2 | **否决"AGH 全局转发到 Clash fake-IP"**——DNS 平面必须与流量转发平面一致 | ✅ 分层方案(§4)明确 AGH 上游为**真实 IP** 解析路径,不与 fake-ip 混用 |
|
|
| 3 | **AGH → mosdns 仅为二期可选项**(经实测确有需求后启用) | ✅ §4 为二期方案;§8 kill-test 已给出"实测需求"证据(降级缺口) |
|
|
| 4 | 代理 VLAN10 将来用独立 OpenClash DNS 平面(fake-ip + TPROXY),不污染普通 LAN | ✅ 现状即此(dnsmasq→clash,非面向 LAN 客户端);文档记录 |
|
|
| 5 | **删除或明确禁用** `.1` 上未使用的 mosdns | ⚠️ 前提修正:mosdns 是 clash 的 nameserver,处于活动链路(§1)。处置改为**正式纳管并文档化**(本文件 + `hosts/gfw.windy.lan.md`),不删除 |
|
|
| 6 | 先完成验证再改动生产路径 | ✅ 本期完成全部 Phase 0 验证(§8),**未改任何生产 DNS 路径** |
|
|
| 7 | 建立 `home.arpa` 内部域(替代 `.local`) | ⏳ 后续任务:当前命名空间为 `.lan`(AGH rewrites + EdgeRouter DHCP domain),`hass.local` 兼容保留至迁移完成;home.arpa 需联动 AGH rewrites、DHCP domain、客户端,另行排期 |
|
|
| 8 | 高可用时增加第二个等价 AGH(独立物理故障域) | ⏳ 备用方案,记录不实施 |
|
|
|
|
## 3. 两个候选方案评估
|
|
|
|
### 方案 A:AGH 单独作为统一入口(现状演进)
|
|
- 优点:单解析点、面板/拦截/日志集中、维护简单。
|
|
- 缺点:AGH 对 geo 分流 + 防污染支持弱(官方定位是"过滤/家长控制")。固定 DoH 上游无法按域名
|
|
region 选路;且 §8 kill-test 显示主上游全挂时缓存未命中查询无有界降级。→ **不足以解决防污染/分流/降级问题。**
|
|
|
|
### 方案 B:mosdns 作为智能上游分流器
|
|
mosdns(v5)用 `sequence` 编排:`geosite/geoip` 匹配器 → 国内域名转发国内 DoH、国外域名转发加密 DoH(防污染),可加 `cache`、`reject`。
|
|
- 优点:真正解决"国内快 / 国外不被污染"的分流;性能高。
|
|
- 缺点:纯转发器,无 Web 面板、无每客户端统计、拦截靠域名表。→ 单独当入口会退回原始体验。
|
|
|
|
**结论:两个方案互补,不是二选一。** 单用 A 无法分流防污染且降级无界;单用 B 失去 AGH 管理体验。
|
|
|
|
## 4. 二期可选项:分层架构(AGH 前端 + mosdns 后端,经实测需求后启用)
|
|
|
|
```
|
|
局域网客户端(DHCP DNS = 192.168.66.36)
|
|
│
|
|
▼
|
|
AdGuard Home (66.36) ── 前端:广告/过滤、拦截表、每客户端统计、Web 面板
|
|
│ 上游 = mosdns
|
|
▼
|
|
mosdns(66.36 伴生容器) ── 后端:智能分流 + 防污染
|
|
│ - geosite:cn → 国内 DoH/UDP(aliDNS / 腾讯 DNSPod)
|
|
│ - 其他 → 加密 DoH(自建 adg.chans.xyz 等)
|
|
▼
|
|
上游 DoH
|
|
```
|
|
|
|
职责分离:
|
|
- **AGH = 策略/拦截/可观测**(拦截表、每客户端日志、面板)。
|
|
- **mosdns = 智能转发**(geo 分流 + 加密防污染 + 内置 cache,可显著缩短降级窗口)。
|
|
- **OpenClash(gfw)= 代理选路**(fake-ip + 规则)。与 DNS 解析选上游是两个独立决策,分开放最干净。
|
|
|
|
**部署位置:mosdns 与 AGH 同机(66.36 伴生容器),而非 gfw(.1)**:单点即 AGH 所在;不受网关重启/
|
|
OpenClash churn 影响;可纳入现有 compose/ansible 管理;不占用 OpenWrt 资源。放 gfw 会与 clash 的
|
|
DNS 处理互相干扰、耦合,且网关重启即断全 LAN DNS。**不推荐放 .1。**
|
|
|
|
> 启用条件(kill-test 实测需求,§8):主上游全挂时,当前 AGH 单入口对缓存未命中查询无有界降级。
|
|
> 分层方案(或下调 `upstream_timeout` + 改 failover 模式)可修;启用与否由用户在二期决定。
|
|
|
|
## 5. 更优替代方案(一并考虑)
|
|
|
|
1. **分层(推荐二期,见 §4)**:AGH(66.36)→ mosdns(66.36 伴生)→ 上游。体验最好、职责最清。
|
|
2. **纯 mosdns + 前端面板**:损失拦截/统计管理体验。**不推荐**用于替换。
|
|
3. **AGH 只挂一个带分流的上游(第三方 DoH 聚合)**:失去可控性且不可信。不推荐做主路径。
|
|
4. **全部交给 OpenClash fake-ip,关闭 AGH**:让"代理网关"成为全 LAN DNS 单点;且 AGH 拦截/日志也没了。**不推荐。**
|
|
|
|
## 6. mosdns 配置要点(mosdns v5,二期实施预留)
|
|
|
|
核心是 `sequence` + 上游拆分 + 缓存 + 屏蔽:
|
|
|
|
```yaml
|
|
plugins:
|
|
- tag: main
|
|
type: sequence
|
|
args:
|
|
- exec: cache 1024 # 缓存加速
|
|
- matches: has_resp
|
|
exec: accept
|
|
# 国内分流:命中 geosite:cn → 国内 DoH
|
|
- matches: [ qname &geosite:cn ]
|
|
exec: forward https://dns.alidns.com/dns-query
|
|
# 广告域名屏蔽交 AGH 前置,不重复维护
|
|
# 其余(国外)→ 加密 DoH 防污染
|
|
- exec: forward https://adg.chans.xyz/dns-query
|
|
- type: udp_server
|
|
args: { entry: main, listen: "127.0.0.1:5353" }
|
|
- type: tcp_server
|
|
args: { entry: main, listen: "127.0.0.1:5353" }
|
|
```
|
|
|
|
> **注意**:`sequence` 中每个 `forward` 分支之后必须跟 `matches: has_resp → accept`
|
|
> (或改用 `goto`/`jump` + `return` 结构),否则查询会继续执行后续规则被二次转发,
|
|
> 最终应答来自最后一个 forward——gfw 上 mosdns 的同类缺陷(2026-08-12)已实测并修复(见 §1)。
|
|
|
|
要点:
|
|
- 上游可加 `upstream` 的 `concurrent > 1` 与多地址故障切换;mosdns 自带 cache,能保证上游故障时
|
|
缓存命中仍即时应答(对应 §8 认定的降级缺口)。
|
|
- `geosite:cn` / `geoip:cn` 数据自动更新;国内 aliDNS/腾讯,国外可用自建 `adg.chans.xyz`(实测
|
|
唯一能返回国外 AAAA 的路径,§8)。
|
|
- 屏蔽交 AGH 前置,AGH 与 mosdns 不各自维护拦截表。
|
|
|
|
## 7. 迁移 / 实施顺序(二期,待用户确认启用)
|
|
|
|
1. 在 66.36 起 mosdns 伴生容器(`/opt/mosdns` + compose,**按 digest 固定镜像**,纳入 ansible)。
|
|
2. AGH「上游 DNS 服务器」改为指向 mosdns(`127.0.0.1:5353`,bootstrap 仍用公网 IP,避免
|
|
AGH → mosdns → AGH 死循环);只保留**一条语义一致的上游路径**,保持可回滚(备份 yaml + `--check-config`)。
|
|
3. 验证:国内域名、国外域名、被拦截域名、每客户端日志、AAA A 解析(§8 基线)。
|
|
4. 回归:EdgeRouter 通告不变(仍 `.36`),LAN 客户端无感;重启 AGH/mosdns 单点验证(§8 kill-test 模板)。
|
|
5. 上线后重跑 §8 kill-test,确认降级窗口有界。
|
|
|
|
## 8. Phase 0 验证证据(2026-08-12 全部实测)
|
|
|
|
### 8.1 基线与功能
|
|
|
|
| 项 | 结果 |
|
|
|----|------|
|
|
| rewrites:`hass.windy.lan` / `hass.local` | → `192.168.55.11` ✅(兼容保留) |
|
|
| rewrites:`dns.windy.lan` / `gfw.windy.lan` / `ubnt.windy.lan` / `nas.windy.local` | → `.36` / `.1` / `.46` / `.32` ✅ |
|
|
| 国内解析 `taobao.com`(经 AGH) | 真实 CN IP(59.82.x 等)✅ |
|
|
| 国外解析 `google.com` / `github.com`(经 AGH) | 真实 IP(142.250.x / 20.205.x),**无 fake-IP 泄漏** ✅ |
|
|
| 广告拦截 `doubleclick.net` / `googleadservices.com` | → `0.0.0.0` ✅ |
|
|
| clash 7874 `google.com` | `198.18.1.101`(fake-ip,仅网关/VLAN10 平面)✅ |
|
|
| clash 7874 `taobao.com` | 真实 IP(经 mosdns→AGH)✅ |
|
|
| dnsmasq :53(.1)`google.com` / `taobao.com` | fake-ip / 真实 IP ✅ |
|
|
| mosdns 6052 直连 `taobao.com` / `google.com` | 真实 IP(59.82.x / 142.250.73.78)✅ |
|
|
| DNSSEC:`dnssec-failed.org`(经 AGH) | 返回正常应答 `96.99.227.255`(非 SERVFAIL)→ 当前路径不校验,W1N-40 结论复现,**维持关闭** |
|
|
|
|
### 8.2 出口路径与 fake-IP 泄漏
|
|
|
|
- AGH 宿主默认路由 `via 192.168.66.254`(EdgeRouter),**直连出网,不经 gfw**;DoH 端点实测可达
|
|
(见 §1)。LAN 客户端经 AGH 的解析结果全部为真实 IP,无 `198.18/16` 泄漏。
|
|
- gfw 自身流量默认进代理(`openclash_mangle_output` mark 0x162 → tproxy :7895),
|
|
clash nameserver-policy 的 `1.1.1.1` DoH 实测 35ms 可达(走代理链路,不依赖直连)。
|
|
- mosdns 上游(AGH `.36`、`223.5.5.5`)命中本地/国内 bypass 规则,保持直连——设计意图达成。
|
|
|
|
### 8.3 IPv6 / RDNSS / AAAA
|
|
|
|
- LAN 有 IPv6 SLAAC(EdgeRouter dhcpv6-pd /60 → eth0 host-address + switch0,**仅 `service slaac`**,
|
|
**无 RDNSS/dns-server 通告**);`.36` 有全局 v6 地址 + RA 默认路由。
|
|
- **RDNSS 未通告** → v6 客户端无 v6 DNS,回退 v4 DNS(`.36`);AGH 仅监听 `0.0.0.0:53`(v4 only),无 v6 DNS 服务。
|
|
- **AAAA 解析实测**:`baidu.com` 公网本就无 AAAA(dns.google NOERROR/0,权威 NS 而已);
|
|
`taobao.com` AAAA 经国内路径正常(`2408:4001:f10::6f` 等);**国外域名(`google.com`)经
|
|
`223.5.5.5` UDP、alidns DoH、`8.8.8.8` UDP 全部返回空**,而 dns.google 与 `adg.chans.xyz`
|
|
DoH 均能返回 `2404:6800:4005:81a::200e` → **国内路径对国外域 AAAA 置空;`adg.chans.xyz`
|
|
兜底是当前唯一能返回国外 AAAA 的路径**。clash `ipv6: false` 亦不返回 AAAA。
|
|
|
|
### 8.4 自举(bootstrap)循环
|
|
|
|
- AGH `bootstrap_dns: [223.5.5.5, 223.6.6.6]`(公网 IP,非 AGH 自身)→ 无自举循环;AGH 解析
|
|
DoH 主机名不经过自身。二期方案要求 AGH→mosdns 时 bootstrap 仍用公网 IP(§7)。
|
|
|
|
### 8.5 Kill-test 矩阵(2026-08-12,全部实测)
|
|
|
|
| # | 场景 | 结果 |
|
|
|---|------|------|
|
|
| 1 | 重启 `.1` mosdns(init.d) | ✅ 直连 6052 与 clash 链恢复 |
|
|
| 2 | 重启 `.1` dnsmasq | ✅ 真实 IP 与 fake-ip 双路径恢复 |
|
|
| 3 | kill `.1` clash 核心 | ✅ LAN DNS(AGH)不受影响;gfw dnsmasq→clash **有界 3s 失败**(无卡死);OpenClash watchdog ~15s 自动拉起 |
|
|
| 4 | stop/start `.1` OpenClash | ✅ DNS 平面独立于代理;clash 与 tproxy 规则恢复 |
|
|
| 5 | 重启 `.36` AGH 容器 | ✅ 全量恢复:rewrites/拦截/国内外解析/DNSSEC 行为不变 |
|
|
| 6 | 黑洞 alidns DoH(223.5.5.5/223.6.6.6:443) | ✅ ~0.5s 内经 `doh.pub` 应答(load_balance 生效) |
|
|
| 7 | 黑洞全部主上游,兜底存活(adg.chans.xyz) | ⚠️ **客户端 15s 内无应答**——兜底未在窗口内生效 |
|
|
| 8 | 黑洞全部上游(含兜底),缓存未命中 | ⚠️ **25s 内无应答、无 SERVFAIL**——解析器对缓存未命中查询"卡死" |
|
|
| 9 | 黑洞全部上游,缓存命中 | ✅ 瞬时 NOERROR(cache 兜底) |
|
|
|
|
**结论(Phase 0 门禁):** 国内解析在代理停止/上游单点故障/组件重启下均维持可用;
|
|
但**"主上游全挂"时缓存未命中查询无有界降级**——`upstream_timeout: 10s` 与 TCP 重试行为使
|
|
兜底 `adg.chans.xyz` 在实践中无法在客户端期望窗口内生效。这是 §4 二期分层方案(或下调
|
|
`upstream_timeout` + failover 模式)的**实测需求依据**;按最终裁决,本期不改生产路径。
|
|
|
|
## 9. 风险与备注
|
|
|
|
- mosdns 仅监听 `127.0.0.1`(不对外),由 clash 消费;二期若启用,保持同样的边界,避免 LAN 出现两套入口。
|
|
- AGH 上游指向本机 mosdns 时务必配公网 bootstrap,否则自举死循环。
|
|
- 本方案不改 EdgeRouter DHCP/通告、不改 gfw OpenClash 代理规则,只动 66.36 上的 DNS 链路,风险可控。
|
|
- 已知降级缺口(§8.5 #7/#8):主上游全挂时缓存未命中查询无有界降级;启用二期前,LAN 客户端会感知
|
|
超时(约 10s+)。缓解:AGH cache 已覆盖高频域;根治需二期。
|
|
- 与 W1N-40「审查并修正 AdGuard Home」联动:该 issue 侧重 AGH 本身,本 issue 侧重整体 DNS 分层。 |