KPanel 多主机终端设计与安全契约
August 28, 2026 · View on GitHub
- 状态:已按既有 v2 Noise 与终端请求模型实现;前后端、竞态与双架构自动化验证进行中;完整部署契约及 L3 实机验收待发布前执行
- 页面:
/terminal - 支持范围:本机 KPanel、使用新 v2 权限重新配对的远端 KPanel、使用新接入命令注册终端公钥的轻量节点
- 暂不支持:旧 v1 配对、未授予终端权限的旧 v2 配对,以及尚未运行
terminal-broker的旧轻量节点 - 已安装的旧轻量节点只会继续遥测;如需终端,先在节点执行
k kpanel node uninstall,在中心删除旧记录, 再生成新的轻量节点接入命令重新加入。单纯升级二进制不会自动扩展 root 权限。
1. 产品交互
用户从左侧“终端”进入后,先看到连接列表:
- 本机显示“本机终端”;
- 已授予终端权限的远端 KPanel 显示“加密直连”;
- 尚未建立反向终端轮询的轻量节点显示“轻量监控节点”;完成轮询握手后与其他远端节点一样显示“加密直连”并可开启终端;旧配对显示“需要重新配对”;
- 每台主机只打开一个页签,可以在多个主机页签间切换;关闭页签会同时终止对应 PTY;
- 主机终端始终在底部显示预输入框;应用、建站、体检和环境任务在脚本等待输入时显示同一套 预输入框。浏览器本地完成编辑,按 Enter 后整行发送;xterm.js 原生键盘输入仍可用于方向键、 密码和 TUI,并以 24 ms 短批次合并,降低高延迟网络中的逐键请求开销;
- 每个终端保留 5000 行浏览器回滚缓冲并显示主题化滚动条;用户位于底部时新输出自动跟随, 向上查看历史时不强制跳回,标题栏按钮可随时回到最新输出;
- 多主机连接列表使用独立滚动区域,不参与右侧终端的高度计算;主机数量增加时不得挤压终端 输出区或底部预输入框;
- 输出中的普通文本
http://、https://地址可在新页面打开;不接受其他协议;终端输出嵌入的 OSC 8 任意链接和 OSC 52 剪贴板指令均被拦截。
浏览器刷新、退出登录或 Panel/Agent 关闭不会把终端转为后台任务。连接丢失时页面重连读取 内存环形缓冲;超出容量的旧输出明确标记为截断。需要长期执行的安装、更新和备份仍必须使用 已有后台任务体系,不能依赖终端保持浏览器连接。
2. 链路与信任边界
本机:浏览器 Session + CSRF/Origin → paneld → Agent Unix Socket → 固定登录 Shell PTY
远端:浏览器 Session + CSRF/Origin → 中心 paneld
→ Noise v2 已认证加密通道 → 目标 paneld
→ 目标 Agent Unix Socket → 固定登录 Shell PTY
轻量:浏览器 Session + CSRF/Origin → 中心 paneld
→ 轻量节点主动发起的 HTTPS 长轮询(v2 Noise 加密) → root `terminal-broker`
→ 固定登录 Shell PTY
- 不开放新的 TCP、SSH、WebSocket 或 Agent 公网监听端口;轻量节点也不接受中心入站连接,终端命令只通过其主动 HTTPS 连接返回;
- Panel 继续无特权运行,宿主机 PTY 仅由 root Agent 创建;
- 轻量节点的遥测进程继续使用无登录低权限账户;独立的 root
terminal-broker只读取 root-only 的终端 Noise 私钥,复用固定 PTY Manager。遥测reporting key不进入终端服务,两者之间没有可被低权限进程调用的 root Socket; - Agent 主服务因文件管理根目录为
/而保持宿主机文件系统可写,Panel 状态目录继续通过ReadOnlyPaths独立保护。主机终端仍由 Agent 通过固定参数创建独立 transient systemd PTY, 以独立生命周期和审计边界提供apt、dnf等系统维护能力;其他 Agent API 仍只能调用各自的 固定适配器; - 远端终端只允许已激活且 scope 包含
cluster.terminal.open的 v2 控制端;当前合法 scope 为cluster.summary.read cluster.terminal.open或新增文件读取权限后的cluster.summary.read cluster.terminal.open cluster.files.read; - 现有 v1 和旧 v2 授权不自动扩权,管理员必须撤销后重新配对;
- 轻量节点终端由独立版本的 root
terminal-broker显式提供;服务启动后才通过已认证的 v2 Noise 轮询公布能力,不会因旧节点升级二进制而自动开放入站 Shell。
3. 固定 API
浏览器接口:
POST /api/v1/terminal-sessions
GET /api/v1/terminal-sessions/{id}/output?offset={n}&wait={0..1000}
POST /api/v1/terminal-sessions/{id}/input
POST /api/v1/terminal-sessions/{id}/resize
POST /api/v1/terminal-sessions/{id}/close
Panel 间接口:
POST /api/v2/federation/terminal/open
POST /api/v2/federation/terminal/output
POST /api/v2/federation/terminal/input
POST /api/v2/federation/terminal/resize
POST /api/v2/federation/terminal/close
轻量节点反向终端使用既有 v2 Federation HTTP 入口的专用反向传输动作:
POST /api/v2/federation/terminal/relay
轮询请求和响应均使用既有 v2 Noise_IK_25519_ChaChaPoly_SHA256 信封,终端私钥在接入时
单独生成并只保存为 root-only 配置;中心节点公钥和目标节点 ID 由 HTTPS 接入响应固定下来。
反向传输的命令 payload 直接复用既有 TerminalOpenRequest、TerminalInputRequest、
TerminalResizeRequest、TerminalCloseRequest,不再叠加第二套终端协议或命令 HMAC。
轮询请求携带当前中心会话 ID 列表和有界事件确认;节点重启或丢失会话后,中心会回收不再出现在
列表中的会话,避免留下不可操作的终端索引。relay 响应还带有中心进程启动 epoch;中心重启
后 epoch 变化,broker 会主动关闭本地旧 PTY,再重新建立会话。旧中心对 v2 relay 返回
404/405/426 时,节点继续保留遥测并延迟重试。
Agent 接口仅位于权限受限的 Unix Socket:
POST /v1/terminals
GET /v1/terminals/{id}/output
POST /v1/terminals/{id}/input
POST /v1/terminals/{id}/resize
POST /v1/terminals/{id}/close
接口不接受浏览器提交的 Shell 路径、启动参数、用户、环境变量或工作目录。Agent 只启动
/bin/bash -l,不存在时回退 /bin/sh -l,并固定 TERM=xterm-256color。systemd 部署环境使用
固定 transient unit 参数,最长运行 8 小时;关闭会话时同时停止 unit,Agent 停止或重启时也会结束会话。
4. 资源、生命周期与审计
| 项目 | 上限或规则 |
|---|---|
| 全局活动会话 | 16 |
| 单一 Panel 用户 / 联邦控制端 | 4 |
| 单次输入 | 16 KiB |
| 单次输出 | 本机最多 64 KiB;远端加密封装最多 32 KiB,可按偏移连续读取 |
| 每会话输出缓冲 | 1 MiB,仅内存 |
| 闲置关闭 | 30 分钟 |
| 最长会话 | 8 小时 |
| 浏览器长轮询 | 最长 1 秒 |
| Panel 失联会话索引 | 35 分钟后回收 |
| 轻量节点无活动终端轮询 | 25 秒长轮询;有活动会话时 750 ms 轮询 |
Panel 公共会话 ID 使用独立 256 位随机值并绑定当前管理员 ID,不向浏览器暴露 Agent 或远端 真实会话 ID。本机会话在 Agent 或 Panel 退出时关闭;远端关闭请求因网络不可达而无法送达时, 由目标 Agent 在最长 30 分钟闲置期后回收。审计只保存打开/关闭、目标主机、结果和管理员, 不保存输入、输出、命令历史、环境变量或终端回放。
5. 安全与失败规则
- 浏览器所有写操作验证 Session、Origin 和 CSRF;跨用户会话查询返回 404;
- Panel 间正文使用现有 Noise IK 身份认证、时间窗和 request ID 防重放,HTTP 链路上不出现 命令或输出明文;HTTPS 仍执行证书验证,HTTP 字面量 IP 仅承载 Noise 密文;
- 维度、偏移、等待时间、请求体、输入、输出、并发和内存全部有界;
- 终端能力本质上等价于 root SSH。它不是任意 Shell API 的替代品,其他页面仍只能调用固定 结构化动作,不能复用终端接口拼接后台业务命令;
- 轻量节点终端不等于开放
sshd:中心必须先通过接入时绑定的终端 Noise 静态公钥完成 v2 身份认证;rootterminal-broker只在本机 systemd 服务中运行,节点不暴露 TCP/SSH/HTTP 监听; - 网络断开只改变前端连接状态,不把“暂时没读到输出”误判为进程失败;PTY 真实退出才显示结束;
- 远端撤销授权后不能新建终端;既有会话由目标 Agent 的会话上限、闲置时间和进程生命周期回收。
6. 验收与回滚
发布前至少验证:
- 本机 open/output/input/resize/close;用户隔离、CSRF/Origin 和会话上限;
- v2 远端完整生命周期、权限不足拒绝、重放拒绝以及命令明文不出现在传输中;
- 轻量节点完整
open/output/input/resize/close生命周期、Noise 密文/身份绑定/重放、旧中心 404 兼容、 节点重启会话回收、root broker systemd 启停和amd64/arm64PTY 验收; - UTF-8 输入、整行预输入、控制键、24 ms 合并、窗口缩放、5000 行滚动、智能跟随、手动回到底部、 输出截断、断线重连和 URL 安全跳转;
- 以超过单屏高度的主机和体检命令列表复核独立滚动,确认终端输出和预输入框始终可见;
- 从测试 Agent 启动主机终端,验证 transient PTY 拥有独立 systemd 单元、可写系统目录并能运行 系统维护命令;同时验证应用、建站、体检和环境任务仍只能调用各自的固定适配器;
- 本机 Panel/Agent 退出关闭 PTY;远端关闭成功或链路中断后的闲置回收、过期索引回收均符合约定;
amd64、arm64,Chrome/Edge 桌面与移动端布局;- CPU、内存、连接数和 16 会话上限下的资源预算。
回滚到上一版本不会修改集群状态、网站、Docker 或宿主机配置。新 v2 配对记录中的 scope 会被 旧版本忽略;终端会话仅在内存中,回滚或重启会全部关闭,不需要数据迁移。