Author SHA1 Message Date
windyboy d43fc5aa01 docs(dns): correct gfw/.1 facts (W1N-56)
- hosts/gfw.windy.lan.md: 3 NICs (eth2/VLAN10 ubunt_upg live), mosdns is
  now OpenClash's nameserver (not idle), rewrite VLAN10 Wi-Fi section to
  live-verified state
- docs/lan-dns-architecture.md: mosdns on gfw no longer 闲置; note the
  recommended AGH+.36 companion architecture is still pending review
2026-08-12 21:38:57 +08:00
windyboy ab0fd16a92 docs(dns): add LAN DNS architecture research + recommendation (W1N-56) 2026-08-12 08:39:55 +08:00
windyboy 9c1d5ab6fe docs(dns): record agh_ui_access LAN55 allow for Home Assistant (2026-08-12) 2026-08-12 08:36:51 +08:00
3 changed files with 146 additions and 16 deletions
+118
View File
@@ -0,0 +1,118 @@
# 内网 DNS 架构调研与优化建议
> 状态:2026-08-12 调研,Linear **W1N-56**。基于网络工程师视角,方案待实施评审。
> 2026-08-12 现场核查修正:gfw 上 mosdns 已并入 OpenClash DNS 链(作为 clash 的
> `nameserver`,DIRECT 规则真实 IP 解析用),**不再闲置**;推荐方案(AGH 前端 +
> `.36` 伴生 mosdns 后端)仍待评审落地。
> 相关:`docs/lan-overview.md`、`hosts/dns.windy.lan.md`、`hosts/gfw.windy.lan.md`。
## 1. 现状(实测)
| 角色 | 部署 | 职责 | 是否在活动路径 |
|------|------|------|----------------|
| **AdGuard Home** | `192.168.66.36`(PVE VM 120,Docker host 网络) | EdgeRouter DHCP 通告给 LAN55/66 客户端的 DNS;广告/过滤、查询统计、Web 面板 | ✅ **是** |
| **mosdns** | `192.168.66.1`(gfw OpenWrt)监听 `127.0.0.1:6052` | OpenClash custom DNS 的 `nameserver`(DIRECT 规则真实 IP 分流:国内→AGH `.36`,国外→国内公网 DNS `223.5.5.5`/`119.29.29.29`) | ✅ 网关侧(clash 消费,不面向客户端) |
| **OpenClash / clash(meta)** | `192.168.66.1`(gfw) | gateway 自身/被劫持流量的 fake-ip + 代理,DNS 走 dnsmasq→clash `#7874` | 仅网关侧 |
**关键事实:** LAN 客户端 DNS 直连 `66.36`,**不经过** gfw(EdgeRouter `service dns forwarding` cache 512,通告 `.36`)。所以 gfw/clash 的 fake-ip 分流对"直连 AGH 的客户端"不起作用;gfw 上 mosdns 作为 clash 的 `nameserver` 供 DIRECT 规则连接的真实 IP 解析用(国内→AGH,国外→国内公网 DNS),不面向 LAN 客户端。
**现状缺口:**
1. AGH 上游是**固定 DoH**(alidns/doh.pub,兜底 adg.chans.xyz),**没有"国内/国外分流"能力** → 国外域名解析易受 DNS 污染/时延差,也无法为不同 region 选最优上游。
2. 国内/国外分流逻辑(geo)与代理分流逻辑(clash fake-ip)混在网关上,职责不清。
## 2. 两个候选方案评估
### 方案 A:AGH 单独作为统一入口(现状演进)
- 优点:单解析点、面板/拦截/日志集中、维护简单。
- 缺点:AGH 对 geo 分流 + 防污染支持弱(官方定位是"过滤/家长控制",见 adguard README "Encrypted DNS upstream... requires additional software")。固定 DoH 上游无法按域名 region 选路。→ **不足以解决防污染/分流问题。**
### 方案 B:mosdns 作为智能上游分流器
mosdns(v5)用 `sequence` 编排:`geosite/geoip` 匹配器 → 国内域名转发国内 DoH、国外域名转发加密 DoH(防污染),可加 `cache``reject`(屏蔽)。
- 优点:真正解决"国内快 / 国外不被污染"的分流;性能高(百万域名表也不卡)。
- 缺点:纯转发器,无 Web 面板、无每客户端统计、拦截要靠域名表(不如 AGH 体验)。→ 单独当入口会退回原始体验。
**结论:两个方案是互补的,不是二选一。** 单用 A 无法分流防污染,单用 B 失去 AGH 的管理体验。
## 3. 推荐:分层架构(AGH 前端 + mosdns 后端)
```
局域网客户端(DHCP DNS = 192.168.66.36)
AdGuard Home (66.36) ── 前端:广告/过滤、拦截表、每客户端统计、Web 面板
│ 上游 = mosdns
mosdns(66.36 伴生容器) ── 后端:智能分流 + 防污染
│ - geosite:cn → 国内 DoH/UDP(aliDNS / 腾讯 DNSPod)
│ - 其他 → 加密 DoH(Cloudflare/Google/自建 adg.chans.xyz)
上游 DoH
```
职责分离,每个工具只做自己最擅长的事:
- **AGH = 策略/拦截/可观测**(拦截表、每客户端日志、面板)。AGH 原生干不了"按域名选路",所以不做分流。
- **mosdns = 智能转发**(geo 分流 + 加密防污染)。不用它当入口,所以保持 AGH 的 UX。
- **OpenClash(gfw)= 代理选路**(fake-ip + 规则决定"哪些流量走代理")。与"DNS 解析选上游"是**两个独立决策**,分开放在不同工具最干净——DNS 解析在 66.36 做,代理路由在网关做,互不耦合。
### 推荐部署位置:mosdns 与 AGH 同机(66.36),而非 gf(.1)
| 位置 | 评价 |
|------|------|
| **66.36 伴生容器(推荐)** | 单点即 AGH 所在;AGH→mosdns 走本机/近端一跳;不受网关重启/OpenClash churn 影响;可纳入现有 ansible compose 管理;不占用 OpenWrt 资源 |
| gf(.1) | 虽近网络边缘,但该网关已有 clash fake-ip + 多种劫持规则,再叠 mosdns 会与 clash 的 DNS 处理互相干扰、耦合;且网关重启即断 DNS(影响整个 LAN)。**不推荐** |
> 注意:若把 mosdns 放 gfw,必须先理清与 OpenClash `dnsmasq→clash #7874` + `nft fw4 DNS-hijack` 的先后/覆盖关系,否则会出现"部分设备解析走了 clash、部分走了 mosdns"的混乱。放 66.36 则完全避开这个冲突。
## 4. 更优替代方案(也一并考虑)
1. **分层(推荐,见上)**:AGH(66.36)→ mosdns(66.36 伴生)→ 上游。体验最好、职责最清。
2. **纯 mosdns + 前端面板**:不用 AGH,用 mosdns + 其他统计面板。→ 会明显损失拦截/统计管理体验,除非你讨厌 AGH 的 Docker 部署。**不推荐**用于替换。
3. **AGH 只挂一个带分流的上游(第三方 DoH 聚合)**:例如接一个已做分流的公共 DoH。→ 失去可控性,且不可信。不推荐做主路径。
4. **全部交给 OpenClash fake-ip,关闭 AGH**:把 LAN 客户端 DNS 指到 gfw。→ 让"代理网关"成为全 LAN DNS 单点,网关重启/代理抖动整个内网断网;且 AGH 的拦截/日志也没了。**不推荐。**
## 5. mosdns 配置要点(mosdns v5,预留实施)
核心是 `sequence` + 上游拆分 + 缓存 + 屏蔽:
```yaml
plugins:
- tag: main
type: sequence
args:
- exec: cache 1024 # 缓存加速
# 国内分流:命中 geosite:cn → 国内 DoH
- matches: [ qname &geosite:cn ]
exec: forward https://dns.alidns.com/dns-query
# 广告域名可选屏蔽(或交给 AGH 前置拦截,二选一)
# - matches: [ qname &./blocklist.txt ]
# exec: reject 3
# 其余(国外)→ 加密 DoH 防污染
- exec: forward https://1.1.1.1/dns-query
# 备选国外上游/兜底
- matches: [ has_resp ]
exec: accept
- exec: forward_addr https://208.67.222.222:443/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" }
```
要点:
- 上游可加 `upstream``concurrent > 1``addr` 做多/故障切换。
- `geosite:cn` / `geoip:cn` 数据插件自动从 repo 更新;国内用 aliDNS/腾讯,国外用 DoH(Cloudflare/Google/自建 adg.chans.xyz)。
- 屏蔽交由 AGH 前置(推荐),不要 AGH 和 mosdns 都自己维护一套拦截表(重复)。
## 6. 迁移 / 实施顺序(待评审)
1. 在 66.36 起 mosdns 伴生容器(`/opt/mosdns` + compose,固定 digest,纳入 ansible)。
2. AGH「上游 DNS 服务器」改为指向 mosdns(`http://127.0.0.1:5353/dns-query``127.0.0.1:5353`)。AGH 的 `bootstrap` 仍用公网 IP(避免 AGH → mosdns → AGH 死循环)。
3. 验证:国内域名(如 `taobao.com`)、国外域名(如 `google.com`)、被拦截域名、每客户端日志。
4. 确认后,`disable`/移除 gfw 上闲置的 mosdns(6052)以免混淆。
5. 回归:EdgeRouter 通告不变(仍 `.36`),因此 LAN 客户端无感;重启 AGH/mosdns 单点验证。
## 7. 风险与备注
- mosdns 仅监听 `127.0.0.1`(不对外),由 AGH 消费;避免 LAN 直连 mosdns 造成两套入口。
- AGH 上游指向本机 mosdns 时,务必配 bootstrap 公网 IP,否则自举死循环。
- 本方案不改 EdgeRouter DHCP/通告,不改 gfw OpenClash 代理规则,只动 66.36 上的 DNS 链路,风险可控。
- 与 W1N-40「审查并修正 AdGuard Home」联动:该 issue 侧重 AGH 本身,本 issue 侧重整体 DNS 分层。
+8 -1
View File
@@ -32,10 +32,17 @@ Do not enable AdGuard Home DHCP unless the existing EdgeRouter DHCP service is
explicitly migrated and disabled first. explicitly migrated and disabled first.
`agh-ui-access.service` loads `/etc/nftables-agh-ui-access.nft`. It permits `agh-ui-access.service` loads `/etc/nftables-agh-ui-access.nft`. It permits
only `192.168.66.0/24` to TCP/80 and drops other TCP/80 input. It deliberately `192.168.66.0/24` (LAN66) and `192.168.55.0/24` (LAN55, for Home Assistant
integration) to TCP/80 and drops other TCP/80 input. It deliberately
does **not** restrict DNS, SSH, Docker, or Mihomo ports. Keep it isolated from does **not** restrict DNS, SSH, Docker, or Mihomo ports. Keep it isolated from
Docker-managed nftables tables. Docker-managed nftables tables.
> 2026-08-12: added `192.168.55.0/24` allow so HAOS (`192.168.55.11`) can reach
the HTTP API on `:80` for the Home Assistant AdGuard Home integration; applied
via `sudo systemctl restart agh-ui-access.service` (file edited first, then
reloaded; syntax verified with `nft -c`). Other firewalls (EdgeRouter LAN_IN/
LAN_OUT inactive, PVE zero rules) were already open for LAN55->LAN66.
Current query-log policy is 14 days with anonymized client IPs. Check free Current query-log policy is 14 days with anonymized client IPs. Check free
space before increasing retention. DNSSEC is disabled because the selected space before increasing retention. DNSSEC is disabled because the selected
upstream path did not pass the known-bad-signature validation check; do not upstream path did not pass the known-bad-signature validation check; do not
+20 -15
View File
@@ -8,7 +8,7 @@
| IPv4 | `192.168.66.1` | | IPv4 | `192.168.66.1` |
| SSH | `ssh -4 root@192.168.66.1` (key-only, verified 2026-08-06) | | SSH | `ssh -4 root@192.168.66.1` (key-only, verified 2026-08-06) |
| OS | ImmortalWrt 25.12.0 (r37854), Linux `6.12.87`, x86/64 | | OS | ImmortalWrt 25.12.0 (r37854), Linux `6.12.87`, x86/64 |
| **Host** | **PVE VM 140 (`gfw`)**dual NIC: `net0`→vmbr0(LAN66), `net1`→vmbr1(LAN55) (verified 2026-08-09) | | **Host** | **PVE VM 140 (`gfw`)**3 NICs: `net0`→vmbr0(LAN66/eth0), `net1`→vmbr1(LAN55/eth1, up but unaddressed), `net2`→VLAN10/`ubunt_upg`(eth2, `192.168.10.1/24`) (topology 2026-08-09; eth2/VLAN10 live verified 2026-08-11) |
Do not store the root password in this repository. Do not store the root password in this repository.
@@ -25,8 +25,11 @@ OpenClash runs `/etc/openclash/clash` (clash_meta core) with configuration
- Mode: **fake-ip + TPROXY transparent proxy** (`operation_mode=fake-ip`, - Mode: **fake-ip + TPROXY transparent proxy** (`operation_mode=fake-ip`,
`en_mode=fake-ip`, `proxy_mode=rule`); fake-ip network `198.18.0.0/16` `en_mode=fake-ip`, `proxy_mode=rule`); fake-ip network `198.18.0.0/16`
- DNS path: dnsmasq → clash DNS `127.0.0.1#7874` (`server=127.0.0.1#7874` in - DNS path: dnsmasq → clash DNS `127.0.0.1#7874` (`server=127.0.0.1#7874` in
dnsmasq config); `mosdns` also listens on `127.0.0.1:6052` but is not the dnsmasq config); OpenClash custom DNS uses `mosdns` on `127.0.0.1:6052` as its
active resolver path `nameserver`/`default-nameserver` for DIRECT-rule real-IP resolution
(`/etc/mosdns/config.yaml`): domestic domains → AGH `.36:53`, foreign →
`223.5.5.5`/`119.29.29.29` (Chinese public DNS). mosdns is **not** in the
client query path — LAN/VLAN10 clients receive fake-ip from clash :7874.
- nft: OpenClash injects TPROXY/redirect + DNS-hijack rules into - nft: OpenClash injects TPROXY/redirect + DNS-hijack rules into
`table inet fw4`; a residual `table inet passwall` exists with 0 packets (unused) `table inet fw4`; a residual `table inet passwall` exists with 0 packets (unused)
@@ -43,20 +46,22 @@ OpenClash runs `/etc/openclash/clash` (clash_meta core) with configuration
`8443` is not an OpenClash listener and has no runtime nftables forwarding rule. `8443` is not an OpenClash listener and has no runtime nftables forwarding rule.
It is included only in OpenClash's common TLS-sniffing port list. It is included only in OpenClash's common TLS-sniffing port list.
## VLAN 10 Wi-Fi feasibility ## VLAN 10 Wi-Fi
`gfw` is a VM attached to untagged LAN 66, rather than a physical VLAN-trunk `gfw`'s third NIC `eth2` hosts the `ubunt_upg` interface at `192.168.10.1/24`,
endpoint. The U6 Lite likewise reaches the ER-X over LAN 66, whose DHCP serving the dedicated `ubnt-upg` SSID VLAN 10 (untagged access path from a
service supplies client addresses. Therefore `gfw` cannot currently receive an VLAN-capable switch/trunk; AP management stays untagged on LAN66). The
SSID's VLAN 10 traffic merely by creating an `eth0.10` interface inside the VM. `ubunt_upg` zone runs the **only** DHCP server for `192.168.10.0/24` (UDP/67),
allows DNS (53), and applies `192.168.10.0/24 → eth0 masquerade` (NAT) for
Internet egress. `forward_ubunt_upg` isolates VLAN10 from LAN66/55 and RFC1918
(deny counters 0, `accept_to_lan` passes).
Do not treat a local `eth0.10`/`192.168.10.1` configuration as a deployable Live-verified 2026-08-11: an `ubnt-upg` client received `192.168.10.168` (lease
Wi-Fi gateway unless the hypervisor/vSwitch and the complete physical path to in `/tmp/dhcp.leases`), the `192.168.10.0/24 masquerade` counter climbed
the AP have first been configured and verified to carry tagged VLAN 10. The (215 pkts/42KB), and the LAN55/LAN66 deny counters stayed 0 → VLAN10→LAN
`ubnt-upg` test receiving a `192.168.66.x` lease was the expected consequence isolation holds. See
of the available untagged path, not evidence that this host should compete [docs/lan-se5420-deployment-guide.md](../docs/lan-se5420-deployment-guide.md),
with ER-X DHCP. See [docs/unifi-openwrt-vlan10-implementation-examples.md](../docs/unifi-openwrt-vlan10-implementation-examples.md)
[UniFi Network: Dedicated Wi-Fi through a third-party gateway](../docs/unifi-network.md#dedicated-wi-fi-through-a-third-party-gateway).
## Operational note ## Operational note