开始
设计哲学
安全
装 agent 不扩大被监控机器的攻击面
- 不监听端口。连接由 agent 主动向 hub 发起,被监控的机器不用开端口,也不用改防火墙
- 不写文件。只读
/proc、/sys/class/net、/etc/os-release和statvfs,没有配置文件、数据库或缓存。累加流量、识别重启这类有状态的工作都在 hub 侧 - 不执行 hub 下发的东西。hub 下发的只有 TCP 探测任务(
host:port和间隔),没有任何指令能让 agent 运行命令或写文件 - 不自动更新。自更新意味着 agent 能以 root 下载并执行任意二进制。升级由你在机器上执行
--upgrade
最小权限运行
一键脚本写出的 systemd 单元带着这些限制:
| agent | hub | |
|---|---|---|
| 身份 | 专用系统用户 monitor-agent | 专用系统用户 monitor |
| 文件系统 | ProtectSystem=strict + ProtectHome,全盘只读 | 同上,另加 ReadWritePaths 只开 data/ |
| 提权 | NoNewPrivileges、RestrictSUIDSGID | NoNewPrivileges、RestrictSUIDSGID |
| 隔离 | PrivateTmp、PrivateDevices | PrivateTmp、PrivateDevices |
| 网络 | RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK | AF_INET AF_INET6 |
| 内存 | MemoryMax=64M | MemoryMax=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提取器,框架在进入函数体之前校验会话,不会漏掉某个接口 - 浏览器发来的写请求带着
Sec-Fetch-Site且不是同源,一律回 403,在进路由之前统一拦下:同一注册域名下兄弟子域上的脚本也借不到会话 cookie。不发这个头的客户端(curl、脚本)照常放行;明文 HTTP 的非回环地址上浏览器不发它,这层保护不生效 - 应急密码用 argon2id 存储。同一地址 15 分钟内失败 5 次即锁定;另有一道并发闸门,同一时刻只做一次密码校验
面板默认不对公网开放
一键脚本写死 --listen 127.0.0.1,装完后从公网打不开面板,要先配反向代理。
原因是明文 HTTP 上,会话 cookie 和节点 token 都在链路上明文传输,中间人还能替换安装命令下载的脚本和二进制,而那个脚本以 root 执行。所以 agent 和 install.sh 拒绝用明文连远程 hub,从 IP 或明文地址进面板时也不给出安装命令。
配法见反向代理。
极简
零配置文件
hub 只有四个运行参数:--listen、--db、--themes、--site,外加一个执行完就退出的 --reset-password。其余设置都存在 SQLite 里,在面板上改。
没有配置文件,也就没有「文件在哪、权限对不对、和数据库是否一致」这类问题。
不做的功能
| 不做 | 原因 |
|---|---|
| 负载告警 | 阈值、持续时长、节点范围是一整套规则引擎。通知只做掉线、流量、到期、登录这几件判定明确的事 |
| 远程 SSH / web terminal | 只读的观察者被攻破,和能执行命令的工具被攻破,代价差一个数量级 |
| 插件系统 | 等于把第三方代码请进 hub 的进程 |
| ICMP / HTTP 探测 | TCP 一种够用,三种要三套超时语义、三套图例 |
| agent 自动更新 | 自更新意味着 agent 能以 root 下载并执行任意二进制 |
| 跨平台 agent | 加 Windows / macOS 等于推翻整个采集实现 |
这些不在计划里。探针只报告机器的状态,不代管机器;不写的代码既不占内存,也不会出漏洞。
高效
同一台机器、同一套负载的实测数字,完整数据在性能:
| 可执行文件 | 6.8 MiB |
| 冷启动到能响应请求 | 6.5 ms |
| 空转内存 | 7.0 MiB |
| 200 个节点在线,每 2 秒上报 | 10.8 MiB,一颗核的 2% |
| 1000 个节点在线,每秒上报 | 29.5 MiB,一颗核的 9% |
Rust 没有 GC,也没有语言运行时,内存只随在线节点数缓慢增长,每台约 23 KiB。换语言只带来体积和内存上的优势,响应速度靠的是几处具体的调优,记在同一页。