轻量的服务器探针 hub。Rust + SQLite,编译为单个二进制。
节点状态、流量统计、TCP 延迟、续费成本,仅此四项。
- 单二进制,零配置启动,
http://<IP>:28080直接用,全部设置存在 SQLite 里 - 内存与磁盘口径对齐
free(1)/df(1) - 总流量跨 VPS 重启、hub 重启、agent 掉线持续累加
- 在线节点过期后,到期日按付款周期自动顺延
- 公开状态页可换主题,主题在运行时从磁盘加载
- 数据库一键导出备份、导入恢复、回收空间
- GitHub SSO 登录,本地应急密码作为备用入口
- agent 静态链接单文件,支持 systemd 与 OpenRC
不做:通知、远程 SSH、插件系统、ICMP / HTTP 探测。
| 仓库 | 说明 |
|---|---|
| monitor | hub:后台、API、公开页宿主 |
| agent | Linux agent |
| monitor-theme-default | 内置默认主题 |
agent (Linux) ──WebSocket / JSON-RPC 2.0──▶ hub (axum + SQLite) ──▶ 后台 + 状态页
需要 Rust stable 与 Node.js(Node 只用于构建后台)。
cd web-admin && npm ci && npm run build && cd ..
cargo build --release后台与默认主题在编译期嵌入二进制。默认主题不需要 clone:cargo build 按 web-theme.pin
里的 <tag> <sha256> 下载主题仓库发布的 theme.tar.gz,校验后解到 target/theme/。
curl -fsSL https://raw.githubusercontent.com/stqfdyr/monitor/main/install-hub.sh -o install-hub.sh
sudo sh install-hub.sh有终端时给一个菜单(安装 / 升级、卸载、状态、日志);curl ... | sh 没有终端可读答案,直接按默认装。
装完打印一次性应急密码,记下来——hub 只监听 127.0.0.1,得先配好反向代理才能打开面板,
脚本最后会把 nginx / caddy / CF 隧道三种配法打出来。密码事后也能找回:
journalctl -u monitor-hub | grep Emergency。
脚本做的事:核对 release 的 sha256sums.txt 之后才把二进制放进 /opt/monitor,建一个 monitor
系统用户,写一个 systemd 单元。重跑一次就是升级——校验通过才替换,起不来自动回滚到上一版,
没写的参数沿用上次的。
除了 systemd 单元,其它都在一个目录下——备份和搬机器只认这一个路径,和容器里的 /data 是同一套
划分:
/opt/monitor/
├── monitor-hub # hub 二进制
├── monitor-agent # 这台机器也装了 agent 的话
├── agent.env # agent 的 token,0600 root
└── data/ # 服务唯一可写的目录,0700 monitor
├── monitor.db
└── themes/ # 后台装的外部主题
| 参数 | 说明 |
|---|---|
--port <n> |
本机监听端口,默认 28080 |
--site <url> |
一般不用填,见「反向代理」 |
--uninstall |
卸载,数据保留在 /opt/monitor/data |
--purge |
卸载并删除数据库 |
不用脚本的话,二进制自己就能跑:
monitor-hub # 0.0.0.0:28080,数据库 ./monitor.db| 参数 | 默认 | 说明 |
|---|---|---|
--listen |
[::]:28080 |
监听地址。一个 socket 同时收 IPv6 与 IPv4 |
--db |
monitor.db |
SQLite 路径 |
--site |
空 | 对外地址,只有反向代理场景需要,见下 |
--themes |
数据库同级 themes/ |
外部主题目录 |
不带 --site 时,hub 不假设自己的地址:面板用浏览器地址栏里的地址拼安装命令,cookie 的 Secure
标志看请求的 X-Forwarded-Proto。裸 ip:port 部署是明文 HTTP,会话与节点凭证在链路上都是明文,
生产环境请放到 TLS 反向代理后面。IP 或明文入口不提供添加、批量注册和安装命令;请从 HTTPS 域名进入。
默认监听 [::]:28080,Linux 上一个 v6 socket 通过 v4-mapped 地址同时收两族,所以双栈机器不用配
任何东西;内核禁用了 IPv6 或 bindv6only=1 时自动退回 0.0.0.0:28080。这件事只影响谁连得上:
纯 v4 的 hub 收不到只有 IPv6 的节点,反之亦然。
首次启动打印一次性应急密码,用它登录 /admin:
- 安全 配置 GitHub OAuth(回调
<面板地址>/api/auth/github/callback)与允许登录的用户名, 修改应急密码 - 节点 添加节点,点下载按钮生成安装命令,在目标主机执行
- 拖动手柄排序;展示与流量设置、续费设置分别编辑
curl -fsSL https://monitor.example.com/install.sh | sh -s -- \
--server https://monitor.example.com --token <token>机器多的时候不必一台一台添加:节点 → 批量添加开一个一小时的注册窗口,期间下面这条命令在 任意机器上跑一次,那台机器就会带着自己的 hostname 出现在列表里,各自拿到各自的 token。
curl -fsSL https://monitor.example.com/install.sh | sh -s -- \
--server https://monitor.example.com --register <key>这条命令里没有任何一台机器的凭证,所以可以直接进 for 循环、ansible 或者开机脚本。窗口到点自
动失效,一个窗口最多注册 100 台;重跑同一条命令不会重复添加——机器上已经有 token 就直接沿用。
agent 二进制由 hub 转发,节点无需直连 GitHub。hub 自身访问不了 GitHub 时,在设置 → 站点里填一个
GitHub 代理(如 https://ghfast.top),hub 拉 release 时会用它,节点侧不用改任何东西。
这个代理只接受
https://,而且它返回的字节会被装到每一台节点上并跑起来——hub 原样转发,install.sh直接安装,中间没有摘要校验。只填信得过的镜像。见 docs/security.md。
hub 只有明文 HTTP 时,命令里会多一个 --insecure——agent 和 install.sh 默认拒绝明文连远程 hub,
因为凭证会明文传输,装 agent 下载的二进制也走同一条未验证的通道。面板会把这件事标出来。上了 TLS
之后命令自动不再带它。
docker run -d --name monitor -p 28080:28080 \
-v monitor-data:/data -e TZ=Asia/Shanghai \
ghcr.io/stqfdyr/monitorFROM scratch 里放同一个 musl 二进制加一份 zoneinfo,约 10 MB,以 uid 65534 运行,数据库和主题
目录都在 /data。首次启动的应急密码在 docker logs monitor 里。
TZ 必须设成 hub 所在的时区。 日流量和账单周期按本地日期切换,不设按 UTC 算——到点不归零,
也不会有任何报错。
挂主机目录代替 named volume 时,先 chown 65534:65534。
install-hub.sh 装出来的 hub 只监听 127.0.0.1,公网访问不到——凭证不会在链路上明文传输,
也没有端口需要防火墙。把域名指过来是反向代理的活。
配好之后不用改 hub 的任何参数,安装命令会自己变。 面板用浏览器地址栏的地址拼命令,所以你改用
https://hub.example.com 进后台,就能添加节点并获得 --server https://hub.example.com 的安装命令。
面板不再生成 --insecure 命令;会话 cookie 的 Secure 跟着请求的 X-Forwarded-Proto 走。
证书、X-Forwarded-Proto、WebSocket 都自动处理,一行就够:
hub.example.com {
reverse_proxy 127.0.0.1:28080
}map $http_upgrade $connection_upgrade { default upgrade; '' close; }
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name hub.example.com;
ssl_certificate /etc/letsencrypt/live/hub.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hub.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:28080;
proxy_http_version 1.1;
# 导入备份和上传主题是分片传的,单片 4 MiB,所以这个数只跟分片
# 大小有关,跟数据库多大无关。其余路径 hub 自己卡在 64 KiB。
client_max_body_size 8m;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket:/api/agent/ws 与 /api/ws 是长连接
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_read_timeout 1h;
proxy_send_timeout 1h;
}
}不用开任何入站端口,纯 IPv4 的机器也能拿到双栈入口(cloudflared 的隧道是出站建立的):
ingress:
- hostname: hub.example.com
service: http://127.0.0.1:28080
- service: http_status:404在边缘做了路径白名单的话,升级 hub 时记得同步:新版本加的路由会被上一版的名单挡在外面,
而且 403 来自边缘,hub 侧一行日志都没有。面板用到的全部路径见
docs/architecture.md 的路由表;这一版新增的是 POST /api/themes(上传主题)
和 DELETE /api/themes/{short}。
- 转发
Upgrade/Connection头,关闭缓冲,读写超时远大于 60 秒,否则节点会周期性掉线 - 请求体上限只需要 8 MiB:导入备份和上传主题都是分片传的(单片 4 MiB,hub 侧单请求硬上限
8 MiB),所以这个数不随数据库增长——256 MiB 的备份也是 64 个 4 MiB 的请求。nginx 默认
client_max_body_size 1m仍然拦得住一片,要改成8m(caddy 默认不限);Cloudflare 免费版 100 MB 的上传上限则不再是天花板,单个请求离它差一个数量级 - 放行
POST/PUT/DELETE - 透传
X-Forwarded-Proto,否则会话 cookie 拿不到Secure - 透传
X-Forwarded-For,否则登录限流会按代理地址计数
手工部署(不走 install-hub.sh)时记得自己加 --listen 127.0.0.1:28080,否则明文端口对外可达,
绕过反代就能明文访问。
只有这两种情况,常规反代都不属于:
- 面板域名和节点连接的域名不同。用
--site指定节点可达的 HTTPS 域名。 IP 或 SSH 转发入口仍不能添加节点,--site不绕过这个限制。 - 反代不发
X-Forwarded-Proto。cookie 拿不到Secure,用--site https://...强制。
主题目录复制到 --themes 指向的位置后,在后台「主题」页切换,无需重启。选中的主题缺失或损坏时回落到内置默认主题。主题包格式见
monitor-theme-default。
主题的 theme.json 里 url 指向 GitHub 仓库时,卡片上的 ⟳ 从该仓库最新的 release 取 theme.tar.gz
装上;tag 和已装版本相同就不下载。走「设置」里的 GitHub 代理。
- 节点 token 只在登录后的面板视图里出现,公开页拿不到;可随时换发,换发即踢掉旧 agent
- 密码登录按来源地址限流,15 分钟 5 次,另有一道并发闸门
- 公开状态页按节点开关,且不输出 IP、主机名与备注
- OAuth 回调校验 state;session cookie 为 HttpOnly + SameSite=Lax + Secure
- 匿名可达的接口都有明确上界:历史窗口最宽 7 天,agent 二进制转发最多 4 个并发
- 导入的备份先整份校验(完整性、表结构、无视图/触发器、schema 版本)才允许覆盖;恢复后所有会话作废
详见 docs/security.md。
| 文档 | 内容 |
|---|---|
| docs/architecture.md | 路由表、协议、数据表 |
| docs/traffic.md | 流量累加与周期重置 |
| docs/security.md | 鉴权、API 边界、公开页 |
| docs/decisions.md | 技术选型与被否决的方案 |
| docs/development.md | 开发流程 |
cargo test
cargo clippy --all-targets
cd web-admin && npm run devMIT