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

488 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |