Mihomo 踩坑实录:从 systemd 服务到 TUN 模式的全套折腾
我的立场:
- 我对代理工具的态度是「又爱又恨」:没有它工作没法开展,配置错了又让人想砸键盘
- 我的观点受限于:只在公司内网环境折腾过,家用宽带可能完全是另一回事
- 如果一年后我发现错了,最可能错在:「所有内网问题都能用 fake-ip-filter 解决」这个假设
我必须承认的偏见: 我对 systemd 有种莫名的信任,觉得「写成服务就稳了」。这可能只是心理安慰,实际上服务崩了我经常后知后觉。
为什么又写这个
去年写过一篇 systemd 服务的教程,当时只懂皮毛,以为 systemctl enable 就是终点。现在回头看,那篇文章太「正确」了,全是教科书式的命令,没有血和泪。
这篇不一样。这是我在公司内网折腾 Mihomo 的真实记录,包括三次完全搞崩网络、两次重装配置文件、以及无数次对着日志骂娘的过程。
如果你正在用 Clash/Mihomo,或者打算把它做成系统服务,这篇可能帮你省几个小时。
先搞清楚你在用什么
很多人(包括三个月前的我)对代理的理解停留在「App 里填个端口」。这没错,但 Mihomo 的 TUN 模式完全是另一回事。
普通代理:App 主动设置代理端口 → Mihomo → 节点 → 目标。只有支持代理的 App 才生效。
TUN 模式:App → 内核网络栈 → 虚拟网卡 Meta(198.18.0.1) → Mihomo → 节点 → 目标。所有流量强制经过,App 无感知。
TUN 模式的好处是「全局生效」,坏处是「全局都可能出问题」。我就是被这个「全局」坑了。
文件结构:别动 runtime.yaml
Mihomo 的配置分三层,这个设计很巧妙,但新手容易懵:
config.yaml # 主配置(节点、规则,从订阅获取)mixin.yaml # 覆盖配置(TUN、DNS 等个性化设置) ↓ 合并runtime.yaml # 实际运行配置(每次重启时生成,不要手动改这个)合并命令我写成 alias 了:
alias mihomo-restart='echo "你的密码" | sudo -S bash -c "/opt/clash/bin/yq eval-all ". as \$item ireduce ({}; . *+ \$item)" /opt/clash/config.yaml /opt/clash/mixin.yaml > /opt/clash/runtime.yaml && systemctl restart mihomo"'血的教训:我手动改过 runtime.yaml,重启服务后被覆盖,当场愣住。
systemd 服务:不只是开机自启
之前的文章只讲了怎么写 service 文件,但没讲为什么要用 root 跑 Mihomo。
答案很简单:TUN 模式需要创建虚拟网卡,普通用户没权限。
我的 /etc/systemd/system/mihomo.service:
[Unit]Description=Mihomo DaemonAfter=network.target
[Service]Type=simpleUser=rootExecStart=/opt/clash/bin/mihomo -f /opt/clash/runtime.yamlRestart=on-failureRestartSec=5StandardOutput=journalStandardError=journal
[Install]WantedBy=multi-user.target几个细节:
Restart=on-failure很重要,Mihomo 崩溃后自动重启- 日志走 journal,用
sudo journalctl -u mihomo -f看实时输出 - 不要用
network-online.target,某些系统上它会拖慢启动
大坑:TUN 开启后 GitLab 挂了
这是我折腾最久的问题。
现象:TUN 关闭可以访问 gitlab.mychery.com,TUN 开启后超时。
排查过程:
看日志发现 DIRECT 出站超时,IP 是 115.120.50.150。但公司的 GitLab 明明是内网服务啊?
# TUN 开启时,mihomo 用公共 DNS 解析:nslookup gitlab.mychery.com 114.114.114.114→ 115.120.50.150 (公网IP,内网不可达)
# TUN 关闭时,系统用公司内网 DNS 解析:nslookup gitlab.mychery.com→ 10.145.229.67 (内网IP,可达)恍然大悟:Mihomo 的 nameserver 只配了公共 DNS,解析内网域名得到公网 IP,而公网 IP 从公司内网根本走不通。
这就像是让外地导航给北京人指路,它当然会告诉你走高速,但你要去的其实是胡同里的老院子。
解决方案:fake-ip-filter 和 nameserver-policy
我查到了公司的内网 DNS 地址:
resolvectl status | grep "DNS Server"# → 10.1.4.141 10.1.4.142然后在 mixin.yaml 里加了这两段:
dns: enable: true enhanced-mode: fake-ip fake-ip-filter: - "*.mychery.com" # 内网域名不走 fake-ip,返回真实 IP - "gitlab.mychery.com" nameserver-policy: "*.mychery.com": [10.1.4.141, 10.4.1.142] # 内网域名专用内网 DNS nameserver: - 10.1.4.141 - 10.1.4.142 - 114.114.114.114 - 8.8.8.8fake-ip-filter 的作用:让 mychery.com 返回真实 IP 而不是假 IP,这样 DIRECT 规则才能拿到内网 IP 直连。
nameserver-policy 的作用:mychery.com 的 DNS 查询专门走内网 DNS,保证解析到内网 IP。
规则里还要加:
rules: - DOMAIN-SUFFIX,mychery.com,DIRECT # 内网域名直连 ...三套组合拳打下来,GitLab 终于能访问了。
等等,fake-ip 到底是什么
写到这我突然意识到,很多人可能不知道 fake-ip 是啥。
简单说:TUN 收到的是 IP 数据包,但规则按域名写,需要还原「IP → 域名」的映射。
传统 DNS:域名 → 真实 IP fake-ip:域名 → 分配假 IP(198.18.0.x) + 记录映射表 → 收到包时查表还原域名 → 匹配规则
所以 DNS 配置只在 TUN 开启时有意义,TUN 关闭时系统走 systemd-resolved,Mihomo 的 DNS 配置完全不参与。
dns-hijack: any:53 是用来在内核层面拦截 DNS 查询的。Mihomo 自身监听哪个端口无所谓,我配的是 1053,这样 TUN 关闭时不会占用 53 端口干扰 systemd-resolved。
我的最终 mixin.yaml
external-controller: "0.0.0.0:9090"external-ui: publicsecret:allow-lan: falseauthentication:
tun: enable: true stack: system auto-route: true auto-redir: true auto-redirect: true auto-detect-interface: true dns-hijack: - any:53 - tcp://any:53 strict-route: true
dns: enable: true enhanced-mode: fake-ip fake-ip-filter: - "*.mychery.com" - "gitlab.mychery.com" nameserver-policy: "*.mychery.com": [10.1.4.141, 10.1.4.142] nameserver: - 10.1.4.141 - 10.1.4.142 - 114.114.114.114 - 8.8.8.8这个配置在我电脑上能跑,但不保证在你那也能跑。
我现在还在困惑的事
-
strict-route 到底开不开? 有人说开了能解决某些泄漏问题,但好像也会出问题,我还没搞懂。
-
system stack 和 gvisor 哪个好? 我用的是 system,但 gvisor 据说更隔离。没测试过性能差异。
-
为什么有时候重启服务后要等 30 秒网络才通? 像是有个缓存没清,但不知道在哪。
如果你要折腾,记得这些命令
# 重新合并配置并重启mihomo-restart
# 查看实时日志(排查流量命中规则)sudo journalctl -u mihomo -f
# 查看公司 DNSresolvectl status | grep "DNS Server"
# 测试域名解析nslookup gitlab.mychery.com
# 查看路由ip route get 115.120.50.150结语
写完这些,我感觉像是把一个混乱的战场整理成了地图。但地图永远不是战场本身。
如果你也遇到「TUN 开启后内网服务挂了」的问题,希望这篇能帮你少走弯路。如果还是没解决,欢迎来骂我(但我不保证能帮你修好)。
一年后我回头看这篇,可能会觉得「怎么连这个都没搞懂」,但那也是好事,说明我又进步了。
你现在卡在哪个坑? 还是已经顺利上岸了?