Featured image of post 拋棄所謂「客户端」,直接使用 Mihomo 核心

拋棄所謂「客户端」,直接使用 Mihomo 核心

與其依賴這些不盡如人意的 Mihomo 客户端,不如自己動手,直接自己編寫、管理配置檔案交給核心啓動,而不是依賴「黑箱」一般的各種「客户端」,不僅更純淨、更穩定,而且更可控。

自從開始使用 Clash/Mihomo,我和大多數人一樣選擇了基於其核心的圖形化客户端——圖方便。實際上,這些 Mihomo 客户端本質上都差不多:核心都是同一個,他們主要負責提供一個易用的介面、管理配置檔案、訂閲更新,以及 GUI 下的系統代理設定。基於這一點,我認為判斷一個 Mihomo 客户端優劣的關鍵,便是看它的「覆寫」功能做得如何。每一個透過客户端下載或者訂閲的配置檔案,都會經過一系列「覆寫」過程,比如更換 mixed-port、新增 sniffer 配置等,最後生成用於啓動 Mihomo 核心的最終配置。

然而,並非所有客户端都能勝任這一核心工作。比如 ShellCrash,其覆寫功能經常莫名其妙出岔子,説到底還是實現得太粗糙。如果連覆蓋和修改配置都做不好,這種客户端實在難稱合格。

與其依賴這些屎一樣的不盡如人意的 Mihomo 客户端,不如自己動手,直接自己編寫、管理配置檔案交給核心啓動,而不是依賴「黑箱」一般的各種「客户端」,不僅更純淨、更穩定,而且更可控。

需要的預備知識

  • 基本的 Linux 操作
  • 知道如何使用 CLI 編輯器,如 nano
  • 已經搭建 Substore(可選)
  • 預設使用 root 用戶操作,非 root 用戶請自行注意提權

安裝 Mihomo 核心

對於基於 Debian 的分支,可以使用預編譯的.deb包安裝,對於其他使用systemd的系統,下載編譯好的二進位制檔案,重新命名為mihomo,放到/usr/local/bin

1
2
curl -o /usr/local/bin/mihomo <下載連結>
chmod +x /usr/local/bin/mihomo

然後新建/etc/systemd/system/mihomo.service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[Unit]
Description=mihomo Daemon, Another Clash Kernel.
After=network.target NetworkManager.service systemd-networkd.service iwd.service

[Service]
Type=simple
LimitNPROC=500
LimitNOFILE=1000000
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
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

使用systemctl daemon-reload重新載入systemd,此時還沒有配置檔案,不能直接啓動核心,但可以使用systemctl enable mihomo讓核心開機自啓,方便後續使用。

配置檔案

核心啓動時會載入/etc/mihomo/config.yaml,沒有了黑箱一樣的礙事「客户端」,配置檔案可以隨心所欲的定製。部分機場會下發完整的配置檔案,直接用curl下載即可。

對於訂閲的管理,我目前使用 Substore,我之前分享過「最速 Substore 訂閲管理指南」,可以直接蔘考這篇文章來定製自己的訂閲。為了實現純核心啓動,現在我的 Substore JS 格式覆寫已經加入了full引數,可以生成完整的配置檔案,包括各種埠設定、統一延遲和外部控制器等,開箱即用。

在 Substore 配置完成以後便可以下載配置檔案並啓動核心:

1
2
sudo curl -o /etc/mihomo/config.yaml 配置檔案連結
sudo systemctl start mihomo

自定義覆寫

我的配置檔案不能滿足你的所有需求?沒問題!你可以自己新增覆寫,想要什麼加什麼。

我用過各種覆寫規則,後來也開始自己從零開始編寫覆寫規則,即使使用自己的覆寫規則能滿足 99% 的場景,但有極個別的域名還是會在規則中有遺漏,更不用説用別人寫的覆寫規則了。這些遺漏的規則大多是形如我伺服器的非標準埠 SSH 的前置代理等相對隱私的規則,直接上傳到 Github 公開顯然不太合適,那麼在自定義規則的基礎上再新增覆寫就成了唯一的選擇。

好在 Substore 可以新增多個指令碼操作,只需要在生成配置檔案時額外新增一個指令碼操作就能解決我們的問題。

1
2
3
4
function main(config) {
  config["rules"].unshift("DOMAIN-SUFFIX,xxx,DIRECT")
  return config
}

需要注意的是覆寫rules時必須要使用.unshift()放在最前面,而不是用.push()放到最後面,因為放在MATCH後面的規則是永遠都匹配不到的。

自定義配置檔案

完全不喜歡我的覆寫規則?不會用/不喜歡用 Substore?沒問題!你也可以蔘考 Mihomo 文件,自己從頭開始手搓配置檔案:

 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
mode: rule
mixed-port: 7890
redir-port: 7892
tproxy-port: 7893
allow-lan: true
log-level: info
ipv6: true
external-controller: 127.0.0.1:8000
# secret: yoursecret
unified-delay: true
routing-mark: 7894
tcp-concurrent: true
disable-keep-alive: true # 推薦在給移動裝置代理時啓用,可以解決待機異常耗電的問題

dns:
  # 你的 DNS 配置

sniffer:
  # 你的域名嗅探配置

geodata-mode: true
geox-url:
  # 自定義 Geodata 檔案 URL

proxy-providers:
  # 你的機場訂閲

rule-providers:
  # 外部規則

rules:
  # 分流規則

proxy-groups:
  # 自定義代理分組

管理面板

管理面板可以根據個人喜好選擇,以 Zashboard 為例,我在使用 mihomo 自帶的external-ui時遇到了一些莫名其妙的問題,所以乾脆直接執行一個 Docker 容器,畢竟這東西就真的只是一個 Web 面板,只要確保核心 API 使用 HTTP 時,Web 面板也使用 HTTP 即可,如果此時 Web 面板使用 HTTPS,則會因為 CORS 策略問題無法連線。

1
2
3
$ mkdir zashboard && cd zashboard
$ nano compose.yml
$ docker compose up -d

compose.yml內容:

1
2
3
4
5
6
services:
  zashboard:
    image: ghcr.io/zephyruso/zashboard:latest
    ports:
      - "8899:80"
    restart: "unless-stopped"

自動維護

核心已經執行起來,自動更新訂閲這種功能怎麼實現?

答曰:自行編寫一個 Shell 指令碼,配合 Crontab 即可實現自動更新訂閲的功能。例如我希望每天凌晨 3 點自動更新訂閲並重啓服務,遂編寫/etc/mihomo/auto_update.sh

 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
#!/bin/bash

# === 配置資訊 ===
CONFIG_URL=""
CONFIG_PATH="/etc/mihomo/config.yaml"
BACKUP_DIR="/etc/mihomo"
BACKUP_PREFIX="config.yaml"
MAX_BACKUPS=7
TMP_PATH="/tmp/config.yaml.tmp"
LOG_FILE="/var/log/mihomo_update.log"

# === 日誌 ===
log() {
    echo "$(date '+%F %T') $1" | tee -a "$LOG_FILE"
}

# === 備份現有配置,並自動清理舊備份 ===
backup_config() {
    if [ -f "$CONFIG_PATH" ]; then
        backup_file="$BACKUP_DIR/${BACKUP_PREFIX}.$(date '+%Y%m%d_%H%M%S').bak"
        cp "$CONFIG_PATH" "$backup_file"
        log "配置檔案已備份到 $backup_file"
        # 清理多餘的備份,只保留最新的 $MAX_BACKUPS 個
        old_backups=$(ls -1t $BACKUP_DIR/${BACKUP_PREFIX}.*.bak 2>/dev/null | tail -n +$(($MAX_BACKUPS+1)))
        for f in $old_backups; do
            rm -f "$f" && log "已刪除舊備份 $f"
        done
    else
        log "未找到現有配置檔案,無需備份"
    fi
}

# === 下載新配置 ===
download_config() {
    log "開始下載新配置..."
    curl -fsSL -o "$TMP_PATH" "$CONFIG_URL"
    if [ $? -ne 0 ]; then
        log "下載配置失敗,請檢查網絡或地址"
        return 1
    fi
    # 基本校驗:檢測檔案體積
    if [ ! -s "$TMP_PATH" ]; then
        log "下載檔案為空,停止更新"
        return 2
    fi
    log "配置下載完成"
    return 0
}

# === 更新配置檔案 ===
replace_config() {
    mv "$TMP_PATH" "$CONFIG_PATH"
    log "配置檔案已更新"
}

# === 重啓 mihomo 服務 ===
restart_service() {
    systemctl restart mihomo
    if [ $? -eq 0 ]; then
        log "mihomo 服務已重啓"
    else
        log "mihomo 服務重啓失敗,請手動檢查"
    fi
}

main() {
    backup_config

    download_config
    DL_STATUS=$?
    if [ "$DL_STATUS" -ne 0 ]; then
        log "操作終止:配置檔案未更新,保留原有配置"
        exit 1
    fi

    replace_config

    restart_service

    log "=== 更新流程完成 ==="
}

main "$@"

使用crontab -e編輯 Crontab,設定如下 Crontab 即可在每天凌晨三點自動更新配置:

1
0 3 * * * /etc/mihomo/update_config.sh

防火牆

手動配置防火牆把流量劫持到 Mihomo 核心其實並不是什麼難事,我之前在「從入門到進階:Tailscale + ShellCrash 異地組網和科學上網」(以下簡稱「Tailscale 那篇文章」中的兩個小節中提到過具體的操作方法,這裏只提操作,不做解説。

根據我的情況,我需要把tailscale0上的網絡卡的所有流量劫持到 Mihomo 核心,其它的情況(例如本機代理)操作也大同小異。如果只是想劫持 TCP 流量,那麼用iptables的 REDIRECT 功能已經足夠,但若還想劫持 UDP、QUIC 等流量,則必須用到 Tproxy。最後,不要忘了 IPV6。

以下是我的防火牆配置:

 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
# 建立自定義鏈
iptables -t mangle -N MIHOMO

# 根據自己的需要忽略本地流量
iptables -t mangle -A MIHOMO -d 127.0.0.1/8 -j RETURN
iptables -t mangle -A MIHOMO -d 100.64.0.0/10 -j RETURN
iptables -t mangle -A MIHOMO -d 192.168.1.0/24 -j RETURN
iptables -t mangle -A MIHOMO -d 172.17.0.0/16 -j RETURN

# mark UDP 和 TCP 到代理
iptables -t mangle -A MIHOMO -p tcp -j TPROXY --on-port 7893 --tproxy-mark 233
iptables -t mangle -A MIHOMO -p udp -j TPROXY --on-port 7893 --tproxy-mark 233

# 介面跳轉
iptables -t mangle -A PREROUTING -i tailscale0 -j MIHOMO

# 路由表配置
echo "233 mihomo" | tee -a /etc/iproute2/rt_tables
ip rule add fwmark 233 lookup mihomo
ip route add local 0.0.0.0/0 dev lo table mihomo

# IPv6
# 建立鏈
ip6tables -t mangle -N MIHOMO6

# 跳過本地地址
ip6tables -t mangle -A MIHOMO6 -d ::1/128 -j RETURN
ip6tables -t mangle -A MIHOMO6 -d fd7a:115c:a1e0::/48 -j RETURN

# 標記 TCP/UDP
ip6tables -t mangle -A MIHOMO6 -i tailscale0 -p tcp -j TPROXY --on-port 7893 --tproxy-mark 233
ip6tables -t mangle -A MIHOMO6 -i tailscale0 -p udp -j TPROXY --on-port 7893 --tproxy-mark 233

# 介面跳轉
ip6tables -t mangle -A PREROUTING -i tailscale0 -j MIHOMO6

# 路由表配置
echo "233 mihomo" | tee -a /etc/iproute2/rt_tables
ip -6 rule add fwmark 233 lookup mihomo
ip -6 route add local ::/0 dev lo table mihomo

大多數系統預設不會儲存防火牆規則,關於規則持久化的內容我已經分別在 Tailscale 那篇文章的「路由規則持久化」和「iptables 規則持久化」兩小節做了詳細説明,直接蔘考即可。

本機代理怎麼配置?

大多數代理軟件預設的配置(其實就是 ShellCrash 的預設配置)是 REDIRECT,用它也基本能滿足大多數需求,REDIRECT 的示例如下:

1
2
3
4
5
# IPv4 劫持 eth0
iptables -t nat -A PREROUTING -i eth0 -p tcp -j REDIRECT --to-ports 7892

# IPv6 劫持 eth0
ip6tables -t nat -A PREROUTING -i eth0 -p tcp -j REDIRECT --to-ports 7892

其中 7892 要和 Mihomo 配置中的redir-port一致,eth0就是想要劫持的 Interface,相比 Tproxy 那可真是簡單太多了。

實際上我個人並不喜歡用把本機所有流量都劫持到防火牆,我更喜歡在需要時直接透過環境變數、proxy-chains和各種軟件自帶的代理配置把流量指向 Mihomo 核心,例如想讓 Docker 走代理,則可以直接編輯 Docker 的/etc/docker/daemon.json來指定代理,而不是直接一股腦把網絡卡上的所有流量都劫持到代理:

1
2
3
4
5
6
7
{
  "proxies": {
    "http-proxy": "http://127.0.0.1:7890",
    "https-proxy": "http://127.0.0.1:7890",
    "no-proxy": "127.0.0.0/8"
  }
}

這麼折騰,何必呢?用客户端不香嗎?

別問,問就是玩虛空終端玩的。