feat: 完善堡垒机与 Agent 智能运维能力
- 支持 Windows Agent 构建、安装脚本和统一上报协议 - 增强 RDP 剪贴板、虚拟盘和文件传输能力 - 新增 AIOps 上下文工具、告警 RCA trace 落库和回放接口 - 重构 docs 模块文档并补充终端、堡垒机、监控、AI Agent 设计
This commit is contained in:
1 parent
a9513c2056
commit
e0d652d211
169 files changed
+12192
-9116
No files matched your search
@@ -0,0 +1,487 @@
|
||||
# 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 |
|
||||
Reference in new issue
Block a user