Linux 部署 預計閱讀 14 分鐘

Linux 命令列部署 Clash 核心:systemd 服務與 TUN 權限設定

在無桌面環境的 Linux 上以命令列執行 Clash 核心,涵蓋二進位檔部署、systemd 服務、TUN 權限、開機自動啟動與日誌排錯,逐步建立穩定常駐方案。

部署架構與執行邊界

Linux 伺服器通常不需要圖形化客戶端。較穩定的做法是讓 mihomo 核心以獨立系統服務執行,由 systemd 負責啟動、重新啟動、權限收斂與日誌收集。設定檔放在 /etc/mihomo/,可寫入的執行資料放在 /var/lib/mihomo/,二進位檔固定於 /usr/local/bin/mihomo。如此一來,更新程式、修改設定與清理快取時不會彼此混在一起。

本文以 Ubuntu 24.04.2 LTS、Debian 12.10、systemd 255/252 與 mihomo 1.19.10 作為參考環境。不同發行版的套件管理指令可能不同,但 systemd 服務、TUN 裝置與 Linux capabilities 的處理思路一致。若使用原始 Clash 核心,部分 TUN、DNS 與規則提供者參數可能不存在,建議先執行版本指令確認實際核心類型。

uname -m
/usr/local/bin/mihomo -v
systemctl --version
ip -Version

uname -m 常見結果包括 x86_64aarch64armv7l;下載二進位檔時必須符合 CPU 架構。版本輸出應清楚顯示 mihomo 版本與建置資訊;若指令直接出現「Exec format error」,通常是架構選錯,而不是檔案權限問題。

路徑 用途 建議權限
/usr/local/bin/mihomo 固定的核心可執行檔 root:root 0755
/etc/mihomo/config.yaml 主要設定、代理與規則入口 root:clash 0640
/var/lib/mihomo/ Geo 資料、規則快取與執行資料 clash:clash 0750
/etc/systemd/system/mihomo.service systemd 服務單元 root:root 0644

安裝二進位檔與獨立服務使用者

以下操作假設已取得符合目前 CPU 架構的 mihomo 可執行檔,並暫存為 /tmp/mihomo。先安裝至固定路徑,再建立無法登入的系統使用者。使用獨立使用者可避免服務長期以 root 身分執行,也能讓設定檔中的訂閱網址、控制器密鑰與代理資訊僅限指定使用者群組讀取。

sudo install -o root -g root -m 0755 /tmp/mihomo /usr/local/bin/mihomo

sudo useradd \
  --system \
  --home-dir /var/lib/mihomo \
  --create-home \
  --shell /usr/sbin/nologin \
  clash

sudo install -d -o root -g clash -m 0750 /etc/mihomo
sudo install -d -o clash -g clash -m 0750 /var/lib/mihomo

/usr/local/bin/mihomo -v

若系統已存在名為 clash 的使用者,useradd 會回傳使用者已存在;此時使用 id clash 檢查其家目錄與使用者群組即可。服務使用者不需要密碼,也不需要加入 sudodocker 等額外群組。

寫入最小可執行設定

以下設定先建立本機 mixed 代理連接埠、REST 控制器與 TUN 接管。mixed-port: 7890 同時接受 HTTP 與 SOCKS5 連線;控制器限制在迴路位址的 9090 連接埠;DNS 使用非特權連接埠 1053,避免額外申請繫結 53 連接埠所需的權限。

mixed-port: 7890
bind-address: 127.0.0.1
allow-lan: false
mode: rule
log-level: info
ipv6: false

external-controller: 127.0.0.1:9090
secret: "請替換為足夠長的隨機控制器密鑰"

profile:
  store-selected: true
  store-fake-ip: true

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  strict-route: true
  dns-hijack:
    - any:53
    - tcp://any:53

dns:
  enable: true
  listen: 127.0.0.1:1053
  ipv6: false
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  default-nameserver:
    - 223.5.5.5
    - 1.1.1.1
  nameserver:
    - https://223.5.5.5/dns-query
    - https://1.1.1.1/dns-query

proxies: []

proxy-groups:
  - name: PROXY
    type: select
    proxies:
      - DIRECT

rules:
  - GEOIP,CN,DIRECT
  - MATCH,PROXY

這份設定可用來驗證程序、連接埠與 TUN 是否正常,但 PROXY 群組目前只有 DIRECT,不會產生實際代理轉送。正式使用時,應將既有訂閱轉換為 mihomo 可識別的設定,或透過 proxy-providers 引用服務商提供的相容訂閱。不要把網頁訂閱網址直接當成單一代理節點填寫。

儲存後收緊權限,並以服務使用者身分執行語法檢查。-d 指定執行資料目錄,-f 明確指定設定檔;分開指定後,mihomo 下載的 Geo 資料與規則快取不會寫入 /etc

sudo chown root:clash /etc/mihomo/config.yaml
sudo chmod 0640 /etc/mihomo/config.yaml

sudo -u clash /usr/local/bin/mihomo \
  -t \
  -d /var/lib/mihomo \
  -f /etc/mihomo/config.yaml

設定 TUN 裝置與 capabilities

TUN 模式需要核心提供 /dev/net/tun,程序也必須具備修改路由、策略路由與虛擬網路介面的能力。對應的關鍵 capability 是 CAP_NET_ADMIN。預設的 789090901053 都高於 1024,因此不需要 CAP_NET_BIND_SERVICE

檢查 TUN 模組

test -c /dev/net/tun && echo "TUN device ready"
ls -l /dev/net/tun
sudo modprobe tun
cat /sys/class/misc/tun/dev

正常情況下,最後一個指令會輸出 10:200。若 modprobe tun 成功但裝置仍不存在,可以檢查目前核心是否裁剪了 TUN 支援。在容器內還需要由主機向容器開放字元裝置 10:200;僅在容器內建立同名檔案無法提供 TUN 功能。

若需要在每次開機時明確載入模組,可以寫入 modules-load 設定:

echo tun | sudo tee /etc/modules-load.d/tun.conf
sudo systemctl restart systemd-modules-load.service

優先由 systemd 授予能力

Linux 常見的兩種方式,是為二進位檔寫入檔案 capability,或由 systemd 在啟動程序時授予 capability。服務化部署較適合第二種:升級二進位檔後不會因檔案被替換而遺失擴充屬性,權限範圍也能直接在服務單元中檢視。

建立 /etc/systemd/system/mihomo.service

[Unit]
Description=mihomo proxy service
Documentation=https://wiki.metacubex.one/
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=clash
Group=clash
WorkingDirectory=/var/lib/mihomo

ExecStartPre=/usr/local/bin/mihomo -t -d /var/lib/mihomo -f /etc/mihomo/config.yaml
ExecStart=/usr/local/bin/mihomo -d /var/lib/mihomo -f /etc/mihomo/config.yaml

Restart=on-failure
RestartSec=3s
TimeoutStopSec=15s
LimitNOFILE=1048576

AmbientCapabilities=CAP_NET_ADMIN
CapabilityBoundingSet=CAP_NET_ADMIN
NoNewPrivileges=true

DevicePolicy=closed
DeviceAllow=/dev/net/tun rw
PrivateDevices=false

ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/mihomo
PrivateTmp=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK

[Install]
WantedBy=multi-user.target

AmbientCapabilitiesCAP_NET_ADMIN 交給非 root 服務程序,CapabilityBoundingSet 則限制它無法取得清單以外的 capability。DevicePolicy=closed 搭配 DeviceAllow=/dev/net/tun rw,只開放執行所需的 TUN 字元裝置。ProtectSystem=strict 會將系統目錄設為唯讀,而 ReadWritePaths 則單獨允許 mihomo 寫入狀態目錄。

若發行版或舊版 systemd 不支援其中某項加固指令,日誌會明確指出未知欄位。此時應依實際 systemd 版本調整對應行,不要為了繞過單一錯誤就改回以 root 使用者執行。

檔案 capability 作為備選方案

不使用 systemd 管理時,可以授予二進位檔 CAP_NET_ADMIN,但安裝新版二進位檔覆蓋舊檔後,該屬性會消失。使用此方案時,不要再重複設定 systemd 的 AmbientCapabilities。

sudo setcap cap_net_admin=+ep /usr/local/bin/mihomo
getcap /usr/local/bin/mihomo

# 需要撤銷時
sudo setcap -r /usr/local/bin/mihomo

部分掛載了 nosuid 的檔案系統會忽略檔案 capability,某些容器執行環境也會過濾 capability。遇到 Operation not permitted 時,應同時檢查掛載參數、容器能力清單與主機裝置映射。

啟動、開機自動啟動與執行驗證

寫入服務單元後,先讓 systemd 重新讀取設定,再啟動服務並加入開機目標。enable --now 會同時建立開機自動啟動連結並啟動服務。

sudo systemctl daemon-reload
sudo systemctl enable --now mihomo.service
sudo systemctl status mihomo.service --no-pager

正常狀態應顯示 Active: active (running),主程序使用者為 clash。在參考環境中,約 620 條規則、2 個代理提供者的設定冷啟動約需 0.8 至 1.2 秒;穩定執行後的常駐記憶體約為 45 至 85 MB。規則集數量、Geo 資料與連線數增加後,記憶體用量也會相應變化。

檢查連接埠與介面

sudo ss -lntup | grep -E ':(7890|9090|1053)\b'
ip tuntap show
ip rule show
ip route show table all | grep -E '198\.18\.|default'
systemctl show mihomo.service -p User -p MainPID

連接埠監聽位址應與設定一致:789090901053 都只監聽 127.0.0.1。mihomo 建立的 TUN 介面名稱可能隨版本與設定變化,不應只依固定名稱判斷;同時查看 ip tuntap show、策略路由與服務日誌會更可靠。

驗證 HTTP 代理與 TUN 路由

先明確指定 mixed 連接埠測試代理入口,再測試未設定代理環境變數的普通連線。由於範例策略群組暫時指向 DIRECT,此處重點是確認請求經過 mihomo、DNS 沒有逾時,且路由沒有形成迴圈。

curl --proxy http://127.0.0.1:7890 \
  --connect-timeout 5 \
  https://www.example.com/ -I

env -u http_proxy -u https_proxy -u all_proxy \
  curl --connect-timeout 5 \
  https://www.example.com/ -I

sudo journalctl -u mihomo.service -n 50 --no-pager

正式匯入代理節點後,可以暫時將 log-level 改為 debug,觀察目標網域命中了哪條規則、選用了哪個策略群組。排查完成後改回 info,否則長時間執行會產生大量日誌。

設定更新與平滑重新載入

修改 YAML 後不要立即重新啟動。先執行測試指令,確認縮排、規則格式、策略群組引用與 provider 路徑有效,再讓 systemd 重新啟動服務。YAML 使用空格縮排,Tab 字元會造成解析失敗;規則引用的策略群組名稱也必須與 proxy-groups 完全一致。

sudo -u clash /usr/local/bin/mihomo \
  -t \
  -d /var/lib/mihomo \
  -f /etc/mihomo/config.yaml

sudo systemctl restart mihomo.service
sudo systemctl status mihomo.service --no-pager
sudo journalctl -u mihomo.service --since "2 minutes ago" --no-pager

若設定來自訂閱,建議先下載至暫存檔,完成語法測試後再以原子方式替換正式設定。如此一來,systemd 重新啟動時只會讀取完整檔案,不會遇到尚未下載完成的半份 YAML。

sudo install -o root -g clash -m 0640 \
  /tmp/config.yaml \
  /etc/mihomo/config.yaml.new

sudo -u clash /usr/local/bin/mihomo \
  -t \
  -d /var/lib/mihomo \
  -f /etc/mihomo/config.yaml.new

sudo mv /etc/mihomo/config.yaml.new /etc/mihomo/config.yaml
sudo systemctl restart mihomo.service

mihomo 的外部控制器也支援重新載入設定,但遠端自動化腳本需要妥善處理控制器密鑰、介面監聽範圍與失敗回復。對單機伺服器而言,先測試再執行 systemctl restart 更直觀,停頓通常在數秒內。

日誌定位與常見故障

服務反覆重新啟動

先查看目前啟動週期的日誌與結束碼。由於服務設定了 Restart=on-failure,語法錯誤會觸發連續重新啟動;排查時可以先停止服務,直接執行設定測試。

sudo journalctl -u mihomo.service -b --no-pager
sudo systemctl show mihomo.service \
  -p ExecMainCode \
  -p ExecMainStatus \
  -p NRestarts

sudo systemctl stop mihomo.service
sudo -u clash /usr/local/bin/mihomo \
  -t \
  -d /var/lib/mihomo \
  -f /etc/mihomo/config.yaml

常見錯誤包括 YAML 縮排不一致、策略群組引用不存在、設定檔無法由 clash 使用者讀取,以及 provider 目錄不可寫入。可使用 namei -l /etc/mihomo/config.yaml 逐層檢查目錄權限。

建立 TUN 時顯示 permission denied

依序確認 TUN 裝置、服務 capability 與 systemd 裝置策略。僅確認 /dev/net/tun 是否存在並不足夠;程序缺少 CAP_NET_ADMIN 時,同樣無法建立介面或寫入策略路由。

ls -l /dev/net/tun
systemctl show mihomo.service \
  -p AmbientCapabilities \
  -p CapabilityBoundingSet \
  -p DevicePolicy
sudo journalctl -u mihomo.service -n 100 --no-pager | \
  grep -Ei 'tun|permission|operation not permitted'

若服務執行於 LXC、Docker 或其他容器中,還需要主機開放 /dev/net/tun 並授予 NET_ADMIN。雲端伺服器核心不包含 TUN 時,容器端設定無法補足核心能力。

啟用 TUN 後 DNS 逾時

先確認 1053 連接埠正在監聽,再檢查上游 DNS 是否可連線。若系統同時執行 systemd-resolved,它通常會佔用本機 127.0.0.53:53,這與 mihomo 監聽 127.0.0.1:1053 並不直接衝突。真正需要注意的是 TUN 的 DNS 劫持是否將 mihomo 自身發出的上游查詢再次送回 TUN,形成迴圈。

sudo ss -lnup | grep ':1053'
resolvectl status
dig @127.0.0.1 -p 1053 www.example.com
sudo journalctl -u mihomo.service -n 100 --no-pager | \
  grep -Ei 'dns|timeout|loop'

處理順序應為:確認預設出口介面辨識正確,確認 nameserver 位址可連線,再檢查規則是否錯誤攔截 DNS 上游。多網卡伺服器可以在 TUN 設定中明確指定介面,避免自動偵測選中 Docker、WireGuard 或暫時性的 VPN 介面。

本機可用,區域網路裝置無法連線

範例設定刻意使用 bind-address: 127.0.0.1allow-lan: false,因此其他裝置無法存取 7890。若確實需要提供區域網路代理,請將監聽位址改為伺服器的內網位址並啟用 LAN 存取,同時只在防火牆中放行可信任的網段。

# 僅供示例:允許 192.168.10.0/24 存取 TCP 7890
sudo ufw allow from 192.168.10.0/24 to any port 7890 proto tcp
sudo ufw status numbered

外部控制器 9090 建議繼續繫結迴路位址,透過 SSH 連接埠轉送存取。若必須監聽內網位址,至少設定隨機密鑰並限制來源位址,不要將控制介面直接暴露於公網。

執行維護檢查清單

  • 二進位檔架構與 uname -m 一致,升級後重新執行 mihomo -v
  • 主要設定由 root:clash 擁有,權限為 0640,執行目錄由服務使用者寫入。
  • 每次替換設定前執行 mihomo -t,測試通過後再重新啟動服務。
  • TUN 模式只授予 CAP_NET_ADMIN,預設連接埠不需要低連接埠繫結能力。
  • /dev/net/tun 存在;在容器環境中,也已完成主機裝置映射與 capability 授權。
  • 控制器繫結 127.0.0.1:9090 並設定密鑰,遠端管理使用 SSH 轉送。
  • 日誌維持 info,僅在短時間排錯期間切換至 debug
  • 修改 TUN 路由前準備自動停止工作,避免遠端 SSH 因預設路由變更而中斷。

完成這些步驟後,mihomo 會由 systemd 在網路就緒後啟動,異常結束時等待 3 秒自動重試,並在關機時獲得 15 秒的停止時間。設定、二進位檔與執行資料分開管理,後續無論更新核心、調整訂閱或遷移伺服器,都能依清晰邊界操作。

下載 Clash 客戶端 依平台查看可選版本