把透明代理下沉到小米万兆路由器之后,我终于不用在每台设备上追着代理跑了
最近把家里的透明代理架构折腾到了一个相当舒服的状态:小米万兆路由器 RC01 作为网关,路由器侧跑 ShellCrash + Mihomo Meta + TUN + fake-ip,再把 Sub-Store、subs-check 和一个轻量管理面板一起放在路由器的 USB 服务栈里。
简单说,人类终于不用每天在 Mac、Windows、手机、虚拟机、Docker、浏览器扩展之间问同一个哲学问题:
你现在到底走没走代理?
现在的答案变成了更网关化的一句:只要设备走这台路由器,它就进入同一套路由和规则系统。该直连直连,该代理代理,Cloudflare Tunnel、AI 服务、国外媒体、国内站点、开发工具这些东西尽量按它们真正的语义分组,而不是散落在每台设备自己的代理软件里。
最终架构
当前稳定下来的核心方案是:
LAN 设备
-> 小米万兆路由器 RC01
-> iptables / fwmark / policy routing
-> TUN 虚拟网卡
-> Mihomo Meta 按规则分流
-> DIRECT 或 PROXY
路由器侧主要组件:
- ShellCrash 负责拉起 Mihomo、接管透明代理模式和防火墙规则。
- Mihomo Meta 跑 TUN 模式,作为真正的策略引擎。
- fake-ip 负责把普通域名流量统一拢进可控路径。
- Sub-Store 负责订阅加工、规则拼装和配置生成。
- subs-check 负责节点筛选,但后来发现它也最容易把小路由器榨干。
- 一个轻量的路由器管理面板负责看状态、调直连规则、观察当前链路。
这套方案最舒服的地方不是“全局代理”四个字,而是把代理从设备问题变成了网关问题。设备侧不用装一堆客户端,不用每次新建虚拟机、换浏览器、开命令行工具时重新想一遍代理环境变量。
为什么最后是 TUN,不是 Redir 或 TProxy
这段是整个折腾里最有意思的部分。
Redir 很好理解:主要靠 NAT REDIRECT 把 TCP 流量扔给 Mihomo。它简单,对网页够用,但 UDP、QUIC、IPv6、直接 IP 等场景都容易露出边界。放在今天的家庭网络里,它更像“能用的老办法”,不是我想要的完整网关接管。
TProxy 理论上很优雅:iptables mangle 表捕获 TCP/UDP,保留原始目标地址,再通过策略路由交给透明 socket。纸面上它很适合透明代理。
但在这台小米官方固件上,TProxy 成了一个很漂亮的悬案:模块在,规则计数会涨,fwmark 和 table 也对,路由查询也显示应该投递到本地 lo,可是应用层就是收不到透明连接。后来还写了一个最小 transparent socket probe 验证:直接访问 probe 没问题,把 LAN 流量 TProxy 给它就没日志,客户端超时。
这个结果很有教育意义。透明代理排障不能只看“iptables 计数增长了”,还要验证包有没有真的投递到应用。最后我基本把问题从 Mihomo 配置、节点、订阅和 ShellCrash listener 里剥离出去了,更像是官方固件桥接、iptables、TPROXY 投递路径里的坑。
所以最后落地到 TUN。
TUN 没有 TProxy 那么“教科书式透明”,但是它在这台路由器上实测稳定,TCP/UDP 覆盖完整,配合 fake-ip 后客户端体验非常好。它的代价也很清楚:国内直连流量也会先进 Mihomo 再 DIRECT 出去,多一层软件处理,硬件转发能力不会像纯 NAT 那样完全发挥。
我现在更愿意接受这个代价。因为家庭网络里最贵的不是多一点 CPU,而是“不知道为什么这台设备今天又不一样”。
IPv6:最容易误判的一层
之前有一轮很典型的误会:Mac 上看起来 IPv6 “又不行了”,但对比同网段的另一台 Linux 机器和外部主机后,结论不是“Mac 没 IPv6”。
真实情况更绕:
- Mac 有全局 IPv6 地址。
- 默认 IPv6 路由也存在。
- 外部机器甚至可以从公网 IPv6 SSH 到 Mac。
- 但是本机
ping6、同 LAN IPv6、TCP/HTTPS 表现会不一致。
原因在路由器侧:LAN 的 TCP/UDP IPv6 被透明代理路径接管,ICMPv6 又不是同一种处理方式,再加上防漏规则,最后看起来就像“IPv6 坏了”。
现在的处理策略是比较保守的:
- 普通域名不返回 AAAA,优先走 fake IPv4。
- IPv6 透明劫持打开,防止应用硬连 IPv6 字面量时直接漏出去。
- LAN 出来的 IPv6 如果不能走 TUN,就不要让它直接从 WAN 漏掉。
这不是在追求“所有 IPv6 行为都像裸网一样自然”,而是在追求“只要它经过这个网关,就不要在你没意识到的时候绕过策略系统”。
直连不是一个词能讲清楚
还有一个很值得记下来的边界:有些管理界面里写“强制直连”或“direct suffix”,但实际语义可能只是:
这个域名解析出的 IP 被放进直连 ipset,流量进了路由器代理体系之后会被 DIRECT。
这和“这个域名完全不进入 Mihomo”不是一回事。
这个差异在 Cloudflare Tunnel 上很明显。Cloudflare Tunnel 这种控制面连接,如果因为代理链路绕了一圈导致 530 或连通性异常,人会自然想说“把 argotunnel.com 加到直连”。但真正要验证的是 live connections 里它最后是不是 DIRECT,以及这个 DIRECT 是在什么阶段发生的。
后来我们把 argotunnel.com、cfargotunnel.com 这类控制面域名放到更明确的位置,并把 Tailscale 看成可选备用管理通道,而不是主架构的一部分。公网 IPv6 适合 SSH,Cloudflare Tunnel/Access 适合管理页面,Tailscale 可以留作私有控制面的保险,不必每层都叠满。
规则分组要按现实语义,不要按历史包袱
另一类折腾来自规则源。
最开始沿用了常见 Clash 时代的规则桶,比如 ProxyMedia、国外媒体。名字听起来很清楚:YouTube、Netflix、Disney、Spotify 这种才叫媒体嘛。
结果一看实际规则,AI、OpenAI、Cursor、Cloudflare challenge、Auth0、Stripe、Sentry 之类东西也被塞在类似“国外媒体”的桶里。于是你打开 Cloudflare 验证、ChatGPT 登录、Cursor 或某些开发者服务,Mihomo 里显示它命中了 ProxyMedia -> 国外媒体。
这不是单个域名错了,而是抽象过时了。
现代规则至少应该把这些东西分开:
- AI / OpenAI / Claude / Gemini / Cursor / Copilot
- 媒体 / 视频 / 音乐 / 流媒体
- 开发者服务 / 验证 / 登录 / 支付 / 观测基础设施
- 通用代理
- 国内直连
所以后面更倾向于 Mihomo-native 的规则源,比如 MetaCubeX、DustinWin、blackmatrix7 这类更明确区分 AI 和 media 的体系。不是为了“追新”,而是为了让策略组名字和真实流量语义一致。否则你调规则的时候脑子里会一直有个小问号:这个国外媒体,怎么还负责给我过 Cloudflare 验证?
Sub-Store 和 subs-check:自动化很爽,但源头要干净
把订阅处理链路放到路由器上以后,另一个感受是:自动化越强,源污染越可怕。
subs-check 有一阵子把路由器压力打得很高,甚至 OOM 杀掉了 CrashCore。最开始很容易以为是内存参数不够,或者并发太大。后来才发现一个更根本的问题:某些“远程订阅列表”其实是完整 Mihomo 配置,被错误地当成 URL 列表解析,于是 proxy-providers、rules、dns 这些字段都变成了奇怪的待抓取链接,候选规模直接爆炸。
后来做了几件事:
- 清掉 subs-check 启动时继承的代理环境变量。
- 用它自己的
system-proxy/github-proxy配置,而不是让整个进程继承路由器代理环境。 - 降低订阅抓取并发。
- 禁用那些不是原始订阅列表的完整配置源。
- 保留备份,并且只改 Sub-Store 的配置对象,再用生成结果验证,不把生成的 YAML 当源文件回写。
这件事的经验很朴素:小路由器不是不能跑自动化,但不能让它吃垃圾输入。自动化管道里,源头干净比后面调十个内存参数更重要。
这套架构现在好在哪里
现在我最满意的不是某个单点,而是整个系统终于形成了一个清楚的边界:
- 设备侧尽量简单,连上网关就进入统一策略。
- 路由器侧是唯一主要控制面。
- SSH / 文件传输优先走公网 IPv6 + key-only。
- Web 管理面可以用 Cloudflare Tunnel / Access 保护。
- Tailscale 留作备用,不强行塞进主数据路径。
- Mihomo 的 live connections 和 provider 状态是判断真相的依据,不靠浏览器感觉猜。
- 规则源从“历史上大家都这么叫”转向“这个流量现实中到底是什么”。
它当然不是完美到没有代价:
- TUN 会让直连流量也多走一层用户态处理。
- 重启后有几十秒到一两分钟的恢复窗口。
- ICMP、GRE、ESP 这类非 TCP/UDP 协议不是它的目标。
- 路由器本机系统流量和 LAN 客户端流量不是一回事。
- 硬编码 DoH/DoT、直接 IP、应用自带 VPN 仍然会削弱域名规则的可解释性。
但这些边界现在都是清楚的。清楚就很好,因为网络问题最折磨人的往往不是“不支持”,而是“看起来支持,实际上不是你以为的那个支持”。
我会怎么向别人推荐
如果你也想把代理下沉到路由器,我现在会这样建议:
- 先做一个可回滚的基线,不要一上来追求最优雅的 TProxy。
- 如果 Redir 能满足你,只代理网页流量,那就别把系统复杂度拉满。
- 如果你要覆盖 UDP、QUIC、Docker、命令行工具、各种客户端,TUN 更值得优先验证。
- IPv6 一定要单独设计防漏,不要靠“现在好像没默认路由”这种偶然状态。
- 管理面、订阅面、节点测试面和真实流量面要分开看。
- 规则源要读实际内容,不要相信文件名。
- 小路由器上跑自动化可以,但要控制源规模、并发和内存。
- 最后一定看 live connections。它比“我感觉这个网站应该走了某组”可靠太多。
这次折腾的最大收获,大概就是把家庭网络从“每台设备各自修行”变成了“网关统一治理”。它不只是代理配置的变化,更像把家里的网络从手工艺品推进到了一个小型基础设施。
而且说实话,这种感觉很爽:打开一台新机器,不用先装代理客户端,不用翻半天环境变量,不用怀疑浏览器和终端是不是两个世界。它只要接入这张网,就进入同一套规则宇宙。
这大概就是我折腾小米万兆路由器透明代理到现在,觉得终于“值得”的原因。