- 支持 Windows Agent 构建、安装脚本和统一上报协议 - 增强 RDP 剪贴板、虚拟盘和文件传输能力 - 新增 AIOps 上下文工具、告警 RCA trace 落库和回放接口 - 重构 docs 模块文档并补充终端、堡垒机、监控、AI Agent 设计
488 lines
12 KiB
Markdown
488 lines
12 KiB
Markdown
# 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 实现规格](../rdp/implementation-spec.md)。
|
||
|
||
---
|
||
|
||
## 2. 统一前端底座
|
||
|
||
### 2.1 组件结构
|
||
|
||
```text
|
||
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 和样式要求
|
||
|
||
```tsx
|
||
<div className="xterm-terminal-host" ref={containerRef} />
|
||
```
|
||
|
||
```css
|
||
.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 配置
|
||
|
||
```ts
|
||
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 协议
|
||
|
||
浏览器到后端:
|
||
|
||
```json
|
||
{ "status": 0, "data": "ls -la\n" }
|
||
{ "status": 1, "cols": 120, "rows": 34 }
|
||
```
|
||
|
||
后端到浏览器:
|
||
|
||
```json
|
||
{ "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,因此支持两种方式:
|
||
|
||
```text
|
||
/ws/terminal/:serverId?token=<JWT>
|
||
/ws/pod/exec?...&token=<JWT>
|
||
```
|
||
|
||
要求:
|
||
|
||
- 后端优先读 `Authorization`,其次读 `?token=`。
|
||
- 鉴权通过后仍要做资源权限校验。
|
||
- token 不能写入审计命令和终端录制。
|
||
|
||
---
|
||
|
||
## 4. SSH WebShell
|
||
|
||
### 4.1 后端流程
|
||
|
||
```text
|
||
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 参数:
|
||
|
||
```go
|
||
ssh.TerminalModes{
|
||
ssh.ECHO: 1,
|
||
ssh.TTY_OP_ISPEED: 14400,
|
||
ssh.TTY_OP_OSPEED: 14400,
|
||
}
|
||
```
|
||
|
||
### 4.2 命令审计
|
||
|
||
基础策略:
|
||
|
||
- 按回车提取命令。
|
||
- 不记录每个按键。
|
||
- 下一条命令出现前,统计上一条命令后的输出字节数。
|
||
- 粘贴多行脚本应合并或标记为多行命令。
|
||
|
||
命令表:
|
||
|
||
```text
|
||
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:
|
||
|
||
```json
|
||
{"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 回放
|
||
|
||
前端回放流程:
|
||
|
||
```text
|
||
GET /api/v1/terminal-sessions/:id/record
|
||
→ parse asciicast
|
||
→ create read-only xterm
|
||
→ 按 timeOffset 调度 terminal.write()
|
||
→ 支持播放、暂停、倍速、拖动进度
|
||
```
|
||
|
||
验收:
|
||
|
||
- 回放画面和原终端输出顺序一致。
|
||
- 支持命令书签跳转。
|
||
- 删除会话后录制不可访问。
|
||
|
||
---
|
||
|
||
## 6. Pod Exec
|
||
|
||
### 6.1 后端链路
|
||
|
||
```text
|
||
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:
|
||
|
||
```json
|
||
{ "Width": 120, "Height": 34 }
|
||
```
|
||
|
||
实现要求:
|
||
|
||
- shell 可选 `/bin/bash`、`/bin/sh`。
|
||
- namespace、pod、container 必须做权限校验。
|
||
- 容器切换时必须重连。
|
||
- Pod exec 不应写入 SSH 的 `sys_terminal_sessions`,建议后续建 K8s 独立会话表。
|
||
|
||
---
|
||
|
||
## 7. kubectl 终端
|
||
|
||
### 7.1 后端链路
|
||
|
||
```text
|
||
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 后端链路
|
||
|
||
```text
|
||
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 实时查看
|
||
|
||
```text
|
||
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 会话
|
||
|
||
```text
|
||
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 命令审计
|
||
|
||
```text
|
||
sys_session_commands
|
||
id
|
||
session_id
|
||
session_type: terminal | rdp
|
||
user_id / username
|
||
server_id / server_name
|
||
command
|
||
output_size
|
||
timestamp
|
||
```
|
||
|
||
### 10.3 策略配置
|
||
|
||
```text
|
||
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 |
|