Spot Monitor

开始

设计哲学

安全

装 agent 不扩大被监控机器的攻击面

  • 不监听端口。连接由 agent 主动向 hub 发起,被监控的机器不用开端口,也不用改防火墙
  • 不写文件。只读 /proc、/sys/class/net、/etc/os-release 和 statvfs,没有配置文件、数据库或缓存。累加流量、识别重启这类有状态的工作都在 hub 侧
  • 不执行 hub 下发的东西。hub 下发的只有 TCP 探测任务(host:port 和间隔),没有任何指令能让 agent 运行命令或写文件
  • 不自动更新。自更新意味着 agent 能以 root 下载并执行任意二进制。升级由你在机器上重新执行一次节点的安装命令

最小权限运行

一键脚本写出的 systemd 单元带着这些限制:

agenthub
身份专用系统用户 monitor-agent专用系统用户 monitor
文件系统ProtectSystem=strict + ProtectHome,全盘只读同上,另加 ReadWritePaths 只开 data/
提权NoNewPrivileges、RestrictSUIDSGIDNoNewPrivileges、RestrictSUIDSGID
隔离PrivateTmp、PrivateDevicesPrivateTmp、PrivateDevices
网络RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINKAF_INET AF_INET6
内存MemoryMax=64MMemoryMax=256M

hub 因此改不了自己的二进制,可写的只有数据库和主题所在的那一个目录。

没开 nesting 的 LXC 容器建不了挂载命名空间,systemd 会跳过「文件系统」和「隔离」两行,服务照常启动,其余几行不受影响。agent 不用 DynamicUser 正是因为这一点:带着它的单元在这种容器里无法启动,报 226/NAMESPACE。

节点 token 写在 /opt/monitor/agent.env,权限 0600,不写进服务定义,因为服务定义会出现在 systemctl cat 和日志里。

OpenRC 上 agent 同样以专用用户 monitor-agent 运行。上表其余几行是 systemd 的单元选项,OpenRC 没有对应的开关,两边一致的只有运行身份、自动重启和 token 的存放方式。

凭证的边界

  • 节点 token 是 256 位随机值,走 Authorization: Bearer 头而不是 URL query,因为 query 会进反代的 access log。它只能上报自己那一个节点,读不到别的节点,也改不了任何设置
  • 换发 token 立即生效,已经建立的连接也会被断开
  • 会话是 256 位随机 token,数据库里只存 sha256,14 天过期。cookie 带 HttpOnly 与 SameSite=Lax,HTTPS 下加 Secure
  • 面板的每个 handler 都在参数里带一个 Admin 提取器,框架在进入函数体之前校验会话,不会漏掉某个接口
  • 应急密码用 argon2id 存储。同一地址 15 分钟内失败 5 次即锁定;另有一道并发闸门,同一时刻只做一次密码校验

面板默认不对公网开放

一键脚本默认 --listen 127.0.0.1(--host 可改),装完后从公网打不开面板,要先配反向代理。

原因是明文 HTTP 上,会话 cookie 和节点 token 都在链路上明文传输,中间人还能替换安装命令下载的脚本和二进制,而那个脚本以 root 执行。所以 agent 和 install.sh 拒绝用明文连远程 hub,从 IP 或明文地址进面板时也不给出安装命令。

一个例外是同一内网的直连:从私网地址(10.、172.16–172.31、192.168.、IPv6 ULA,或本机)直接用 http://IP:28080 进面板,hub 会照常给出安装命令——令牌没有离开这个网络,上面那些风险不成立。判断依据是「这次请求没有声明 X-Forwarded-Proto,且 hub 看到的客户端地址是私网地址」;经反代转发的公网访客不满足,所以反代部署不会因为这个例外被绕过。

配法见反向代理。

极简

零配置文件

hub 只有四个运行参数:--listen、--db、--themes、--site。其余设置都存在 SQLite 里,在面板上改。

没有配置文件,也就没有「文件在哪、权限对不对、和数据库是否一致」这类问题。

不做的功能

不做原因
负载告警阈值、持续时长、节点范围是一整套规则引擎。通知只做掉线、流量、到期、登录这几件判定明确的事
远程 SSH / web terminal只读的观察者被攻破,和能执行命令的工具被攻破,代价差一个数量级
插件系统等于把第三方代码请进 hub 的进程
HTTP 探测一个 URL 的成败取决于状态码、跳转、证书、正文匹配,那是另一套东西;而 ICMP 已经做了,见下
agent 自动更新自更新意味着 agent 能以 root 下载并执行任意二进制
跨平台 agent加 Windows / macOS 等于推翻整个采集实现

这些不在计划里。探针只报告机器的状态,不代管机器;不写的代码既不占内存,也不会出漏洞。

本分支加了 ICMP 探测,与上面这些不同:这个仓库是二次开发,不逐条沿袭上游的取舍。上游的理由是「一种够用,三种要三套超时语义、三套图例」——我们只加了一种(按任务可选,默认仍是 TCP),而且超时与图例和 TCP 共用一套,所以那句担心没有兑现。它要回答的是一个 TCP 握手答不了的问题:这台主机、这条线路通不通,与「某个服务通不通」是两件事。权限不足时不会静默成丢包,原因会显示在相关任务上,也有 monitor-agent --ping 可以先自检——详见延迟与丢包。

高效

同一台机器、同一套负载的实测数字,完整数据在性能:

可执行文件6.3 MiB
冷启动到能响应请求70 ms
空转内存6.5 MiB
200 个节点在线,每 2 秒上报8.4 MiB,一颗核的 2.5%
每秒 1000 次上报8.6 MiB

Rust 没有 GC,也没有语言运行时,负载上去内存基本不动。换语言只带来体积和内存上的优势,响应速度靠的是几处具体的调优,记在同一页。

在 GitHub 上修改这一页最后更新 2026-10-02