風雪 84eab7750c fix(scripts): scope SSH menu config writes to the server: block (#100)
* fix(scripts): scope SSH menu config writes to the server: block

The SSH management menu edited /etc/3m-ui/config.yaml with a global sed
substitution, which rewrote every identically-indented key in the file rather
than just the panel's. Changing the panel port therefore also rewrote unrelated
listener ports, and because the menu restarts the service right afterwards the
corruption went live immediately.

The same code path wrote the new file with `mv`, so a 0600 config holding the
JWT and credential-encryption keys came back as a umask-default 0644 file,
readable by every local user after any port or path change.

Both are fixed by scoping reads/writes to the `server:` block and by writing
back through the original inode (`cat > file`) instead of replacing it.

Three further defects found while auditing the menu:

- scripts/3m-ui.sh: `config port` read a bare "$2" under `set -u`, so running
  it without an argument aborted with "parameter not set" and the usage
  message below it was unreachable. Now "${2:-}".
- scripts/3m-ui.sh: the no-arg branch exec'd /usr/local/bin/3m-ui without
  checking whether it resolves back to this script, which spun in an endless
  exec loop on a broken entrypoint. Direct and indirect self-reference are now
  detected and reported with an actionable message.
- scripts/3m-ui: `enable_bbr` wrote into /etc/sysctl.d without creating it; on
  minimal/container images the redirect failed and `set -e` tore down the whole
  interactive menu. Now mkdir -p with a guarded failure.

Hardening and consistency:

- yaml_set reports an error instead of silently no-oping when neither the key
  nor a `server:` block exists; the menu no longer restarts the service for a
  change that never happened.
- yaml_get is scoped to the `server:` block so it matches yaml_set.
- temp files come from mktemp (0600) instead of a predictable .$$ name.
- open_firewall asks for confirmation before opening 22/80/443.
- status reports boot-time enablement on OpenRC, not only systemd.
- menu item 8 reports invalid input instead of returning silently.
- menu item 23 is relabelled: it updates all of 3m-ui, not only the script.
- the acme.sh helper is annotated as an example to review before running.

Verified locally: the listener port is left untouched when the panel port
changes, config mode stays 0600, `config port` with no argument prints usage,
the self-referencing entrypoint fails fast instead of looping, and both files
pass `sh -n`.

* fix(scripts): scope SSH menu config writes to the server: block

The SSH management menu edited /etc/3m-ui/config.yaml with a global sed
substitution, which rewrote every identically-indented key in the file rather
than just the panel's. Changing the panel port therefore also rewrote unrelated
listener ports, and because the menu restarts the service right afterwards the
corruption went live immediately.

The same code path wrote the new file with `mv`, so a 0600 config holding the
JWT and credential-encryption keys came back as a umask-default 0644 file,
readable by every local user after any port or path change.

Both are fixed by scoping reads/writes to the `server:` block and by writing
back through the original inode (`cat > file`) instead of replacing it.

Three further defects found while auditing the menu:

- scripts/3m-ui.sh: `config port` read a bare "$2" under `set -u`, so running
  it without an argument aborted with "parameter not set" and the usage
  message below it was unreachable. Now "${2:-}".
- scripts/3m-ui.sh: the no-arg branch exec'd /usr/local/bin/3m-ui without
  checking whether it resolves back to this script, which spun in an endless
  exec loop on a broken entrypoint. Direct and indirect self-reference are now
  detected and reported with an actionable message.
- scripts/3m-ui: `enable_bbr` wrote into /etc/sysctl.d without creating it; on
  minimal/container images the redirect failed and `set -e` tore down the whole
  interactive menu. Now mkdir -p with a guarded failure.

Hardening and consistency:

- yaml_set reports an error instead of silently no-oping when neither the key
  nor a `server:` block exists; the menu no longer restarts the service for a
  change that never happened.
- yaml_get is scoped to the `server:` block so it matches yaml_set.
- temp files come from mktemp (0600) instead of a predictable .$$ name.
- open_firewall asks for confirmation before opening 22/80/443.
- status reports boot-time enablement on OpenRC, not only systemd.
- menu item 8 reports invalid input instead of returning silently.
- menu item 23 is relabelled: it updates all of 3m-ui, not only the script.
- the acme.sh helper is annotated as an example to review before running.

Verified locally: the listener port is left untouched when the panel port
changes, config mode stays 0600, `config port` with no argument prints usage,
the self-referencing entrypoint fails fast instead of looping, and both files
pass `sh -n`.
2026-10-08 23:49:39 +08:00
2026-08-19 21:18:31 +08:00

3m-ui

3m-ui logo

Languages / 语言: README · English · 简体中文 · 繁體中文 · 日本語 · 한국어 · Español · Français · Deutsch · Русский · Português (Brasil) · Tiếng Việt · Bahasa Indonesia · ไทย · Türkçe · العربية · हिन्दी · Polski · Українська

Mihomo 服务端 Web 管理面板

稳定版优先: 默认安装和更新只使用正式 Release。预发布需要主动选择。完整安装包及 Docker 镜像包含固定版本的 Mihomo;详见 安装、升级与恢复。

轻量、自托管,用于在 Linux 上管理 Mihomo Listener、用户、订阅与运行状态。

License Go

文档:https://3m-ui.top/docs/ · 官网:https://3m-ui.top/


特性

类别 能力
节点 协议注册表驱动的 Listener:VLESS / VMess / Trojan / Shadowsocks / Hysteria2 / TUIC / AnyTLS / Snell / ShadowQUIC 等;Reality、TLS 自签、一键批量证书、流量倍率、传输层字段互斥校验
用户 一键创建(随机用户名/密码/UUID,可选绑定全部节点)、绑定节点、流量限额、按节点分别计量(计费 = 实际 × 节点倍率)、到期、IP/订阅拉取限制、首次使用起算、周期续期/重置、分组标签、外部订阅合并、批量操作、订阅 Token
订阅 UA 自动识别 Clash/Mihomo YAML、v2ray Base64(?target=v2ray 始终 Base64)、sing-box JSON;可选 ?target=;HTML 订阅页;TUIC/HY2 分享链含 SNI/allow_insecure 等客户端参数
配置 生成 → 校验 → 应用 分离;失败回滚上一份 config.yaml
Telegram 告警与管理命令(需 Token + Chat ID)
多机 登记远程面板、健康检查、同步节点镜像、合并订阅;按名称选择本机节点推送到远程(默认禁用,可改名/端口)
安全 JWT、首登改密、TOTP 两步验证、可选 GitHub OAuth、web_path 路径前缀、凭据加密、CORS
运维 核心启停/更新、Geo、面板 SSL/ACME(含 DNS-01 通配符)、备份、Cloudflare WARP 一键注册(WireGuard/MASQUE YAML)

补充说明(用户限制 / 证书 / 订阅)

前端:React + Ant Design + Lucide Icons(frontend/),构建后嵌入单一 Go 二进制。

发布产物:纯静态链接(CGO_ENABLED=0 + modernc SQLite),无需系统 libsqlite3,兼容 glibc / musl。

完整安装包 / Docker:linux/amd64 · arm64。高级独立面板二进制另外支持 armv7 · armv6 · 386 · riscv64 · loong64 · ppc64le · s390x,需要自行准备兼容内核。


快速安装

curl -fsSL https://raw.githubusercontent.com/kazeyukiro/3m-ui/main/scripts/install.sh | sudo sh

完整安装支持 Linux amd64 / arm64,自动安装面板、配套 Mihomo 和系统服务。首次配置和管理员密码由程序统一生成;配置已存在时保持原值。其他架构可使用独立二进制和自行准备的内核,见 详细安装说明。

安装完成后:

面板地址   http://SERVER_IP:8080/
默认账号   admin
初始密码   安装时随机生成并显示一次,首次登录必须修改
管理命令   3m-ui

更新:

# 推荐(已安装后)
sudo 3m-ui update
# 指定版本: sudo 3m-ui update v1.0.6
# 从旧安装脚本迁移时,先重新执行上面的最新安装命令,配置和账号会保留。

卸载:

sudo 3m-ui uninstall

AI 提示词安装

把提示词复制到 ChatGPT / Claude / Cursor 等助手,并说明你的系统(如 Ubuntu 22.04)与是否已有 root SSH。助手应只协助执行官方脚本,不要改写成不明来源命令。完整提示词及使用说明见 AI 提示词安装文档。

无需手动复制大段文本,用 curl 直接把纯文本提示词加载到剪贴板再粘贴:

curl -fsSL https://raw.githubusercontent.com/kazeyukiro/3m-ui/main/docs/ai-install-prompt.zh.txt | pbcopy        # macOS
curl -fsSL https://raw.githubusercontent.com/kazeyukiro/3m-ui/main/docs/ai-install-prompt.zh.txt | xclip -sel clip # Linux X11
curl -fsSL https://raw.githubusercontent.com/kazeyukiro/3m-ui/main/docs/ai-install-prompt.zh.txt | wl-copy       # Linux Wayland

提示词只引导使用下方官方一键脚本,所有下载均校验 SHA256SUMS:

curl -fsSL https://raw.githubusercontent.com/kazeyukiro/3m-ui/main/scripts/install.sh | sudo sh

测试 / 预发布通道(可选,非默认)见 安装文档。


Docker 安装

Linux VPS 下载发布版本中的 docker-compose.yml,在同一目录执行:

docker compose up -d
docker compose logs 3m-ui

镜像为 ghcr.io/kazeyukiro/3m-ui:latest,包含面板及固定版本的 Mihomo;latest 只跟随稳定版。配置、随机密钥和管理员会自动初始化,首次密码只在首次启动输出一次。默认 host 网络,新增节点后放行实际使用的 TCP/UDP 端口。另提供 docker-compose.bridge.yml,以及版本固定、证书配置和持久化恢复说明,见 安装文档。


从源码构建

# 前端
cd frontend && npm install && npm run build

# 嵌入并编译后端(示例 amd64)
rm -rf backend/cmd/server/web/dist
cp -r frontend/dist backend/cmd/server/web/dist
cd backend
CGO_ENABLED=0 go build -tags sqlite_modernc -trimpath -ldflags='-s -w' -o ../3m-ui ./cmd/server

需要本机已安装兼容的 Mihomo 二进制,并在配置中指向其路径。


配置要点

默认数据与配置大致位于:

路径 用途
/etc/3m-ui/config.yaml 面板配置
/var/lib/3m-ui/3m-ui.db SQLite
/var/lib/3m-ui/listener-certs/ 节点自签证书(请与数据库一并备份)
/var/lib/3m-ui/mihomo/ Mihomo 工作目录与 config.yaml

JWT / 凭据密钥请使用独立随机值(≥ 32 字节),不要使用文档示例。

自定义面板端口:3m-ui config port <port> 或编辑配置后重启服务。 新建节点默认随机填写 10000~60000 的监听端口(轻量抽样并避开已加载节点端口;支持倒序范围),可手动修改或点击「随机端口」。


架构示意

  Browser (Ant Design + Lucide)
        │  REST + JWT
        ▼
  Gin API  ── Telegram Bot
        │
   Service layer (users / listeners / sub / cluster / …)
        │
   SQLite · certstore · visual config
        │
        ▼
     Mihomo (-t 校验 → 应用 / 回滚)

备份建议

至少备份:

  1. 3m-ui.db
  2. listener-certs/
  3. 面板 config.yaml 与 Mihomo 配置目录

仅更新二进制时,证书会尽量从磁盘 hydrate;若目录丢失会重新自签,客户端需重新拉取订阅。


致谢

特别感谢以下开源项目和社区:

贡献者:Contributors


Star History

Star History Chart

许可证

Eclipse Public License 2.0

第三方组件见 THIRD-PARTY-NOTICES.md。



English & other languages

Full translations (18 languages): see the language links at the top, or open README.en.md.

S
Description
Lightweight self-hosted web panel for Mihomo: listeners, users, subscriptions, SSL/ACME, Telegram, and multi-node cluster.
https://3m-ui.top
Readme EPL-2.0
6.3 MiB
0 Stars 1 Watchers 0 Forks
Languages
Go 49.9%
TypeScript 44.7%
Shell 2.5%
CSS 0.9%
Python 0.9%
Other 0.9%