Linux 部署 预计阅读 14 分钟

Linux 命令行部署 Clash 内核:systemd 守护进程与 TUN 权限配置

在无桌面环境的 Linux 上以命令行运行 Clash 内核:二进制放置、systemd 服务单元编写、TUN 设备权限与 capabilities 设置、开机自启与日志排查,一步步走通常驻方案。

部署结构与运行边界

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 客户端 按平台查看可选版本