NasWorks 部署记录

家 Nas 上跑的服务的方案 / 改动 / 踩坑

OpenWrt 旁路由

家里网络的旁路由:接管全屋默认网关与 DNS 下发,光猫只负责出口 NAT。

系统
ImmortalWrt 18.06-k5.4-SNAPSHOT r12339 (x86/64)
管理地址
192.168.4.200 · SSH root(本机已放密钥,可直接连)
LAN 口
eth0 · 静态 192.168.4.200/24 · 网关 192.168.4.1
角色
全屋默认网关 + DNS 下发;光猫 192.168.4.1 仅做出口 NAT
关键进程
AdGuardHome(192.168.4.200:53,本地 DNS + 去广告);dnsmasq(OpenWrt 内置,原兼 DHCP;2026-10-31 起 DHCP 已关、仅留 DNS 转发);DHCP 现由 NAS 主机上的 nas-dhcp 容器接管(macvlan 容器收不到客户端 DHCP 广播,详见改动/踩坑);OpenClash / Clash Meta(PID 常驻,7892=redir 入口、7890=http、9090=API)——只代理 TCP,UDP 在 mangle 链里被全部 RETURN 掉,不走代理
其他接口
vpn0(tun0)、zerotier(dhcp 获取地址)

最后更新:2026-10-31

方案与决策

目标

全屋流量默认走旁路由 (网关 = 192.168.4.200),以便统一做去广告 / 分流 / 代理等。谁当 DHCP 服务器本身无所谓,关键是下发给客户端的网关必须是旁路由——而网关由 DHCP 的 Option 3 决定。

为什么把 DHCP 从光猫迁到旁路由

光猫的 IPv4 DHCP 服务器配置 页只有:地址池起止、租期、主/备 DNS。没有"下发网关 (Option 3)"字段,它硬编码把自身 192.168.4.1 作为客户端网关下发,无法改。运营商光猫普遍如此。

结论:光猫上做不到把客户端网关指到旁路由 → 解决方案是关闭光猫 DHCP,改由旁路由 dnsmasq 下发 Option 3 = 192.168.4.200(顺带 Option 6 = 192.168.4.200 保持 DNS 一致)。

旁路由自身怎么出网

  • 旁路由 LAN 口网关指回光猫 192.168.4.1(否则它自己不知道怎么出网)。
  • 关于 MASQUERADE(原写"不开",2026-10-31 实测更正):理论上客户端与光猫同处二层,旁路由纯转发即可,光猫回程能直接 ARP 到客户端真实 IP,确实不需要 NAT。但本机 /etc/firewall.user 里实际保留着 iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE,实测有 4241K 包 / 338M 走过这条规则。保留它只是把客户端真实 IP 在光猫侧隐藏掉,无害;若要去掉,必须实测确认仍能上网。以「搭建手册」第 8 步为准。
  • OpenWrt 默认已开 IP 转发 (/proc/sys/net/ipv4/ip_forward = 1),LAN→LAN 同 zone 默认允许转发。
数据流: 客户端 192.168.4.x → 网关 192.168.4.200(旁路由) → 路由表默认路由 → 192.168.4.1(光猫) → 互联网。回程光猫直接送回客户端真实 IP。

踩坑记录

1. dnsmasq 拒绝启动:检测到同网段已有 DHCP 真坑

光猫 DHCP 还没关时,旁路由 /etc/init.d/dnsmasq restart 后,日志报:

found already running DHCP-server on interface 'eth0' refusing to start, use 'option force 1' to override

这是 dnsmasq 的安全保护:启动时探听到网里已有别的 DHCP 服务器,拒绝抢答 (避免双 DHCP 打架)。正确做法:先关光猫 DHCP,再 restart 旁路由 dnsmasq。 不要图省事加 option force 1——那会强制双 DHCP 并存。

2. 双 DHCP 同时开 = 客户端网关随机 必踩

两个 DHCP 服务器同时响应时,客户端拿到哪个全看谁先答:网关可能是 .1 也可能是 .200,表现为间歇性断流、部分设备上不了网。全网只能有一个 DHCP 服务器。

2.1 地址池重叠 + dhcp-authoritative 的 NAK 死循环 真坑

地址池重叠(2026-10-31 实测):光猫池 192.168.4.2 – .254,nas-dhcp 池 .100–.199 —— 完全被覆盖。两个 DHCP 并存时会把同一个 IP 真的发给两台设备,形成 IP 冲突,两边都时通时断。

NAK 死循环:dnsmasq.conf 里原来有 dhcp-authoritative,它会对"不属于自己池子"的续租请求一律回 NAK。拿到光猫地址的设备每次续租都被打回,陷入 DISCOVER→NAK 循环,永远拿不到稳定地址 —— 表现是全屋同时失去网络,而一停 nas-dhcp 立刻恢复。

已修:删除 dhcp-authoritative(dnsmasq --test 通过)。但池重叠本身没法靠改配置解决——要么只开一个 DHCP,要么先把光猫池收窄到 .2–.99,.100–.199 才能安全交给 nas-dhcp。

2.2 「切了 DHCP 就恢复」是假因果 认知坑

现象:全屋断网时,停掉 nas-dhcp + 打开光猫 DHCP → 立刻恢复。很容易得出"是 DHCP 配置搞坏了网络"。

为什么这个推断不成立:切换 DHCP 这个动作发生在光猫上。光猫的 DHCP / DNS 代理 / NAT / Web 管理通常是同一个固件进程在管,改一次设置会重启它的 LAN 服务栈——这本身就能把卡死的状态清掉。恢复可能来自"重启了光猫的服务",而不是"换了哪个 DHCP"。

更强的判据:故障时把本机网关和 DNS 都手动改成光猫 192.168.4.1(等于完全绕开旁路由)。如果仍然不通,说明问题不在旁路由这条链路、也不在 DHCP 分配,而在光猫或更上游——因为旁路由默认路由就是 via 192.168.4.1,全屋只有一个出口,改网关改变不了出口。

该判据来自 2026-10-31 用户实测:改到 .1 依然阻塞,且全屋所有设备同时断——共同节点只有光猫。

2.3 容器 exit 0 + --restart = 无限重启,而 docker ps 看着像在跑 真坑

现象:重建 nas-dhcp 后 docker ps 显示 Up 2 seconds,看起来正常。实际 RestartCount=10 一路涨,ExitCode=0,DHCP 一秒都没服务过。

根因:镜像自带的 CMD 是 sh -c "apk add --no-cache dnsmasq; echo rc=0" —— 只装包,不启动 dnsmasq。装完 exit 0,而 --restart unless-stopped 对 任何退出码都重启(不只是异常退出),于是无限循环。

判据:看 docker inspect 的 RestartCount 和 State.StartedAt,不要只看 docker ps 的 Up xx —— 重启循环时它永远显示刚启动。另一招是 docker exec <name> ps aux,看 PID 1 到底是不是目标进程。

修法:docker run 末尾显式传启动命令。本服务的:

sh -c 'command -v dnsmasq >/dev/null 2>&1 || apk add --no-cache dnsmasq >/tmp/apk_dns.log 2>&1; exec dnsmasq -k --conf-file=/etc/dnsmasq.conf'

exec 让 dnsmasq 顶替 shell 成为 PID 1,信号才能正确送达;command -v 命中就跳过 apk(dnsmasq 2.92rel2 本就在镜像 /usr/sbin/ 里)。

2.4 docker start 不会应用新挂载 脚本坑

改完 -v 挂载后 docker start,容器照旧起来、进程照旧跑,但挂载还是旧的。本服务实测:三个挂载只剩 dnsmasq.conf,log/ 与 leases/ 在 NAS 上全空,日志和租约仍写在容器可写层 —— 容器一重建就全丢。

判据:docker inspect <name> --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{println}}{{end}}' 逐条对;再对一眼 NAS 侧目录里是否真的有文件(目录存在 ≠ 有文件)。

修法:改挂载必须 docker rm -f + docker run 重建。重建前先把容器内的日志和租约 docker exec ... cat 导出来,否则 rm 一执行就没了。

2.5 挂载日志目录后,NAS 上的普通用户读不了日志 权限坑

dnsmasq 在容器内以 uid 100 打开 /var/log/dnsmasq.log,创建出来是 0660、属主 uid 100。/volume2 的目录虽是 777,但文件本身 qinchao 读不了 —— tail 直接 Permission denied,日志落盘却等于没法看。

修法:启动前预建文件并放开到 666:

[ -f "$DIR/log/dnsmasq.log" ] || : > "$DIR/log/dnsmasq.log"
chmod 666 "$DIR/log/dnsmasq.log"

dnsmasq 不会 chmod 既有文件,权限得以保留(实测重启后仍是 666)。租约文件不用管:dnsmasq 重写时用的是 0644,全局可读。

3. uci -q delete 不存在的 option 返回 rc=1 脚本坑

想"清空旧 option 再 add_list"时,如果原本没有这个 option,uci -q delete dhcp.lan.dhcp_option 会返回 1。脚本里若用了 set -e,会在这里直接中断,后面的 commit/restart 全没执行。去掉 set -e,或单独忽略这步的返回值。

4. 旁路由作网关不需要 MASQUERADE 认知坑

一度以为要开 NAT 客户端才能出网。其实客户端与光猫同二层,旁路由只做路由转发就行;光猫回程能直接送回客户端真实 IP。开 MASQUERADE 也能用,但会掩盖客户端真实 IP,非必要不开。

5. macvlan 容器收不到外部 DHCP 广播 真坑

把 DHCP 服务器放在macvlan 容器里时,客户端发的 DHCP 广播 DISCOVER 到不了容器——macvlan bridge 模式不会把外部广播转发给容器端口(单播正常,所以"已连设备能用、新设备不行")。在容器 netns 内抓包 0 个包,宿主机 eth0 却能抓到真实客户端的广播,根因坐实。

promisc 无效:给父接口 eth0 开混杂模式不能解决这个广播转发问题 (实测)。

修法:DHCP 服务改跑在宿主机 host-network 容器上,或把 macvlan 父接口改成 Linux 桥。本项目选前者 (不动 Synology 网络栈、可一键回退):nas-dhcp 容器 --network=host 接管 DHCP,网关/DNS 仍下发 192.168.4.200。

6. 去广告"手机上还有广告"≠ 没走对 DNS 认知坑

⚠️ 后续更正:手机/iPad 实际连的是光猫自带 Wi-Fi(192.168.2.x 独立网段),压根没走 AGH——见下方卡片 7。本卡片结论仅适用于"设备确实在 192.168.4.x 网络"的情形。

先证伪"客户端绕过"假设:AGH 查询日志里,手机 (iPhone,租约 192.168.4.125) 的查询确实打到了 192.168.4.200(日志命中 246 条),且 ad.doubleclick.net、pvs.sohu.com、auth.wosms.cn 等都被 IsFiltered:true 拦掉。即手机 DHCP 拿到的 DNS=192.168.4.200,且 AGH 在工作、在拦。

那为什么还看到广告?这是 DNS 去广告的固有局限,不是配置坏了:① 视频 / App 内广告 (YouTube、抖音、腾讯视频、游戏 SDK)常与正文同域名、走 HTTPS,没有独立广告域名可拉黑;② 个别 App 内置 DoH/DoT 或硬编码 IP 会绕过 AGH(日志显示手机主流流量仍走 AGH,属部分绕过,非整体失效)。

定位办法:AGH 后台 → Query Log,按客户端 192.168.4.125 过滤,看哪些"允许"的域名像广告 → 把具体域名告诉我加自定义规则;或在手机上关"私人 DNS / iCloud 私人中继"排除加密 DNS 绕过。想进一步防 DoT 绕过,可在 OpenWrt 加封 853 与常见 DoH 域名 (你已选"只开明文 53 重定向",暂未做)。

7. 光猫自带 Wi-Fi 的网段行为:三次实测结论不一致 待确认

三次实测(动作相同、结果不同):

观测时机光猫 DHCP手机拿到当时 nas-dhcp
故障当天早上关192.168.2.x运行中
10:1x 复测关169.254.x.x(无应答)已停
10:12 复测关192.168.4.x启动后

已能确定:手机所连的 Wi-Fi 是桥接到 LAN 的——关掉光猫 DHCP 后,只要启动 nas-dhcp,手机就能拿到 192.168.4.x,说明 DHCP 广播确实能到达 nas-dhcp(host-network,跑在 NAS 宿主 eth0 上)。这也解释了为什么 169.254.x.x 会出现:那一刻网里一个 DHCP 服务器都没有。

尚不能解释:早上那次 192.168.2.x 从哪来。若 Wi-Fi 桥接 LAN、且当时 nas-dhcp 确在运行(已由 restarts=0 证明),手机不该拿到 .2.x。可能是:当时连的是光猫的另一个 SSID / 频段(2.4G 与 5G 可能分属不同 VLAN),或光猫在特定状态下启用了兜底 DHCP。下次复现请记录「手机连的是哪个 SSID」。

⚠️ 本卡片不解释"断网"本身。早上断网的根因已定位为 Clash 的 TCP REDIRECT 路径失效(见"改动记录"与下方卡片 8),与 Wi-Fi 网段无关。

8. "有 IP、能解析、打不开网页" = 先查 TCP REDIRECT,别查 DHCP/DNS 真坑

现象模板:设备拿到正常内网 IP,dig 能解析域名,ping 192.168.4.1 正常,但所有网页 / App 打不开。

为什么容易误判:直觉上"上不了网"第一反应查 DHCP 和 DNS。但在本架构下这两条都是独立旁路,它们好着的时候 TCP 仍然可以全断。

链路iptables 位置是否经过 Clash
DNS (53)nat PREROUTING → REDIRECT 本机 53不经过
UDPmangle PREROUTING → 全部 RETURN不经过
TCPnat PREROUTING → REDIRECT :7892经过

复现时按顺序抓这 5 项(缺一项就无法定论):

# ① TCP 是否还在被 REDIRECT 进 Clash(计数应持续增长)
docker exec openwrt iptables -t nat -L openclash -n -v

# ② Clash 的 redir / tproxy 入口是否在监听
docker exec openwrt netstat -tlnp | grep -E ":7892|:7895"

# ③ 从故障客户端直接打 Clash 的 http 入口(绕过 iptables,看能否出网)
curl -x http://192.168.4.200:7890 -m 8 -s -o /dev/null -w '%{http_code}\n' \
  http://www.gstatic.com/generate_204

# ④ Clash 自身还能不能测节点延迟(能测=内核活;不能测=内核假活)
curl -s -H "Authorization: Bearer <secret>" \
  "http://192.168.4.200:9090/proxies/VPS-Reality/delay?url=http://www.gstatic.com/generate_204&timeout=5000"

# ⑤ 内核有无 conntrack 满 / 丢包
dmesg | grep -iE "conntrack|table full"
cat /proc/sys/net/netfilter/nf_conntrack_count

判读:②③④ 都正常但客户端仍不通 → 问题在 nat 规则/转发层;④ 失败 → Clash 内核假活,重启 Clash 即可(不必重启整台旁路由,更不必动 DHCP)。

应急逃生:把客户端网关 + DNS 改回 192.168.4.1 就能绕过整条 Clash 链路立即上网——这正是本次"停 nas-dhcp + 开光猫 DHCP 就恢复"的真正原因。

遗留 / 待办

  • 已收尾 光猫 DHCP 已关闭,nas-dhcp 已成为全网唯一 DHCP(2026-11-01 凌晨切换并验证)。当时"双 DHCP 并存、客户端拿到 .1 网关"的状态已结束,客户端现在拿到的是池内地址、网关与 DNS 均为旁路由。唯一遗留是部分设备仍持光猫时代的老租约(池外地址),续租无人应答后会自动迁到池内,期间这几台不走去广告与分流。
  • 单点故障 旁路由现在是全屋流量必经点,它宕机/重启 = 全家断网。建议给关键设备留一个手动指定网关回 192.168.4.1 的逃生通道。
  • 长跑假活 Clash 已连续运行 15 天以上(CPU 累计 22 小时),出现过"进程活着但 redir 入口不处理新连接"。建议加一条看门狗:每 5 分钟从宿主机探测 7890 出网,连续 3 次失败就 docker restart openwrt 或重启 Clash。
  • 日志缺失 Clash 内核日志未落盘(/tmp/openclash.log 只有脚本层的版本检查),导致本次故障无法回溯确切原因。建议在 OpenClash 里开启内核日志并落到 /volume2(容器重建不丢),否则下次复现仍然只能靠现象推断。
  • 旁路由当前跑:dnsmasq(仅 DNS 转发) + AdGuardHome(:53) + OpenClash(:7892 redir / :7890 http / :9090 API)。

OpenClash 分流代理

跑在旁路由容器里的透明代理 + 分流器:国内域名直连、境外域名走节点,同时给全屋提供 fake-ip DNS。客户端不需要做任何设置 —— 网关指到 192.168.4.200 就自动接管。

版本
luci-app-openclash 0.46.137 · 内核 linux-amd64
控制台
http://192.168.4.200:9090(API) / LuCI → 服务 → OpenClash
运行模式
fake-ip · fake-ip 段 198.18.0.1/16
代理模式
rule(按规则分流)
配置文件
/etc/openclash/config/*.yaml · 运行时 /etc/openclash/*.yaml

端口一览

端口用途谁在用
7890HTTP 代理手动配代理的客户端、脚本、mitmproxy(不用)
7891SOCKS5同上
7892redir(TCP 透明代理)iptables REDIRECT 的目标,全屋 TCP 走这里
7893mixed(HTTP + SOCKS 混合)备用入口
7895tproxy配置里有,但当前分流链没有 TPROXY 规则,未实际使用
7874DNS(fake-ip 解析)AdGuard Home 的上游
9090控制台 API / Dashboard浏览器管理

要让全屋能上网,这 6 项必须对

全部来自 uci show openclash 的实测值。改错任意一项的表现都写在最后一列 —— 排障时先对这张表。

配置项当前值作用配错的后果
运行模式
operation_mode
fake-ip 域名解析成 198.18.x.x,由 Clash 按域名决定走哪条线 改成 redir-host 后真实解析会暴露给上游,境外站点易被污染
代理模式
proxy_mode
rule 按规则分流:国内 DIRECT、境外走节点 改 global 会让国内流量全绕节点,又慢又费流量
局域网连接
intranet_allowed
1 允许局域网设备使用代理端口(allow-lan) 关掉后只有旁路由自己能上网,全屋断网
自定义 DNS
enable_custom_dns
0 不用 Clash 自带的 DNS 配置,交给 AdGuard Home 做上游 开启会覆盖 AGH,广告过滤直接失效
IPv6
ipv6_enable
0 不代理 IPv6 开着但上游不支持时,部分站点解析到 IPv6 后不通(表现为"某些站特别慢/打不开")
UDP 转发
enable_udp_proxy
disable_udp_quic
1 / 1 允许 UDP 代理,并屏蔽 QUIC 不屏蔽 QUIC 时浏览器会绕过代理走 UDP:443,分流形同虚设

配置完怎么验证

# 1) DNS 分流:境外应拿到 fake-ip(198.18.x.x),国内应是真实 IP
nslookup www.google.com 192.168.4.200     # → 198.18.x.x
nslookup www.baidu.com  192.168.4.200     # → 真实 IP(如 110.242.x.x)

# 2) 代理端口能不能出网(需要认证,见下方注意事项)
curl -s -o /dev/null -w '%{http_code}\n' \
  -x http://Clash:口令@192.168.4.200:7890 https://www.google.com/generate_204
# → 204

# 3) 透明代理是否接管 TCP(客户端上不配任何代理,直接访问)
curl -s -o /dev/null -w '%{http_code}\n' https://www.google.com/generate_204
# → 204 说明被旁路由接管了;超时说明没接管

注意事项 动手前必看

  • 代理端口开着认证(用户名 Clash,口令在 LuCI → OpenClash → 覆写设置)。直连 7890 不带凭据会返回 407,看起来像"代理坏了"。口令不要写进仓库或文档。
  • log_level = 0(silent),Clash 不输出连接日志,出问题只能靠现象推断。要排查时临时调高日志级别。
  • Clash 会"假活":进程在、9090 API 能响应、节点延迟能测,但 7892 不处理新连接 → 全屋网页打不开。此时重启 Clash 即可,不用重启旁路由,更不用动 DHCP。
  • 旁路由是全屋流量单点:它宕机 = 全家断网。留好逃生通道(把设备网关手动改回 192.168.4.1)。

最后更新:2026-10-31

方案与决策

为什么用 fake-ip

fake-ip 让所有被代理的域名先解析成一个假地址(198.18.x.x),Clash 收到连接后再按域名决定走哪条线。好处是分流判断不受 DNS 污染影响,连接建立也更快。

代价:容器内 / 旁路由自身的 curl 拿到的是假地址,任何绕过 Clash 的程序都会失败。所以不要在旁路由容器内做联网测试,结论会误导。

为什么 AdGuard Home 在 Clash 上游

链路是 设备 → AGH(:53 过滤) → Clash(:7874 分流) → 上游 DNS。

  • AGH 在上游:先过滤再分流,被拦的域名根本不会进 Clash,省一次转发。
  • Clash 在下游:只负责把域名解析成 fake-ip 并按规则选择节点。
  • 反过来配(Clash 在上游)会导致 AGH 只能看到 fake-ip,过滤规则全部失效。

为什么 TCP 走 REDIRECT 而不是 TPROXY

当前实现是 nat PREROUTING → openclash 链 → REDIRECT :7892。REDIRECT 改的是目标地址,实现简单、兼容性好;代价是看不到原始目标 IP,只能靠 Clash 从连接里还原域名。

UDP 那边 mangle PREROUTING → openclash 链 里全部 RETURN(只有 UPnP 规则),没有 TPROXY —— 所以UDP 实际不被代理。这也是为什么必须 disable_udp_quic=1:否则浏览器用 QUIC 走 UDP,绕过了整条分流链。

为什么开着代理认证

代理端口监听在 ::(所有网卡),局域网内任何设备都能用。加认证是为了防止被蹭流量。代价是所有直连 7890 的脚本都要带凭据,否则 407。

为什么不用 TUN 模式

旁路由是 Docker 容器,/dev/net/tun 默认不可用;且 TUN 会接管全部流量,与 macvlan 网络叠加后路由极易打结。REDIRECT 方案在这个环境里更稳。

改动记录

2026-10-31 · 摸清实际配置(排障前置) 已完成

此前文档只写了"装了 OpenClash",没记录实际配置值,导致排障时反复试错。这次把 uci show openclash 的关键项全部落档:

# 只看端口与模式(不含凭据)
uci show openclash | grep -E 'port|mode|intranet|ipv6|udp|dns'

# 实测值
proxy_port    = 7892   # redir,全屋 TCP 的目标
tproxy_port   = 7895   # 配置有,但分流链未使用
mixed_port    = 7893
socks_port    = 7891
http_port     = 7890
dns_port      = 7874   # AGH 的上游
cn_port       = 9090   # 控制台
proxy_mode    = rule
operation_mode= fake-ip
intranet_allowed = 1
enable_custom_dns = 0
ipv6_enable   = 0
enable_udp_proxy = 1
disable_udp_quic = 1
log_level     = 0      # silent

一处纠错:之前笔记把 tproxy 记成 7893,实际 7893 是 mixed、tproxy 是 7895。已按实测值更正。

2026-10-31 · 确认分流链与"UDP 不代理"

# 三条路径互不相干
DNS(53)  → nat PREROUTING  REDIRECT 到本机 53(AGH)      # 完全不经过 Clash
UDP      → mangle PREROUTING → openclash 链 → 全部 RETURN  # UDP 不代理
TCP      → nat PREROUTING → openclash → REDIRECT :7892    # Clash redir 入口
检查项结果
DNS 分流✓ google → 198.18.x.x(fake-ip);baidu → 真实 IP
TCP 透明代理✓ 客户端不配代理可访问境外站点
代理端口认证! 不带凭据返回 407,带凭据 204
UDP 代理✗ 分流链内无 TPROXY,UDP 全部 RETURN

踩坑记录

1. Clash 会"假活":进程活着但不处理新连接 真坑

现象:全屋网页打不开,但 ps 能看到 Clash 进程、9090 API 正常响应、甚至能测出节点延迟。

根因:7892 redir 入口不再接受新连接(长跑后状态异常)。进程存在 ≠ 服务可用。

解决:重启 Clash 即可。不必重启旁路由,更不要动 DHCP —— 曾经因为误判成 DHCP 问题折腾很久。

判据:"有 IP、能解析、网页打不开" → 查 TCP REDIRECT / Clash redir 入口,别查 DHCP、别查 DNS。

2. 直连 7890 返回 407,被误判成"代理挂了" 坑

OpenClash 开着代理认证,curl -x http://192.168.4.200:7890 不带凭据一律 407。加上凭据即可:

curl -x 'http://Clash:口令@192.168.4.200:7890' https://www.google.com/generate_204

错误口令返回 403,可以据此区分"没带凭据"和"口令错"。

3. 7890 对国内 DIRECT 域名一律失败,只有境外能通 真坑

现象:用 7890 做上游时,境外站点正常,但 baidu.com / bilibili.com 这类走 DIRECT 的域名,CONNECT 返回 200 后一发明文就断(客户端看到 000)。

结论:这个环境里 HTTP 代理端口不适合当上游。正确做法是让下游程序把默认网关指向 192.168.4.200,由透明代理接管 —— mitmproxy 就是这么改的(见 mitmproxy 的「方案与决策」)。

另外:在旁路由容器内部连 127.0.0.1:7890 访问境外也失败 —— openclash_output 链把容器自己的出站绕成了环。所以容器内不要做联网测试。

4. log-level: silent 导致故障无法回溯 坑

配置里 log_level = 0(silent),Clash 内核不输出连接日志,/tmp/openclash.log 只有脚本层的版本检查记录。出问题时只能靠现象推断。

建议:排查时临时调高日志级别并落到 /volume2(容器重建不丢),否则下次复现仍然抓瞎。

5. fake-ip 让"容器内测网"的结论全部失真 坑

容器内解析境外域名得到 198.18.x.x,这个地址只有 Clash 认得。任何绕过 Clash 的探测(直连 curl、ping)都会失败或超时,看起来像"网络坏了",其实只是没走代理。

所有验证都要从局域网其它设备发起,或在容器内显式走代理端口。

日常运维

  • 建议 加一条看门狗:每 5 分钟从宿主机探测一次出网,连续 3 次失败就重启 Clash。
  • 建议 开启内核日志并持久化,否则"假活"这类问题永远只能靠猜。
  • 注意 改任何配置前先备份 /etc/openclash/,容器重建会丢。

AdGuard Home 广告过滤

旁路由上的全屋 DNS 过滤器,独占 53 端口。所有走旁路由的设备,DNS 查询先经它按规则库筛一遍,命中黑名单的域名直接返回 :: / 0.0.0.0,请求根本发不出去。

版本
AdGuard Home v0.107.39
监听
:::53(IPv4 + IPv6 全接管)
管理面板
http://192.168.4.200:3000
配置文件
/etc/AdGuardHome.yaml(规则在 user_rules 段)
数据目录
/usr/bin/AdGuardHome/data/(querylog.json / filters/ / stats.db)
上游 DNS
127.0.0.1:7874 —— 指向 OpenClash,不是公共 DNS

配置方式(4 步)

1) 让 dnsmasq 交出 53 端口

AGH 和 dnsmasq 抢同一个端口会有一个起不来。正确做法是把 dnsmasq 的 DNS 关掉但保留它的其他功能:

uci set dhcp.@dnsmasq[0].port='0'
uci commit dhcp
/etc/init.d/dnsmasq restart
# 验证:53 上只剩 AdGuardHome
netstat -tlnp | grep ':53 '

2) 上游 DNS 指向 OpenClash,不要填公共 DNS

面板 → 设置 → DNS 设置:

项值为什么
上游 DNS 服务器127.0.0.1:7874交给 Clash 做 fake-ip 解析 + 分流。填公共 DNS 会绕过 Clash,境外站点被污染
Bootstrap DNS9.9.9.10 / 223.5.5.5 / 119.29.29.29 等只用来解析上游服务器自己的域名,不参与业务查询
上游模式并行(默认)单个上游挂掉不影响解析

3) 启用过滤器列表

面板 → 过滤器 → DNS 黑名单。当前启用 4 条(共约 12 MB 规则):

列表状态说明
AdGuard DNS filter启用官方基础库,覆盖面广
CHN: AdRules DNS List启用国内 App 广告域名,效果最明显
CHN: anti-AD启用国内综合库,体积大
CHN: ADgk 广告净化启用补充国内 App 广告
AdAway Default Blocklist停用与 AdGuard DNS filter 重叠度高
HUN: Hufilter停用匈牙利语地区库,用不上

4) 自定义规则(按 App 精修)

面板 → 过滤器 → 自定义过滤规则。两种语法:

||domain.com^              # 拦截该域名及其所有子域
@@||domain.com^$important  # 放行(优先级高于拦截)

# 实例:拦掉闲鱼的广告上报域名
||i9.taou.com^
||track.mm.taou.com^

# 实例:某些功能域名被误伤时,显式放行
@@||feed.baidu.com^$important

放行优先于拦截 —— 这是排障时最容易忘的一条:明明写了拦截规则却不生效,先查有没有 @@ 放行把它盖掉了。

配置完怎么验证

# 1) 拦截是否生效 —— 应返回 :: 或 0.0.0.0
nslookup i9.taou.com 192.168.4.200

# 2) 放行是否生效 —— 应返回真实 IP
nslookup feed.baidu.com 192.168.4.200

# 3) 正常域名不能误伤 —— 应返回真实 IP
nslookup www.baidu.com 192.168.4.200

# 4) 看哪些请求被拦了(面板 → 查询日志,或看原始日志)
grep -c 'i9.taou.com' /usr/bin/AdGuardHome/data/querylog.json

注意事项 动手前必看

  • AGH 只能按域名整拦。同一个域名下混着正文和广告(第一方广告)就拦不了 —— 这是 DNS 方案的物理天花板,要治只能上 MITM(见「mitmproxy 广告改写」)。
  • 改完配置要重启才稳:/etc/init.d/AdGuardHome restart。热改 user_rules 有时不立即生效。
  • 改之前先备份:cp /etc/AdGuardHome.yaml /etc/AdGuardHome.yaml.bak-$(date +%Y%m%d%H%M)。误加一条规则可能让某个 App 整页打不开。
  • 设备设了 HTTP 代理后,AGH 对它完全失效 —— 因为代理模式下设备只发 CONNECT host:443,不做 DNS 查询。这种情况下要在 MITM 层补一份同样的名单。

最后更新:2026-10-31

方案与决策

为什么让 AGH 独占 53,而不是让 dnsmasq 转发给它

dnsmasq 的 port=0 是"关掉 DNS"的标准做法,不是删掉 dnsmasq。这样:

  • 53 只被一个进程监听,不会出现"两个都起来了但随机响应"。
  • dnsmasq 的 DHCP 等功能还能继续用(本环境 DHCP 已迁到 NAS 的 nas-dhcp 容器,见 OpenWrt 小节)。
  • AGH 能拿到真实客户端 IP,查询日志按设备可查。走 dnsmasq 转发的话,AGH 只会看到 127.0.0.1。

为什么上游是 Clash 而不是公共 DNS

链路:设备 → AGH(:53 过滤) → Clash(:7874 fake-ip) → 上游。

如果把 AGH 的上游直接填 223.5.5.5:

  • Clash 收不到 DNS 查询,fake-ip 机制失效,分流退化成按 IP 判断,国内 CDN 会被误判成境外。
  • 境外域名拿到的是真实 IP,容易被污染。

所以顺序不能反:过滤在上,分流在下。被拦的域名根本不会进 Clash,省一次转发。

为什么不用 AGH 自带的 DHCP

AGH 的 DHCP 与 dnsmasq 的 DHCP 会抢同一个 67 端口。而且旁路由是 macvlan 容器,收不到客户端的二层 DHCP 广播(这是手机拿不到 IP 的根因)。所以 DHCP 交给 NAS 上的 host-network 容器 nas-dhcp,AGH 只做 DNS。

规则优先级

顺序规则类型说明
1(最高)@@||domain^自定义放行,压过所有拦截规则
2||domain^自定义拦截
3过滤器列表订阅的黑名单库

这条顺序决定了排障方向:写了拦截却不生效,先查有没有放行规则;而不是怀疑 AGH 没启动。

已知天花板

  • 只能按域名拦:同一个域名下的广告(第一方广告)拦不了。
  • 路径级规则执行不了:很多规则库里写的是 domain.com/path/to/ad,AGH 只认域名,这类规则会被忽略。
  • 预缓存的广告拦不住:App 提前下好的开屏素材,断网也能播。

改动记录

2026-10-30 · 接管 53 端口,全屋生效 已完成

# dnsmasq 关 DNS(保留其他功能)
uci set dhcp.@dnsmasq[0].port='0' && uci commit dhcp
/etc/init.d/dnsmasq restart
# AGH 上游指向 Clash 的 DNS
# 面板 设置 → DNS 设置 → 上游 DNS = 127.0.0.1:7874

结果:全屋约 50 万条规则生效,DNS 查询全部经 AGH 过滤后再交给 Clash 分流。

2026-10-30 · 自定义规则逐个 App 攻坚

日期动作结果
10-30 加 ||i9.taou.com^ 等闲鱼广告域名 ✓ 成功 闲鱼开屏 + 信息流广告消失,商品图正常(走 gw.alicdn.com,未被误伤)
10-30 加 ||cm.bilibili.com^ / ||*.play-analytics.com^ ✓ 已拦 解析返回 ::
10-30 加 ||ads.privacy.qq.com^ ✗ 无效 规则库里存在 @@||ads.privacy.qq.com^ 放行,放行优先。已删除该条
10-30 加 ||igtmpg1.kuwo.cn^(波点音乐) ✗ 误伤已撤 见踩坑记录 3 —— 该域名是共享网关,拦掉后乐库/商场整页打不开
10-31 加 ||mnt-report.skytigris.cn^ / ||trace-report.tgp.qq.com^ ✓ 已拦 由日志挖矿脚本筛出的候选

当前规则清单(21 条,17 拦 + 5 放行,含 1 空行)

# 放行(优先级最高)
@@||clientservices.googleapis.com^$important
@@||badjs.weixinbridge.com^$important
@@||otheve.beacon.qq.com^$important
@@||feed.baidu.com^$important
@@||hpd.baidu.com^$important

# 拦截
||track.mm.taou.com^        ||tk2.taou.com^
||app-sentry.taou.com^      ||pum.m.alibaba.com^
||ssp.taobao.com^           ||i9.taou.com^
||cm.bilibili.com^          ||cg.play-analytics.com^
||up.play-analytics.com^    ||dnha.play-analytics.com^
||o2o-ad-clicking.alibaba.com^
||cp601.prod.do.dsp.mp.microsoft.com^
||geo.prod.do.dsp.mp.microsoft.com^
||kv601.prod.do.dsp.mp.microsoft.com^
||al-log.d.meituan.net^     ||mnt-report.skytigris.cn^
||trace-report.tgp.qq.com^

备份

/etc/AdGuardHome.yaml.bak-202610302137
/etc/AdGuardHome.yaml.bak-202610302306
/etc/AdGuardHome.yaml.working-202610310040
/etc/AdGuardHome.yaml.stable-202610310201

踩坑记录

1. 拦截规则写了却不生效:放行规则优先 真坑

现象:加 ||ads.privacy.qq.com^ 后该域名仍正常解析。

根因:订阅的规则库里已经有 @@||ads.privacy.qq.com^ 放行规则,放行永远压过拦截。

解决:先查有没有 @@ 冲突,再决定是删掉自己的规则还是加 $important 提权。别急着怀疑 AGH 没启动。

2. querylog 不实时落盘,抓不到刚发生的请求 真坑

现象:刚打开 App 制造了广告请求,去 querylog.json 里找不到。

根因:AGH 的查询日志在内存里缓冲,定时刷盘,间隔可达 5 分钟以上。

应对:要么等几分钟再看,要么直接在面板的「查询日志」里实时看(内存态)。抓开屏广告时尤其容易踩 —— 素材预缓存的请求可能压根没发生。

3. 把共享网关当独立广告 CDN → 整页功能被打挂 真坑(已回滚)

现象:拦掉 igtmpg1.kuwo.cn(波点音乐)后,乐库和商场页面打不开。

根因:bd-api.kuwo.cn(波点的 API)CNAME 到 igtmpg1.kuwo.cn,同一个网关 IP 111.206.98.20。拦这个域名等于同时拦掉 API + 图片 + H5 页面。

解决:立即删规则、重启 AGH、验证恢复解析。教训:看域名像广告就拦,很可能拦掉的是整个业务网关。拦之前先 dig +trace 看 CNAME 链,确认它是不是多业务共用。

4. 第一方广告拦不住:DNS 的物理天花板 局限

汽水音乐的开屏、B 站的开屏、波点的信息流,广告内容和正文走同一个域名。DNS 只能按域名整拦,拦了正文也没了。

另一个表现:预缓存广告。App 提前把开屏素材下好,之后即使域名被拦,开屏照样播 —— 需要删掉 App 重装清缓存才能验证规则是否生效。

要治这类广告只能上 MITM(见「mitmproxy 广告改写」)。

5. 双 DHCP 导致"有的设备没去广告" 真坑

现象:部分设备有去广告、部分没有,同一台设备时快时慢。

根因:光猫 DHCP 与 nas-dhcp 同时开着,客户端随机拿到网关 .1 或 .200。拿到 .1 的设备完全绕过旁路由。

判据:设备上查网关,192.168.4.1 = 没走旁路由。

解决:同一网段只能有一个 DHCP。二选一,不要双开。

6. 规则库里的"路径级"规则 AGH 执行不了 坑

不少规则库写的是 cm.bilibili.com/cm/api/xxx 这种带路径的形式,AGH 只认域名,这类规则会被静默忽略 —— 看着"已启用 50 万条",实际生效的没那么多。

判断方法:拦截后 nslookup 看是否返回 ::,返回真实 IP 就是没生效。

日常运维

  • 工具 日志挖矿脚本 scripts/mine_ads.py:扫 querylog,按广告特征词筛出未被拦的候选域名,人工确认后再加规则。
  • 建议 每次加规则前备份 yaml;加完立即用 nslookup 验证目标域名 + 一个同 App 的功能域名。
  • 注意 改动后同步更新 mitmproxy 的 block_hosts,否则设备设了代理就丢掉这份名单。

全局注意事项

这一页把跨服务的坑集中列出。每条都给了详情入口,出问题时按这张表先对一遍,能省掉大半试错。

会断网的(动手前先想清楚怎么退)

注意点说明详情
旁路由是单点 全屋流量必经 192.168.4.200,它宕机 = 全家断网。给关键设备留一条手动把网关改回 192.168.4.1 的逃生通道。 OpenWrt · 踩坑
全屋同时断 + 改网关无效 = 故障在光猫或更上游 全屋所有设备同时断,且把本机网关/DNS 手动改成光猫 .1(完全绕开旁路由)仍然不通 —— 说明问题不在旁路由、也不在 DHCP,因为旁路由默认路由是 via 192.168.4.1,全屋只有一个出口。先确认光猫管理页还能不能打开(nc -vz 192.168.4.1 80):能开=光猫转发/NAT 出问题;打不开=光猫整体挂了,断电重启。 OpenWrt · 踩坑 2.2
同一网段只能有一个 DHCP 光猫 DHCP 与 nas-dhcp 双开时,客户端随机拿到网关 .1 或 .200,表现为"有的设备有去广告、有的没有,时快时慢"。必须二选一(现状:光猫 DHCP 已关,nas-dhcp 是唯一 DHCP,2026-11-01)。
⚠️ 实测地址池还重叠:光猫 .2–.254 完全覆盖 nas-dhcp 的 .100–.199,双开会把同一 IP 发给两台设备。
⚠️ dhcp-authoritative 会对非本池续租回 NAK,导致设备 DISCOVER→NAK 死循环(2026-10-31 已从配置删除)。
⚠️ 换 DHCP 后老租约不会立刻失效:设备会一直用旧地址(旧网关)直到续租失败才迁移。
OpenWrt · 踩坑 2 / 2.1
Clash 会"假活" 进程在、9090 API 能响应、节点延迟能测,但 7892 不处理新连接 → 网页全打不开。此时重启 Clash 即可,别去动 DHCP / DNS。 OpenClash · 踩坑 1
改配置前先备份 旁路由是容器,重建会丢 /etc/openclash/ 和 /etc/AdGuardHome.yaml。改之前 cp xxx xxx.bak-$(date +%Y%m%d%H%M)。 AGH · 备份清单

安全相关的

注意点说明详情
装 CA = 这台设备的 HTTPS 全可解密 mitmproxy 的 CA 装进哪台设备,那台设备的密码、聊天内容、支付信息都能被 NAS 解密。只装自己的设备,不用时关掉 Wi-Fi 代理。 mitmproxy · 安全提醒
CA 私钥不能外传 私钥在 mitmproxy 的数据目录下(具体路径见该服务的概览节),泄露 = 所有装过证书的设备流量被看光。不要提交进仓库。 mitmproxy · 概览
代理端口开着认证 OpenClash 的 7890 需要凭据,不带凭据返回 407(不是"代理坏了")。口令不要写进文档或仓库。 OpenClash · 踩坑 2

两套过滤必须同步

这是最容易漏的一条:

  • 走 DNS 的设备 → 由 AdGuard Home 拦(按域名)。
  • 手动设了 HTTP 代理 的设备 → 不再做 DNS 查询,AGH 对它完全失效,改由 mitmproxy 的 block_hosts 拦。

所以每次往 AGH 加规则,都要同步进 docker/mitmproxy/rules.json 的 block_hosts,否则一旦设了代理,那台设备的广告会全部回来。

# 拉 AGH 当前规则
ssh root@192.168.4.200 "sed -n '/^user_rules:/,/^[a-z_]*:/p' /etc/AdGuardHome.yaml"
# 同步进 rules.json 的 block_hosts / allow_hosts(热加载,不用重启)

详见 → AGH · 注意事项 · mitmproxy · 概览

验证时最容易骗自己的几件事

陷阱正确做法
在旁路由容器内 curl 外网 必然失败。fake-ip 让境外域名解析成 198.18.x.x,且 openclash_output 链把容器自己的出站绕成环。所有验证从局域网其它设备发起。
在 NAS 宿主机上连 .200 / .201 连不上 —— macvlan 的宿主隔离,宿主不能和自己的 macvlan 子接口通信。用 Mac / 手机测。
export NO_PROXY="*" 之后用 curl -x 代理被静默绕过,-x 形同虚设,看到"成功"其实是直连。要 unset 掉所有代理变量,不要设 NO_PROXY=*。
重启容器后立刻验证 mitmproxy 重启后有 30~60 秒初始化期,期间 HTTPS 全握手失败,会被误判成"方案失效"。等一分钟再测。
只拦域名就以为广告没了 开屏素材可能预缓存,拦了域名照样播。要删 App 重装清缓存才能验证。

能力边界(这些做不到,别再试了)

  • DNS 只能按域名整拦 —— 第一方广告(与正文同域名/同 CDN)拦不了;规则库里带路径的规则 AGH 执行不了。
  • MITM 只对设了代理的设备生效,且 iPhone 只在 Wi-Fi 下能设代理;移动网络、别人的设备都不覆盖。
  • Clash 的 7890 不能当上游 —— 这个环境里它对国内 DIRECT 域名一律失败,只有境外能通。
  • UDP 不被代理 —— 分流链里没有 TPROXY,所以必须 disable_udp_quic=1,否则浏览器走 QUIC 绕过整条链。

最后更新:2026-10-31

WireGuard 出口 VPN(腾讯云)

把"在家才能用的出网能力"搬到公网侧。家里原本靠 SSH 动态转发在本地开 127.0.0.1:1080 的 SOCKS 代理来出网,但那个代理只活在"家里那台机器"的 localhost 上,手机在外面(蜂窝网)根本到不了。于是直接在腾讯云 CVM(43.159.43.10)上跑一个 WireGuard 服务端,手机装客户端连上来,所有流量经这台服务器出去,等价于在外面也能用家里的出网通道。

服务器
腾讯云 CVM 43.159.43.10 · Ubuntu 22.04 x86_64 · 内核 5.15(已内置 WireGuard)
隧道网段
10.0.0.0/24(服务端 10.0.0.1 / 手机 10.0.0.2)
监听端口
UDP 48210(随机生成,非默认 51820,降低被扫描概率)
模式
全隧道:手机 AllowedIPs = 0.0.0.0/0, ::/0,所有流量走服务器
DNS
客户端 8.8.8.8, 1.1.1.1(随隧道走,避免 DNS 泄漏)
认证
密钥对(非密码),服务端已开机自启 wg-quick@wg0

和家里 OpenClash / 旁路由的关系:这是第二套独立的出网通道,走腾讯云而不是家里宽带;家里那条只覆盖局域网设备,这条覆盖"在外面"的手机。

方案与决策

为什么需要它

家里用 ssh -D 动态转发做 SOCKS(配置在 ~/.ssh/config 的 Host vps-google 里,DynamicForward 127.0.0.1:1080)。它只能在本机 localhost 监听,手机在外面连不到;且代理本身没跑在公网 IP 上。要让手机在外面也用,必须在服务器公网侧跑一个对外监听的代理 / VPN。

候选方案对比

方案iOSAndroid安全性结论
WireGuard官方 App(需外区 ID)官方 App(免费)极强(密钥 + 全加密)✅ 选定:两端官方免费、一键、最省电稳定
Shadowsocks / XrayShadowrocket(付费)v2rayNG(免费)强(密码 + 加密)可分流,但 iOS 客户端要花钱
gost SOCKS5需支持 SOCKS 的客户端同上较弱(SOCKS 不加密)最简但长期不安全,弃用

关键判据

  • iOS 国区 App Store 下架了所有 VPN App(含 WireGuard、Shadowrocket,2017 起按工信部要求)。所以客户端必须用美区 / 港区 Apple ID 下载 WireGuard;装完可切回国区,App 留着。Android 直接下 APK,无此限制。
  • 安全性:只开放一个指定 UDP 端口,靠"腾讯云安全组 + 服务器 UFW"双重限制;不暴露其他服务;用强密钥而非密码。
  • 选 WireGuard 而非 OpenVPN:客户端更轻、更省电、配置就是一份极简文本,扫码即用。

改动记录

2026-11-02 · 初次部署

  • 在腾讯云 CVM 43.159.43.10(Ubuntu 22.04)安装 wireguard-tools + qrencode。
  • 开启 IPv4 转发;生成服务端 / 手机密钥对;写入 /etc/wireguard/wg0.conf 与 phone.conf。
  • 随机端口 UDP 48210;PostUp 注入 FORWARD 放行 + eth0 MASQUERADE;UFW 放行该端口。
  • 启动并设为开机自启;验证 wg show / 地址 / 监听 / NAT / FORWARD / 转发 全部正常。
  • 生成手机端二维码 PNG 与 phone.conf 供导入。

待办(用户侧):腾讯云控制台安全组放行 UDP 48210;手机用外区 Apple ID 装 WireGuard 后扫码导入。两项完成后手机即可经服务器出网。

踩坑记录

手机在外面连不上家里的 SOCKS 根因

现象:家里用 ssh -D 127.0.0.1:1080 出网好好的,手机在外面却用不了。

根因:动态转发的 SOCKS 只监听在"家里那台机器"的 localhost,外部到不了;且代理根本没跑在公网 IP 上。

解决:改为在服务器公网侧跑 WireGuard(本服务),手机直连服务器公网 IP。

iOS 国区 App Store 搜不到 WireGuard 限制

现象:国内 App Store 搜 WireGuard / Shadowrocket 都搜不到。

根因:2017 起苹果按工信部要求下架国区所有 VPN App,它们在美区 / 港区仍可用。

解决:切到美区 / 港区 Apple ID 下载(免费),装完可切回国区,App 保留。Android 直接下 APK 无此限制。

部署完手机仍连不上 —— 漏了腾讯云安全组 最常见

现象:服务端 wg show 正常、UFW 也放行了,但手机握手失败 / 超时。

根因:UFW 只是系统内防火墙;腾讯云还有一层网络安全组,默认入站是拒绝的,没在那里放行 UDP 端口,包根本进不来。

解决:云控制台 → 安全组 → 入站规则加 UDP:48210 允许。

scp 取回二维码报 Permission denied 已修

现象:scp ...:/tmp/phone-wg.png 报 Permission denied。

根因:搭建脚本以 root 生成 PNG,文件属主 root、权限 600,普通 ubuntu 用户读不到。

解决:sudo chmod 644 /tmp/phone-wg.png 后再取;或脚本里直接 cp 一份 644 到 /tmp。

➕ 新增一个服务 (模板)

以后家 Nas 上每部署一个新服务 (比如 AList、Immich、HomeAssistant…),就按下面复制一份结构。保持统一的四个小节:概览/方案与决策/改动记录/踩坑记录。

若这个服务的搭建过程复杂到「换台机器照着做会搭不出来」,再加一个可选的 <svc>-rebuild(搭建手册)小节,放在概览之后。要求:只写实测值、每步命令可直接复制、必带 IP 规划表 + 验证清单 + 回滚步骤。参考已有的 openwrt-rebuild。

做法

  1. 在<aside class="sidebar">的"部署的服务"下,复制 OpenWrt 那组<li>,改标题和 4 个href="#服务 id-xxx"。
  2. 在<div class="content">里复制一组<article id="openwrt-*">,把 id 前缀换成新服务 id,填内容。
  3. 侧栏链接的href必须和 article 的id一一对应 (支持#服务 id-小节直达与分享)。
<article id="服务 id-overview">
  <h2 class="sec">服务名</h2>
  <div class="breadcrumb">家 Nas · 部署的服务·服务名·概览</div>
  <p>一句话说明这服务干嘛的、跑在哪。</p>
</article>

约定:服务 id 用小写短横线 (如immich、home-assistant),小节固定为-overview / -plan / -changes / -pitfalls。

关于本站点

一个零依赖的自包含静态站点,用来沉淀家 Nas 上部署的各服务的:方案与决策、改动记录、踩过的坑。纯 HTML+CSS+JS,单文件,方便在 Nas 上长期留存。

怎么打开

  • 直接用浏览器打开index.html即可 (锚点链接在file://下也有效)。
  • 想用 http 访问:在目录里跑python3 -m http.server 8080,浏览器开http://<nas-ip>:8080。

交互

  • 右上角🌙/☀️切换深/浅色主题 (记忆在 localStorage)。
  • 侧栏点任意条目跳转;URL 带#openwrt-pitfalls这类锚点,可直接分享/收藏某一节。

内容规范 (建议)

  • 方案与决策:为什么这么做、否决了什么、关键判据。
  • 改动记录:日期 + 改了啥 (含命令/配置片段)+ 验证结果。
  • 踩坑记录:现象 → 根因→ 解决;带命令/日志原文最有价值。

建站日期:2026-10-31 · 第一个服务:OpenWrt 旁路由。