Linux 主机 Mihomo 搭建与 pon / poff 代理开关记录

最近为了让 Linux 主机能够更稳定地访问 GitHub 和部分外部服务,我重新整理了一套 Mihomo + pon / poff 的本机代理方案。

这套方案的重点不是“给全局网络开代理”,而是:只让 Linux 主机自己按需走代理,并且尽量不影响其他本地服务。

一、这篇文章要解决什么问题

我的目标比较明确:

  • Mihomo 常驻运行
  • 只监听本机回环地址
  • 不对局域网其他设备开放代理
  • 需要时一键打开当前 shell 代理
  • 不需要时一键关闭代理

最终效果如下:

1
2
3
4
命令行程序(git / curl / apt 等)
-> 127.0.0.1:7890
-> Mihomo
-> 代理节点

也就是说,这套方案主要服务于:

  • git
  • curl
  • apt
  • 终端里的其他联网程序

而不会主动影响:

  • 局域网其他设备
  • 本机对内服务
  • 不需要走代理的日常网络访问

二、准备条件

开始之前,需要具备这些前提:

  1. Linux 主机已经可以正常执行 Linux 命令
  2. 已经拿到一份可用的 Clash / Mihomo 配置
  3. 已经下载好 Mihomo 二进制,或者准备上传到服务器

我这里约定:

  • 二进制路径:
1
/usr/local/bin/mihomo
  • 配置目录:
1
/etc/mihomo

三、先清理旧代理残留

在正式配置之前,最好先把旧的代理环境变量和 Git 代理配置清掉。

在 Linux 主机执行:

1
2
3
4
env | grep -i proxy
git config --global --get http.proxy
git config --global --get https.proxy
grep -n 'proxy\|9999' ~/.bashrc ~/.profile ~/.zshrc 2>/dev/null

如果你之前残留过类似 127.0.0.1:9999 的旧代理配置,可以执行:

1
2
3
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY all_proxy ALL_PROXY
git config --global --unset http.proxy 2>/dev/null || true
git config --global --unset https.proxy 2>/dev/null || true

如果 ~/.bashrc 里还写着旧代理变量,例如:

1
2
export http_proxy=http://127.0.0.1:9999
export https_proxy=http://127.0.0.1:9999

建议一并删除或注释掉,然后重新加载:

1
source ~/.bashrc

四、安装 Mihomo 二进制

如果当前目录已经有 mihomo 二进制,可以直接安装:

1
2
3
sudo mkdir -p /etc/mihomo
sudo install -m 0755 ./mihomo /usr/local/bin/mihomo
/usr/local/bin/mihomo -v

确认能输出版本信息即可。

五、编写 Mihomo 配置文件

1. 创建规则目录

1
sudo mkdir -p /etc/mihomo/ruleset

2. 编辑主配置文件

1
sudo nano /etc/mihomo/config.yaml

这里我保留了原本订阅的基本结构,同时做了几个关键调整:

  • allow-lan: false:只给本机使用,不开放局域网访问
  • external-controller: 127.0.0.1:9091:避开常见的 9090 端口冲突
  • dns.enable: false:避免绑定 53 端口导致权限问题

配置示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info

external-controller: 127.0.0.1:9091
secret: "请改成一个强密码"

dns:
enable: false

proxies:
- name: Example-US-Node
type: vless
server: node.YOUR_DOMAIN
port: 443
uuid: 00000000-0000-0000-0000-000000000000
network: tcp
udp: true
tls: true
flow: xtls-rprx-vision
servername: YOUR_DOMAIN
reality-opts:
public-key: YOUR_PUBLIC_KEY_HERE
short-id: YOUR_SHORT_ID
client-fingerprint: chrome

proxy-groups:
- name: PROXY
type: select
proxies:
- Example-US-Node
- DIRECT

rule-providers:
reject:
type: http
behavior: domain
path: ./ruleset/reject.yaml
url: https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/reject.txt
interval: 86400

direct:
type: http
behavior: domain
path: ./ruleset/direct.yaml
url: https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/direct.txt
interval: 86400

proxy:
type: http
behavior: domain
path: ./ruleset/proxy.yaml
url: https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/proxy.txt
interval: 86400

private:
type: http
behavior: domain
path: ./ruleset/private.yaml
url: https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/private.txt
interval: 86400

apple:
type: http
behavior: domain
path: ./ruleset/apple.yaml
url: https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/apple.txt
interval: 86400

icloud:
type: http
behavior: domain
path: ./ruleset/icloud.yaml
url: https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/icloud.txt
interval: 86400

applications:
type: http
behavior: classical
path: ./ruleset/applications.yaml
url: https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/applications.txt
interval: 86400

lancidr:
type: http
behavior: ipcidr
path: ./ruleset/lancidr.yaml
url: https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/lancidr.txt
interval: 86400

cncidr:
type: http
behavior: ipcidr
path: ./ruleset/cncidr.yaml
url: https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/cncidr.txt
interval: 86400

rules:
- RULE-SET,reject,REJECT
- RULE-SET,private,DIRECT
- RULE-SET,applications,DIRECT
- RULE-SET,icloud,DIRECT
- RULE-SET,apple,DIRECT
- RULE-SET,direct,DIRECT
- RULE-SET,lancidr,DIRECT
- RULE-SET,cncidr,DIRECT
- RULE-SET,proxy,PROXY
- MATCH,PROXY

六、先做本地测试

正式挂 systemd 之前,先做两步验证。

1. 测试配置文件是否合法

1
sudo /usr/local/bin/mihomo -d /etc/mihomo -t

2. 前台启动验证代理是否可用

1
sudo /usr/local/bin/mihomo -d /etc/mihomo

另开一个终端执行:

1
2
curl -x http://127.0.0.1:7890 -I https://github.com
curl -x http://127.0.0.1:7890 -I https://raw.githubusercontent.com

如果能返回 200301302 这一类响应,通常就说明代理已经正常工作。

测试完成后,用 Ctrl + C 结束前台进程。

七、配置 systemd 常驻运行

编辑服务文件:

1
sudo nano /etc/systemd/system/mihomo.service

写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=mihomo Daemon
After=network.target NetworkManager.service systemd-networkd.service iwd.service

[Service]
Type=simple
Restart=always
ExecStartPre=/usr/bin/sleep 1s
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

加载并启动:

1
2
3
4
5
sudo systemctl daemon-reload
sudo systemctl enable mihomo
sudo systemctl start mihomo
sudo systemctl status mihomo --no-pager
journalctl -u mihomo -n 50 --no-pager

再确认监听端口:

1
ss -lntp | grep -E ':7890|:9091'

预期应看到:

  • 127.0.0.1:7890
  • 127.0.0.1:9091

如果监听地址是 0.0.0.0,就说明配置没有达到“仅本机使用”的目标,需要重新检查。

八、配置 pon / poff

1. 为什么不用普通脚本

如果想让代理变量立即作用于当前 shell,会有一个常见坑:

  • 普通脚本只能影响子进程
  • 不能直接修改当前 shell 的环境变量

所以这里更稳妥的做法是:

  • 写两个环境片段文件
  • 再在 ~/.bashrc 里定义 pon() / poff() 函数

2. 创建代理环境片段

1
2
mkdir -p ~/.proxy
nano ~/.proxy/pon.env

写入:

1
2
3
4
5
6
7
export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
export HTTP_PROXY=http://127.0.0.1:7890
export HTTPS_PROXY=http://127.0.0.1:7890
export all_proxy=socks5://127.0.0.1:7890
export ALL_PROXY=socks5://127.0.0.1:7890
echo "[proxy] ON -> 127.0.0.1:7890"

再创建关闭文件:

1
nano ~/.proxy/poff.env

写入:

1
2
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY all_proxy ALL_PROXY
echo "[proxy] OFF"

3. 在 ~/.bashrc 中添加函数

编辑:

1
nano ~/.bashrc

在文件末尾追加:

1
2
3
4
5
6
7
8
9
10
11
pon() {
source ~/.proxy/pon.env
}

poff() {
source ~/.proxy/poff.env
}

ptest() {
curl -x http://127.0.0.1:7890 -I https://github.com
}

使配置立即生效:

1
source ~/.bashrc

九、如何使用

开启代理:

1
pon

关闭代理:

1
poff

测试代理:

1
ptest

查看当前环境变量:

1
env | grep -i proxy

我的使用习惯是:

  • 平时默认 poff
  • 需要访问 GitHub、外部 API、模型服务时再 pon
  • 用完后再手动 poff

这样不会影响本机其他局域网服务。

十、Git 代理开关(可选)

如果还想单独控制 Git 的全局代理,也可以继续在 ~/.bashrc 中追加:

1
2
3
4
5
6
7
8
9
10
11
gpon() {
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890
echo "[git proxy] ON"
}

gpoff() {
git config --global --unset http.proxy 2>/dev/null || true
git config --global --unset https.proxy 2>/dev/null || true
echo "[git proxy] OFF"
}

重新加载:

1
source ~/.bashrc

以后就可以这样用:

1
2
3
gpon
git clone https://github.com/NousResearch/hermes-agent.git
gpoff

十一、常见问题

1. 127.0.0.1:9090 被占用

如果默认控制端口冲突,可以改成:

1
external-controller: 127.0.0.1:9091

2. listen udp :53: bind: permission denied

这通常是内置 DNS 尝试绑定 53 端口导致的,可以直接关闭:

1
2
dns:
enable: false

3. cache.db: permission denied

如果用普通用户前台启动,Mihomo 可能没有权限往 /etc/mihomo 写缓存。

解决思路:

  • 手工测试时使用 sudo
  • 正式运行交给 systemd

4. pon 执行了但环境变量没生效

这种情况基本就是把 pon 做成了普通脚本,而不是 shell function。

本篇文章使用 source ~/.proxy/pon.env 的方式,就是为了避免这个问题。

5. 规则文件下载失败

rule-providers 依赖从 GitHub 拉取规则,如果网络环境本身不通,第一次启动可能就会失败。

建议按下面顺序排查:

  • 先确认代理节点可用
  • 再用前台模式看日志
  • 没问题后再切换到 systemd 常驻

十二、最终验证清单

按顺序执行下面这些命令,基本就能确认整套方案是否可用:

1
2
3
sudo /usr/local/bin/mihomo -d /etc/mihomo -t
sudo systemctl status mihomo --no-pager
ss -lntp | grep -E ':7890|:9091'

然后继续验证 shell 代理:

1
2
3
4
5
pon
env | grep -i proxy
ptest
poff
env | grep -i proxy

如果结果符合预期,就说明:

  • Mihomo 已经常驻运行
  • Linux 主机本机代理可用
  • pon / poff 开关正常

十三、总结

这套方案最大的优点,是把“代理服务常驻”和“当前 shell 是否走代理”拆开了。

也就是说:

  • Mihomo 可以一直在后台运行
  • 但 shell 默认不带代理环境变量
  • 只有真正需要访问外网时才手动开启

这样既保留了灵活性,也尽量减少了对本机其他服务的干扰。

如果你的目标和我一样,是在 Linux 主机上做一个只给自己用、按需开启、尽量不影响内网服务的代理环境,那么这套方式会比较实用。