Files
aiops/docs/terminal/implementation-spec.md
T
Logan.Li e0d652d211 feat: 完善堡垒机与 Agent 智能运维能力
- 支持 Windows Agent 构建、安装脚本和统一上报协议

- 增强 RDP 剪贴板、虚拟盘和文件传输能力

- 新增 AIOps 上下文工具、告警 RCA trace 落库和回放接口

- 重构 docs 模块文档并补充终端、堡垒机、监控、AI Agent 设计
2026-07-05 23:49:36 +08:00

12 KiB
Raw Blame History

AIOps 终端体系实现规格

更新:2026-07-05 目标:让另一个项目仅阅读本文档,也能实现 WebSSH、Pod exec、kubectl、Workspace 终端和审计回放。 范围:xterm.js 前端底座、WebSocket 协议、后端 PTY/SSH/K8s 代理、会话录像、命令审计、实时查看。


1. 终端类型和职责

类型 前端渲染 后端执行 典型入口 审计方式
SSH WebShell xterm.js ssh.Session + PTY 服务器管理、WebShell asciicast + 命令
Pod Terminal xterm.js K8s pods/exec WebSocket Pod 详情 预留 K8s 审计
kubectl Terminal xterm.js 本地 kubectl 进程 + PTY 集群管理 建议命令审计
Workspace Terminal xterm.js 本地 shell + PTY,限制目录 流水线工作区 建议命令审计
RDP Canvas RDP 协议引擎 Windows 资产 独立图形录像

本文只定义字符终端。RDP 详见 RDP 实现规格。


2. 统一前端底座

2.1 组件结构

Terminal Dialog/Page
  ├── create Terminal()
  ├── load FitAddon
  ├── mount into .xterm-terminal-host
  ├── open WebSocket
  ├── onData → ws.send({ status: 0, data })
  ├── ResizeObserver → fit() → ws.send({ status: 1, cols, rows })
  └── ws.onmessage → terminal.write(data)

公共工具应提供:

方法 职责
mountXterm() 延迟挂载、打开 terminal、执行多次初始 fit
fitTerminal() 调用 FitAddon,返回 cols/rows
attachTerminalResize() 监听容器 resize,节流 fit 并同步后端
disposeTerminal() 关闭 WebSocket、移除 observer、销毁 xterm

2.2 DOM 和样式要求

<div className="xterm-terminal-host" ref={containerRef} />
.xterm-terminal-host {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #0a0e14;
}

.xterm-terminal-host .xterm {
  height: 100%;
}

要求:

  • 终端容器必须有稳定高度,不能依赖内容撑开。
  • Dialog 动画会影响 xterm 测量,终端弹窗建议禁用打开/关闭缩放动画。
  • term.open() 同一实例只能调用一次。
  • fit() 后必须把 cols/rows 发给后端,否则远端 PTY 仍按旧尺寸输出。

2.3 推荐 xterm 配置

const terminalOptions = {
  cursorBlink: true,
  convertEol: true,
  fontFamily: 'Menlo, Monaco, Consolas, monospace',
  fontSize: 13,
  lineHeight: 1.15,
  scrollback: 5000,
  theme: {
    background: '#0a0e14',
    foreground: '#d8dee9',
    cursor: '#f8fafc',
  },
};

交互要求:

  • ESC、Ctrl+C、Ctrl+D 必须传给终端,不应被 Dialog 或页面快捷键抢走。
  • 粘贴走 xterm 默认粘贴或 navigator.clipboard.readText() 后写入 WebSocket。
  • 多标签终端切换时,不要销毁非当前标签的 WebSocket,除非用户主动关闭。

3. WebSocket 协议

3.1 通用 JSON 协议

浏览器到后端:

{ "status": 0, "data": "ls -la\n" }
{ "status": 1, "cols": 120, "rows": 34 }

后端到浏览器:

{ "status": 0, "data": "total 8\r\n" }
{ "status": 2, "message": "connected" }
{ "status": -1, "message": "connection failed" }

字段语义:

字段 方向 说明
status=0 双向 stdin/stdout 数据
status=1 浏览器到后端 resize,包含 cols/rows
status=2 后端到浏览器 状态提示,可选
status=-1 后端到浏览器 错误提示

实现要求:

  • 后端读取 WebSocket 后按 status 分流。
  • stdout/stderr 可以合并写回 status=0,也可以扩展 stream 字段。
  • 输入数据必须原样写入 PTY,不要按行缓存,否则 vim/top 等交互程序不可用。
  • resize 事件必须节流,建议 100-200ms。

3.2 鉴权

WebSocket 无法稳定设置自定义 Header,因此支持两种方式:

/ws/terminal/:serverId?token=<JWT>
/ws/pod/exec?...&token=<JWT>

要求:

  • 后端优先读 Authorization,其次读 ?token=。
  • 鉴权通过后仍要做资源权限校验。
  • token 不能写入审计命令和终端录制。

4. SSH WebShell

4.1 后端流程

GET /ws/terminal/:serverId
  → JWTAuth
  → load server asset
  → load credential
  → ssh.Dial
  → session.RequestPty(term, rows, cols, modes)
  → session.Shell()
  → create sys_terminal_sessions
  → goroutine: ws input → ssh stdin
  → goroutine: ssh stdout/stderr → ws output + recorder + audit hub
  → on close: update duration, status, record_data, record_size

PTY 参数:

ssh.TerminalModes{
    ssh.ECHO:          1,
    ssh.TTY_OP_ISPEED: 14400,
    ssh.TTY_OP_OSPEED: 14400,
}

4.2 命令审计

基础策略:

  • 按回车提取命令。
  • 不记录每个按键。
  • 下一条命令出现前,统计上一条命令后的输出字节数。
  • 粘贴多行脚本应合并或标记为多行命令。

命令表:

sys_session_commands
  session_id
  session_type = terminal
  user_id / username
  server_id / server_name
  command
  output_size
  timestamp

敏感信息建议:

  • 对 password=...、token=...、AKIA...、私钥片段做脱敏。
  • 命令审计保存脱敏结果,原始录制可按安全策略决定是否保存。

5. asciicast 录制和回放

5.1 录制格式

使用 asciicast v2:

{"version":2,"width":120,"height":34,"timestamp":1783131042,"env":{"TERM":"xterm-256color"}}
[0.120,"o","Last login: ...\r\n"]
[0.450,"o","$ "]
[1.200,"o","ls -la\r\n"]

实现要求:

  • 第一行是 header。
  • 后续每行是 [timeOffset, "o", output]。
  • 只录制后端输出,不录制用户输入的独立事件,避免重复。
  • RecordData 建议限制大小,超过上限后截断并标记。

5.2 回放

前端回放流程:

GET /api/v1/terminal-sessions/:id/record
  → parse asciicast
  → create read-only xterm
  → 按 timeOffset 调度 terminal.write()
  → 支持播放、暂停、倍速、拖动进度

验收:

  • 回放画面和原终端输出顺序一致。
  • 支持命令书签跳转。
  • 删除会话后录制不可访问。

6. Pod Exec

6.1 后端链路

Browser xterm
  → AIOps backend WebSocket
  → Kubernetes API /api/v1/namespaces/{ns}/pods/{pod}/exec
  → channel.k8s.io WebSocket
  → container shell

K8s channel:

Channel 方向 内容
0 client to pod stdin
1 pod to client stdout
2 pod to client stderr
3 pod to client error/status
4 client to pod resize

resize frame:

{ "Width": 120, "Height": 34 }

实现要求:

  • shell 可选 /bin/bash、/bin/sh。
  • namespace、pod、container 必须做权限校验。
  • 容器切换时必须重连。
  • Pod exec 不应写入 SSH 的 sys_terminal_sessions,建议后续建 K8s 独立会话表。

7. kubectl 终端

7.1 后端链路

GET /ws/kubectl?cluster_id=1
  → JWTAuth + cluster permission
  → create temp kubeconfig
  → exec kubectl with PTY
  → ws input/output bridge
  → cleanup temp kubeconfig and process

安全要求:

  • kubeconfig 必须按用户和集群隔离。
  • 临时文件使用随机名,关闭后删除。
  • 禁止复用上一次集群上下文。
  • kubectl 权限由 AIOps RBAC 和集群 RBAC 双重约束。

建议审计:

  • 记录输入命令。
  • 标记 delete、scale、rollout restart、patch、apply 为高风险。
  • 后续接入审批或二次确认。

8. Workspace 终端

8.1 后端链路

GET /ws/workspace/:pipelineRunId/terminal
  → JWTAuth + pipeline permission
  → resolve workspace dir
  → ensure dir is under workspace root
  → start shell with PTY
  → ws input/output bridge

路径安全:

  • 所有路径必须 filepath.Clean。
  • realpath 后必须仍位于 workspace root 下。
  • 禁止用户通过 ../../ 进入任意系统目录。
  • Shell 的工作目录固定为本次 pipeline workspace。

9. 实时审计

9.1 SSH 实时查看

active SSH session
  → AuditHub broadcast output
  → /ws/audit/:sessionId
  → LiveMonitorDialog read-only xterm

要求:

  • 审计端只读,不允许发送 stdin。
  • 审计连接不能影响主会话性能。
  • 活动会话结束后,审计连接收到关闭提示。

9.2 统一审计页

建议页面分区:

页签 内容
在线会话 SSH active + RDP active
历史会话 SSH asciicast + RDP bitmap replay
会话命令 SSH 命令 + RDP 操作摘要

10. 数据模型

10.1 SSH 会话

sys_terminal_sessions
  id
  user_id / username
  server_id / server_name / server_ip / server_port
  client_ip
  status: active | closed | failed
  duration
  record_data
  record_size
  start_time / end_time

10.2 命令审计

sys_session_commands
  id
  session_id
  session_type: terminal | rdp
  user_id / username
  server_id / server_name
  command
  output_size
  timestamp

10.3 策略配置

sys_audit_settings
  retention_days
  record_all
  session_watermark_enabled
  session_record_enabled
  command_policy_enabled
  approval_required

字符终端第一版至少需要:

  • record_all
  • session_record_enabled
  • session_watermark_enabled
  • retention_days

11. 错误处理

场景 前端表现 后端处理
鉴权失败 显示未登录或无权限 关闭 WebSocket
凭据错误 显示连接失败 会话状态 failed,保存错误
SSH 超时 显示超时 释放 session/client
PTY resize 失败 显示警告或忽略 继续会话
WebSocket 断开 显示已断开 关闭远端 session
用户关闭窗口 清理资源 更新 end_time、duration

实现要求:

  • 任何退出路径都必须关闭 PTY、进程、SSH client、WebSocket。
  • 后端 goroutine 需要 context 取消,避免泄露。
  • 会话状态更新要放在 defer 中兜底执行。

12. 验收清单

12.1 WebSSH

  1. 打开服务器 SSH 终端成功。
  2. ls、top、vim、Ctrl+C、Ctrl+D 可用。
  3. 拖动窗口或最大化后,远端 stty size 与前端一致。
  4. 会话关闭后 sys_terminal_sessions 状态为 closed。
  5. 回放可播放完整输出。
  6. 命令审计能看到命令和输出大小。

12.2 Pod Exec

  1. 选择 namespace/pod/container 后能进入 shell。
  2. stdout/stderr 都能显示。
  3. resize 后 stty size 正确。
  4. 容器切换会重连。
  5. 无权限 namespace 无法连接。

12.3 kubectl

  1. 打开指定集群 kubectl 终端。
  2. kubectl get pods -A 输出正常。
  3. 切换集群不会复用旧上下文。
  4. 关闭窗口后后端 kubectl 进程退出。

12.4 Workspace

  1. 打开流水线工作区终端。
  2. 默认目录在 workspace root。
  3. pwd、ls 正常。
  4. 不能通过路径逃逸访问系统目录。

12.5 审计回放

  1. 在线会话能实时只读查看。
  2. 历史会话能按时间播放。
  3. 删除会话后录制和命令记录按策略处理。
  4. 关闭录制策略后不写 record_data。

13. 常见问题

问题 原因 处理
终端右侧黑边 初始 fit 太早 mount 后延迟多次 fit
resize 后远端仍旧宽度 只 fit 前端,未发 cols/rows WebSocket 发送 status=1
Ctrl+C 不生效 页面快捷键拦截 xterm 容器 focus 后透传
vim 显示错乱 PTY 类型或 resize 不正确 使用 xterm-256color 并同步尺寸
回放速度不对 time offset 解析错误 按 asciicast v2 第一列调度
WebSocket 断开后进程残留 后端未 cancel context close handler 统一清理
Pod exec 没输出 K8s channel 未拆分 按 channel 1/2 读取 stdout/stderr