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,454 @@
|
||||
# AIOps Web RDP 远程桌面
|
||||
|
||||
> 作者:Logan.Li
|
||||
> 更新:2026-07-05
|
||||
> 归档副本:`/opt/rdp/docs/rdp.md` · 变更记录:`/opt/rdp/CHANGELOG.md`
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
AIOps RDP 是内置在浏览器里的 Windows 远程桌面能力,采用“后端 Go 自研 RDP 协议栈 + WebSocket + 前端 Canvas”的方式实现。当前功能已从远程桌面连接推进到可用堡垒机阶段,支持图形会话、键鼠输入、双向剪贴板、RDPDR 虚拟磁盘、文件上传下载、会话录像回放、实时审计和全局会话安全策略。
|
||||
|
||||
当前 RDP 不再依赖 SMB、445 端口或第三方 RDP 实现。文件传输走 RDP 自身的 `rdpdr` 设备重定向通道,远端资源管理器中显示为 `Go-client 上的 AIOps`,UNC 路径为 `\\Go-client\AIOps`。// Logan.Li
|
||||
|
||||
## 文档阅读路径
|
||||
|
||||
本目录按“能让其他项目复刻实现”的标准组织:
|
||||
|
||||
| 文档 | 适合读者 | 内容 |
|
||||
|------|----------|------|
|
||||
| [README.md](./README.md) | 产品、架构、联调人员 | 功能范围、调用链、文件清单、现有限制 |
|
||||
| [implementation-spec.md](./implementation-spec.md) | 后端、前端、测试工程师 | 从零实现 RDP Web 网关所需的协议分层、状态机、WebSocket 帧、剪贴板、虚拟盘、审计和验收用例 |
|
||||
|
||||
如果要在另一个项目中实现同类能力,建议先按 `implementation-spec.md` 搭出最小闭环,再回到本文档对照 AIOps 的实际文件落点。// Logan.Li
|
||||
|
||||
| 能力 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| RDP 图形连接 | 已完成 | 自研 X.224 / TLS / CredSSP / MCS / FastPath / Bitmap |
|
||||
| 键鼠输入 | 已完成 | Fast-Path Input,支持组合键审计与服务端修饰键补齐 |
|
||||
| 双向剪贴板 | 已完成 | `cliprdr` 通道 + 工具按钮 + Ctrl/Command 快捷键 |
|
||||
| 文件上传 | 已完成 | 浏览器选择/拖拽文件,暂存到远端虚拟盘 `Upload` |
|
||||
| 文件下载 | 已完成 | 远端文件复制到虚拟盘 `Download`,浏览器自动下载 |
|
||||
| 会话录像 | 已完成 | RDP bitmap 快照录制,过滤启动黑帧,支持历史回放 |
|
||||
| 实时审计 | 已完成 | 支持 RDP 实时画面订阅 `/ws/rdp-audit/:sessionId` |
|
||||
| 会话水印 | 已完成 | RDP/SSH 会话显示当前登录用户水印 |
|
||||
| 会话策略 | 已完成 | 水印、录像、剪贴板、上传、下载、命令策略、审批开关 |
|
||||
| SMB 文件传输 | 已移除 | 不再使用 445 端口和管理共享 |
|
||||
|
||||
---
|
||||
|
||||
## 用户体验
|
||||
|
||||
### 入口
|
||||
|
||||
1. 进入 `服务器管理`。
|
||||
2. Windows / RDP 资产点击终端按钮。
|
||||
3. 弹出 RDP 图形窗口。
|
||||
4. 顶部工具图标打开 RDP 工具面板。
|
||||
|
||||
### RDP 窗口
|
||||
|
||||
| 区域 | 说明 |
|
||||
|------|------|
|
||||
| 顶栏 | 显示资产名称、IP、连接状态、工具、最大化、关闭 |
|
||||
| Canvas | 渲染远端桌面图形帧 |
|
||||
| 水印层 | 显示当前登录用户,例如 `admin` |
|
||||
| 工具面板 | 剪贴板同步、虚拟盘上传说明、上传进度 |
|
||||
|
||||
13 寸屏幕默认使用较小窗口尺寸,避免看不到窗口边框和工具栏。最大化时前端只缩放现有 Canvas,不重建 RDP 会话,避免登录阶段闪屏。// Logan.Li
|
||||
|
||||
### 虚拟盘体验
|
||||
|
||||
远端 Windows 中应看到:
|
||||
|
||||
```text
|
||||
此电脑
|
||||
└── Go-client 上的 AIOps
|
||||
├── Upload
|
||||
└── Download
|
||||
```
|
||||
|
||||
| 目录 | 用途 |
|
||||
|------|------|
|
||||
| `Upload` | 浏览器上传的文件会出现在这里,远端可复制到桌面或目标目录 |
|
||||
| `Download` | 远端文件复制/拖入这里后,AIOps 捕获并触发本地浏览器下载 |
|
||||
|
||||
---
|
||||
|
||||
## 架构与调用链
|
||||
|
||||
```text
|
||||
frontend/src/components/server/RdpDialog.tsx
|
||||
│ WebSocket /ws/rdp?id=&width=&height=&token=
|
||||
│ Canvas 渲染、键鼠捕获、快捷键、下载触发
|
||||
▼
|
||||
backend/controller/rdp.go
|
||||
│ 创建 RDPSession、加载会话策略、审计、录屏、WebSocket frame sink
|
||||
│ loadRDPCredential() → rdp.Engine.Run()
|
||||
▼
|
||||
backend/pkg/rdp/session_engine.go
|
||||
│ OpenSession() → readLoop() → handleEvent()
|
||||
├── 图形帧:FastPath / SlowPath Bitmap → RGBA → WebSocket binary
|
||||
├── 输入:浏览器 key/mouse/wheel → Fast-Path Input
|
||||
├── 剪贴板:cliprdr 静态虚拟通道
|
||||
└── 文件:rdpdr 静态虚拟通道
|
||||
```
|
||||
|
||||
文件下载链路:
|
||||
|
||||
```text
|
||||
Windows 文件 → \\Go-client\AIOps\Download
|
||||
▼
|
||||
rdpdr Device I/O 写入捕获
|
||||
▼
|
||||
pkg/rdp/filestage.Put(name, data, 30min)
|
||||
▼
|
||||
WebSocket frame: { type: "rdp_file_download", name, url }
|
||||
▼
|
||||
浏览器访问 /api/v1/rdp-staged/:token 自动下载
|
||||
```
|
||||
|
||||
文件上传链路:
|
||||
|
||||
```text
|
||||
浏览器选择/拖拽文件
|
||||
▼
|
||||
RdpToolsPanel FileReader 读取为 base64
|
||||
▼
|
||||
WebSocket event: { type: "rdp_file_upload", name, text }
|
||||
▼
|
||||
rdpdr.Handler.AddUpload()
|
||||
▼
|
||||
Windows 资源管理器读取 \\Go-client\AIOps\Upload\文件名
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 后端文件清单
|
||||
|
||||
### 协议栈 `backend/pkg/rdp/`
|
||||
|
||||
| 文件 / 目录 | 说明 |
|
||||
|-------------|------|
|
||||
| `engine.go` | `Engine` 接口,当前固定使用自研 `SessionEngine` |
|
||||
| `session_engine.go` | 图形会话主循环、输入事件、虚拟通道分发、黑帧过滤 |
|
||||
| `session_connect.go` | `OpenSession`、CredSSP、MCS、SuppressOutput 激活 |
|
||||
| `mcs_probe.go` | MCS/GCC/ClientInfo/ConfirmActive 构建与探测 |
|
||||
| `packet_io.go` | TPKT / FastPath 读写 |
|
||||
| `fastpath_recv.go` | FastPath 更新、SurfaceCmd、MPPC 解压入口 |
|
||||
| `bitmap_decode.go` | Bitmap/RLE 解码与 RGBA 转换 |
|
||||
| `input_fastpath.go` | 键盘、鼠标、滚轮 Fast-Path Input 编码 |
|
||||
| `keymap.go` | 浏览器 `KeyboardEvent.code` 到 RDP 扫描码映射 |
|
||||
| `mppc.go` | MPPC-64K 解压器 |
|
||||
| `transport/` | TPKT、X.224、TLS |
|
||||
| `credssp/` | CredSSP + NTLMv2 + ASN.1 |
|
||||
| `rle/` | 位图 RLE 解压 |
|
||||
| `channels/cliprdr/` | RDP 剪贴板静态虚拟通道 |
|
||||
| `channels/rdpdr/` | RDP 设备重定向虚拟磁盘 |
|
||||
| `filestage/` | Download 捕获文件的内存暂存与 token |
|
||||
| `trace/` | `RDP_DEBUG=1` 协议级调试日志 |
|
||||
|
||||
### 控制器 / 模型
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `backend/controller/rdp.go` | WebSocket 会话、策略拦截、审计、录屏、实时广播 |
|
||||
| `backend/controller/rdp_session.go` | RDP 会话列表、详情、回放、终止、录像下载 |
|
||||
| `backend/controller/rdp_staged.go` | RDPDR Download 捕获文件的浏览器下载入口 |
|
||||
| `backend/controller/rdp_cred.go` | RDP 凭据加载与解密 |
|
||||
| `backend/model/rdp_session.go` | `RDPSession` 表 |
|
||||
| `backend/model/audit_setting.go` | 会话安全策略字段 |
|
||||
|
||||
### 前端
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `frontend/src/components/server/RdpDialog.tsx` | RDP 弹窗、Canvas、WebSocket、键鼠、下载触发 |
|
||||
| `frontend/src/components/server/RdpToolsPanel.tsx` | 剪贴板、上传、虚拟盘说明、上传进度 |
|
||||
| `frontend/src/components/common/SessionWatermark.tsx` | 会话水印 |
|
||||
| `frontend/src/components/system/RdpAuditViewer.tsx` | RDP 历史回放 / 实时审计查看 |
|
||||
| `frontend/src/components/system/SystemSettingsTab.tsx` | 会话安全策略开关入口 |
|
||||
| `frontend/src/store/index.ts` | `useSessionSecurityStore` 前端策略缓存 |
|
||||
|
||||
---
|
||||
|
||||
## WebSocket 协议
|
||||
|
||||
### 服务端到浏览器
|
||||
|
||||
JSON frame:
|
||||
|
||||
```json
|
||||
{ "type": "session", "message": "123", "width": 900, "height": 596 }
|
||||
{ "type": "status", "message": "正在连接 RDP 1.2.3.4:3389" }
|
||||
{ "type": "error", "message": "连接失败" }
|
||||
{ "type": "clipboard", "text": "远端剪贴板内容" }
|
||||
{ "type": "rdp_file_download", "name": "a.txt", "url": "/api/v1/rdp-staged/<token>" }
|
||||
```
|
||||
|
||||
Binary frame:
|
||||
|
||||
```text
|
||||
magic: "RDPR"
|
||||
x: uint32 little-endian
|
||||
y: uint32 little-endian
|
||||
width: uint32 little-endian
|
||||
height: uint32 little-endian
|
||||
rgba: width * height * 4
|
||||
```
|
||||
|
||||
历史回放仍使用 JSON `bitmap` 快照:
|
||||
|
||||
```json
|
||||
{ "type": "bitmap", "x": 0, "y": 0, "width": 900, "height": 596, "image": "data:image/png;base64,..." }
|
||||
```
|
||||
|
||||
### 浏览器到服务端
|
||||
|
||||
```json
|
||||
{ "type": "mouse_move", "x": 100, "y": 200 }
|
||||
{ "type": "mouse_down", "x": 100, "y": 200, "button": 0 }
|
||||
{ "type": "mouse_up", "x": 100, "y": 200, "button": 0 }
|
||||
{ "type": "wheel", "x": 100, "y": 200, "delta_y": -120 }
|
||||
{ "type": "key", "code": "KeyA", "key": "a", "down": true, "ctrl": false }
|
||||
{ "type": "clipboard", "text": "本地剪贴板内容" }
|
||||
{ "type": "remote_copy" }
|
||||
{ "type": "rdp_file_upload", "name": "php.ini", "text": "base64..." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 剪贴板机制
|
||||
|
||||
| 方向 | 入口 | 机制 |
|
||||
|------|------|------|
|
||||
| 本地到远端 | 工具面板“粘贴到远端”、Canvas 粘贴、Ctrl/Command+V | 浏览器读取本地剪贴板,发送 `clipboard` event,后端写入 RDP `cliprdr` |
|
||||
| 远端到本地 | 工具面板“复制远端内容”、远端 Ctrl+C/Ctrl+X | 后端发送/识别复制事件,通过 `cliprdr` 请求远端文本并回传浏览器 |
|
||||
|
||||
注意:
|
||||
|
||||
- 浏览器读取/写入系统剪贴板受浏览器权限限制,需要页面处于用户交互上下文。
|
||||
- 远端 Ctrl+C/Ctrl+X 会按普通键盘事件发送给 Windows,同时后端延迟请求远端剪贴板文本。
|
||||
- 审计会记录 `[RDP复制] Ctrl+C`、`[RDP剪切] Ctrl+X`、`[RDP粘贴] ...`、`[RDP粘贴快捷键] Ctrl+V`。
|
||||
|
||||
---
|
||||
|
||||
## RDPDR 虚拟磁盘
|
||||
|
||||
RDPDR 使用静态虚拟通道 `rdpdr`,实现最小可用的磁盘设备重定向:
|
||||
|
||||
| 项目 | 当前值 |
|
||||
|------|--------|
|
||||
| Client Name | `Go-client` |
|
||||
| Drive Name | `AIOps` |
|
||||
| UNC | `\\Go-client\AIOps` |
|
||||
| 远端展示 | `Go-client 上的 AIOps` |
|
||||
| 固定目录 | `Upload`、`Download` |
|
||||
|
||||
### Upload
|
||||
|
||||
上传流程:
|
||||
|
||||
1. 用户在 RDP 工具面板选择或拖拽本地文件。
|
||||
2. 前端读取文件并通过 WebSocket 发送到后端。
|
||||
3. 后端加入 rdpdr upload map。
|
||||
4. 远端资源管理器进入 `\\Go-client\AIOps\Upload` 后可看到文件。
|
||||
5. 用户可在远端复制到桌面或目标目录。
|
||||
|
||||
当前前端单文件上传限制为 50 MB。上传进度表示浏览器读取和发送到 WebSocket 的进度,不是远端 Windows 复制进度。
|
||||
|
||||
### Download
|
||||
|
||||
下载流程:
|
||||
|
||||
1. 用户在远端 Windows 中把文件复制/拖入 `\\Go-client\AIOps\Download`。
|
||||
2. rdpdr 捕获远端写入数据。
|
||||
3. 后端将文件暂存到 `filestage`,默认 30 分钟有效。
|
||||
4. 后端向浏览器发送 `rdp_file_download` frame。
|
||||
5. 浏览器自动访问 `/api/v1/rdp-staged/:token` 下载到本地下载目录。
|
||||
|
||||
### 不再使用 SMB
|
||||
|
||||
已移除 SMB 直传和 445 端口依赖。原因:
|
||||
|
||||
- 外网访问 Windows 445 通常不可达或不安全。
|
||||
- 管理共享需要额外权限,部署和排障成本高。
|
||||
- 堡垒机场景更适合走 RDP 自身的设备重定向能力。
|
||||
|
||||
---
|
||||
|
||||
## 会话安全策略
|
||||
|
||||
入口:
|
||||
|
||||
```text
|
||||
系统管理 → 系统设置 → 安全 → 会话设置
|
||||
```
|
||||
|
||||
策略字段存储在 `sys_audit_settings`:
|
||||
|
||||
| 字段 | 默认 | 影响 |
|
||||
|------|------|------|
|
||||
| `session_watermark_enabled` | true | RDP/SSH 会话显示当前用户水印 |
|
||||
| `session_record_enabled` | true | 是否保存 RDP 录像数据 |
|
||||
| `clipboard_enabled` | true | 是否允许剪贴板同步 |
|
||||
| `file_upload_enabled` | true | 是否允许 RDP 文件上传 |
|
||||
| `file_download_enabled` | true | 是否允许 RDP 文件下载 |
|
||||
| `command_policy_enabled` | false | 命令策略预留开关 |
|
||||
| `approval_required` | false | 访问审批预留开关 |
|
||||
|
||||
RDP 后端在 WebSocket 读事件时会执行策略拦截:
|
||||
|
||||
| 事件 | 策略 |
|
||||
|------|------|
|
||||
| `clipboard` / `remote_copy` | `clipboard_enabled` |
|
||||
| `file_upload` / `rdp_file_upload` | `file_upload_enabled` |
|
||||
| `file_download` | `file_download_enabled` |
|
||||
|
||||
前端也会同步禁用对应按钮,但后端拦截才是最终边界。// Logan.Li
|
||||
|
||||
---
|
||||
|
||||
## 审计与回放
|
||||
|
||||
### 会话录像
|
||||
|
||||
RDP 会话关闭时写入 `rdp_sessions.record_data` 和 `record_size`。录制策略:
|
||||
|
||||
- 非 bitmap 状态帧直接记录。
|
||||
- bitmap 帧通过后端 framebuffer 合成整屏快照。
|
||||
- 启动阶段黑屏帧会被过滤,减少历史回放黑屏片段。
|
||||
- 快照最小间隔约 500ms,避免小块 bitmap 过密导致回放碎片化。
|
||||
- 关闭 `session_record_enabled` 后不写入录像数据。
|
||||
|
||||
### 操作审计
|
||||
|
||||
RDP 输入会进入 `session_commands`,用于统一审计:
|
||||
|
||||
| 类型 | 示例 |
|
||||
|------|------|
|
||||
| 文本输入 | `[RDP输入] hello` |
|
||||
| 快捷键 | `[RDP复制] Ctrl+C`、`[RDP剪切] Ctrl+X` |
|
||||
| 粘贴 | `[RDP粘贴] http://localhost:8080/servers` |
|
||||
| 上传 | `[RDP上传到虚拟盘] php.ini` |
|
||||
| 下载 | `[RDP下载] a.txt` |
|
||||
|
||||
鼠标移动、点击轨迹只进入图形录像,不写命令审计,避免审计列表噪声。
|
||||
|
||||
### 实时审计
|
||||
|
||||
活动 RDP 会话可通过 `/ws/rdp-audit/:sessionId` 实时订阅图形帧,供审计页面查看在线会话画面。
|
||||
|
||||
---
|
||||
|
||||
## 闪屏与画面优化
|
||||
|
||||
当前已做的优化:
|
||||
|
||||
| 问题 | 处理 |
|
||||
|------|------|
|
||||
| 登录初期黑屏闪烁 | 后端跳过启动阶段纯黑全屏 bitmap |
|
||||
| 最大化导致重连 | 前端只缩放 Canvas,不重建 WebSocket |
|
||||
| 回放很多小块 | 后端 framebuffer 合成整屏快照再录制 |
|
||||
| 高频 mousemove 堆积 | 前端 16ms 合并移动事件,后端队列优先保留关键输入 |
|
||||
| 13 寸屏看不到工具栏 | 默认窗口缩小,最大化再使用全屏空间 |
|
||||
|
||||
---
|
||||
|
||||
## 环境变量与调试
|
||||
|
||||
| 环境变量 | 行为 |
|
||||
|----------|------|
|
||||
| `RDP_DEBUG=1` | 打开 `[RDP:TRACE]` 协议级日志 |
|
||||
| `RDP_ENGINE` | 已不再参与引擎选择,保留也不会改变行为 |
|
||||
|
||||
常用日志过滤:
|
||||
|
||||
```bash
|
||||
tail -f logs/aiops-local.log | rg "RDP|rdpdr|cliprdr"
|
||||
```
|
||||
|
||||
常见日志含义:
|
||||
|
||||
| 日志 | 含义 |
|
||||
|------|------|
|
||||
| `[RDP] ws connected` | 浏览器 RDP WebSocket 已连接 |
|
||||
| `[RDP] engine start` | 后端开始连接远端 3389 |
|
||||
| `[RDP] bitmap session=...` | 收到并发送图形帧 |
|
||||
| `[rdpdr] device list announced` | 虚拟盘设备已公告 |
|
||||
| `[rdpdr] upload staged` | 文件已进入虚拟盘 Upload |
|
||||
| `[rdpdr] download captured` | 远端写入 Download 已被捕获 |
|
||||
| `[cliprdr]` | 剪贴板虚拟通道交互 |
|
||||
|
||||
---
|
||||
|
||||
## 路由
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `GET /ws/rdp` | RDP 图形会话 WebSocket |
|
||||
| `GET /ws/rdp-audit/:sessionId` | RDP 活动会话实时审计 WebSocket |
|
||||
| `GET /api/v1/rdp-staged/:token` | RDPDR Download 暂存文件下载 |
|
||||
| `GET /api/v1/rdp-sessions` | RDP 会话列表 |
|
||||
| `GET /api/v1/rdp-sessions/:id` | RDP 会话详情 |
|
||||
| `GET /api/v1/rdp-sessions/:id/record` | RDP 录像数据 |
|
||||
| `GET /api/v1/rdp-sessions/:id/download` | RDP 录像下载 |
|
||||
| `POST /api/v1/rdp-sessions/:id/kill` | 终止活动 RDP 会话 |
|
||||
| `POST /api/v1/rdp-sessions/:id/replay-view` | 记录回放查看行为 |
|
||||
| `GET /api/v1/session-commands?session_type=rdp` | RDP 输入/文件/剪贴板操作审计 |
|
||||
| `GET /api/v1/admin/audit-settings` | 获取会话安全策略 |
|
||||
| `PUT /api/v1/admin/audit-settings` | 保存会话安全策略 |
|
||||
|
||||
---
|
||||
|
||||
## 当前限制
|
||||
|
||||
| 限制 | 说明 |
|
||||
|------|------|
|
||||
| 大文件上传 | 前端当前限制 50 MB,尚未实现分片、断点续传 |
|
||||
| 大文件下载 | 当前以内存暂存 token 方式实现,尚未实现断点下载 |
|
||||
| RDPGFX/H.264 | 未实现,当前以 bitmap/RLE/SurfaceCmd 为主 |
|
||||
| 动态虚拟通道 | `drdynvc` 当前识别但不处理 |
|
||||
| 复杂文件操作 | 重命名、属性编辑、复杂 Explorer 操作仍需继续压测 |
|
||||
| TLS 指纹 | RDP 自签证书当前 `InsecureSkipVerify`,后续可做 TOFU 指纹落库 |
|
||||
| 细粒度授权 | 当前是全局会话策略,尚未做到用户/角色/资产/协议维度的 ABAC |
|
||||
|
||||
---
|
||||
|
||||
## 验证清单
|
||||
|
||||
### 构建
|
||||
|
||||
```bash
|
||||
cd frontend && npx tsc --noEmit
|
||||
cd backend && env GOSUMDB=sum.golang.org GOTOOLCHAIN=auto go test ./pkg/rdp/... ./controller
|
||||
./run.sh build
|
||||
```
|
||||
|
||||
### 手动验证
|
||||
|
||||
1. 登录 `http://localhost:8080`。
|
||||
2. 进入 `服务器管理`,打开 Windows 资产 RDP。
|
||||
3. 确认 RDP 桌面画面稳定,无明显登录黑屏闪烁。
|
||||
4. 确认远端 `此电脑` 能看到 `Go-client 上的 AIOps`。
|
||||
5. 工具面板上传文件,远端进入 `\\Go-client\AIOps\Upload` 可打开/复制。
|
||||
6. 远端文件复制到 `\\Go-client\AIOps\Download`,浏览器触发下载。
|
||||
7. 本地复制文本后在远端 Ctrl+V 或工具面板粘贴。
|
||||
8. 远端选中文本 Ctrl+C 后,工具面板复制远端内容并在本地粘贴。
|
||||
9. 进入 `系统管理 → 系统设置 → 安全 → 会话设置`,关闭剪贴板/上传/下载后验证前端按钮禁用和后端策略拦截。
|
||||
10. 进入审计页面查看 RDP 会话、操作审计和历史回放。
|
||||
|
||||
---
|
||||
|
||||
## 外部项目借鉴边界
|
||||
|
||||
| 项目 | 可借鉴点 | 不直接采用原因 |
|
||||
|------|----------|----------------|
|
||||
| FreeRDP | 协议完整性、通道、编解码、客户端能力矩阵 | C 生态和体量过大,不适合直接嵌入 |
|
||||
| Apache Guacamole | Web 网关、协议代理、录制与前端桥接分层 | 依赖 guacd/C,不符合单 Go 后端目标 |
|
||||
| IronRDP | 现代模块化、状态机测试、安全边界 | Rust 生态,适合作为测试和分层参考 |
|
||||
| Teleport | 身份、RBAC、审计、Desktop Gateway 架构 | 产品目标偏完整堡垒机,当前只借鉴分层思路 |
|
||||
| tomatome/grdp / nakagami/grdp | Go 协议状态机、能力集、bitmap/input 参考 | 第三方实现已移除,仅保留 `/opt` 备份用于协议对照 |
|
||||
|
||||
第三方源码只能作为协议行为和工程分层参考;涉及 GPL 的项目不得直接搬运实现。// Logan.Li
|
||||
@@ -0,0 +1,553 @@
|
||||
# AIOps RDP 实现规格
|
||||
|
||||
> 更新:2026-07-05
|
||||
> 目标:让另一个项目仅阅读本文档,也能实现一个可用的 Web RDP 堡垒机会话。
|
||||
> 范围:RDP 图形连接、键鼠输入、剪贴板、RDPDR 虚拟盘、文件上传下载、录像回放、实时审计、安全策略。
|
||||
|
||||
---
|
||||
|
||||
## 1. 功能边界
|
||||
|
||||
### 1.1 必须实现
|
||||
|
||||
| 能力 | 验收标准 |
|
||||
|------|----------|
|
||||
| 图形登录 | 浏览器打开 Windows 桌面,登录后能稳定看到完整画面 |
|
||||
| 键鼠输入 | 鼠标移动/点击/滚轮、普通键、组合键能到达远端 |
|
||||
| 双向剪贴板 | 本地复制到远端、远端 Ctrl+C 后复制到浏览器 |
|
||||
| 文件上传 | 浏览器上传文件后,远端 `\\Go-client\AIOps\Upload` 可见 |
|
||||
| 文件下载 | 远端文件写入 `\\Go-client\AIOps\Download` 后,本地浏览器下载 |
|
||||
| 录像回放 | 会话结束后能按时间顺序播放图形快照 |
|
||||
| 实时审计 | 审计端可只读观看活动 RDP 会话 |
|
||||
| 安全策略 | 后端能强制禁用剪贴板、上传、下载、录像、水印 |
|
||||
|
||||
### 1.2 不建议作为第一版实现
|
||||
|
||||
| 能力 | 原因 |
|
||||
|------|------|
|
||||
| RDPGFX/H.264 | 协议复杂,先用 Bitmap/FastPath 能完成可用闭环 |
|
||||
| 动态虚拟通道 `drdynvc` | 文件和剪贴板可先用静态通道完成 |
|
||||
| SMB 文件传输 | 外网 445 经常不可达,权限和安全边界差 |
|
||||
| 远端任意路径浏览 | 容易变成文件管理器和权限绕过问题,先用虚拟盘 |
|
||||
| 断点续传 | 大文件体验优化,基础链路稳定后再做 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 端到端架构
|
||||
|
||||
```text
|
||||
Browser
|
||||
├── RdpDialog.tsx
|
||||
│ ├── Canvas 绘制 RDPR 二进制帧
|
||||
│ ├── 捕获 mouse/key/wheel/paste/drop
|
||||
│ └── 发送 JSON input event
|
||||
└── RdpToolsPanel.tsx
|
||||
├── 粘贴到远端
|
||||
├── 复制远端内容
|
||||
└── 文件上传
|
||||
|
||||
Backend HTTP/WebSocket
|
||||
├── /ws/rdp
|
||||
│ ├── JWT 鉴权
|
||||
│ ├── 加载资产和 RDP 凭据
|
||||
│ ├── 加载会话安全策略
|
||||
│ ├── 创建 sys_rdp_sessions
|
||||
│ ├── 启动 rdp.Engine.Run()
|
||||
│ └── 写入录像、审计、实时广播
|
||||
├── /ws/rdp-audit/:sessionId
|
||||
└── /api/v1/rdp-staged/:token
|
||||
|
||||
RDP Engine
|
||||
├── TCP + TPKT + X.224
|
||||
├── TLS + CredSSP/NLA
|
||||
├── MCS/GCC/Channel Join
|
||||
├── DemandActive/ConfirmActive/FontList/SuppressOutput
|
||||
├── FastPath Bitmap/SurfaceCmd
|
||||
├── FastPath Input
|
||||
├── cliprdr 静态虚拟通道
|
||||
└── rdpdr 静态虚拟通道
|
||||
```
|
||||
|
||||
核心分层原则:
|
||||
|
||||
1. 控制器负责鉴权、策略、审计、WebSocket、数据库。
|
||||
2. RDP 引擎只负责协议状态机和帧收发,不依赖 Gin/GORM/React。
|
||||
3. 前端只知道 WebSocket 帧协议,不知道 CredSSP、MCS、PDU 细节。
|
||||
4. 文件传输不走远端 SMB,不依赖远端开放 445。
|
||||
|
||||
---
|
||||
|
||||
## 3. 后端接口契约
|
||||
|
||||
### 3.1 连接配置
|
||||
|
||||
```go
|
||||
type Config struct {
|
||||
Host string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
Domain string
|
||||
Security string
|
||||
Width int
|
||||
Height int
|
||||
Debug bool
|
||||
}
|
||||
```
|
||||
|
||||
实现要求:
|
||||
|
||||
- `Password` 只允许在服务端内存中存在,不返回给浏览器。
|
||||
- `Width`、`Height` 是远端桌面初始分辨率,不是浏览器 CSS 尺寸。
|
||||
- 浏览器最大化优先缩放 Canvas,不要重建 RDP 会话。
|
||||
|
||||
### 3.2 浏览器输入事件
|
||||
|
||||
```json
|
||||
{ "type": "mouse_move", "x": 100, "y": 200 }
|
||||
{ "type": "mouse_down", "x": 100, "y": 200, "button": 0 }
|
||||
{ "type": "mouse_up", "x": 100, "y": 200, "button": 0 }
|
||||
{ "type": "wheel", "x": 100, "y": 200, "delta_y": -120 }
|
||||
{ "type": "key", "code": "KeyC", "key": "c", "down": true, "ctrl": true }
|
||||
{ "type": "clipboard", "text": "from local" }
|
||||
{ "type": "remote_copy" }
|
||||
{ "type": "rdp_file_upload", "name": "php.ini", "text": "base64..." }
|
||||
```
|
||||
|
||||
处理规则:
|
||||
|
||||
- `mouse_move` 高频事件可丢弃或合并。
|
||||
- `mouse_down` / `mouse_up` / `key down` / `key up` 不允许丢弃。
|
||||
- `key` 事件必须带 `ctrl`、`alt`、`shift`、`meta`,后端根据状态补齐修饰键。
|
||||
- `remote_copy` 表示请求后端向远端剪贴板索取文本。
|
||||
- `rdp_file_upload.text` 是 base64 文件内容,第一版可限制 50 MB。
|
||||
|
||||
### 3.3 服务端帧
|
||||
|
||||
JSON 帧:
|
||||
|
||||
```json
|
||||
{ "type": "session", "message": "43", "width": 900, "height": 596 }
|
||||
{ "type": "status", "message": "正在连接 RDP 1.2.3.4:3389" }
|
||||
{ "type": "error", "message": "连接失败" }
|
||||
{ "type": "clipboard", "text": "from remote" }
|
||||
{ "type": "rdp_file_download", "name": "a.txt", "url": "/api/v1/rdp-staged/token" }
|
||||
```
|
||||
|
||||
二进制图形帧:
|
||||
|
||||
```text
|
||||
offset size type desc
|
||||
0 4 bytes magic, fixed "RDPR"
|
||||
4 4 uint32 LE x
|
||||
8 4 uint32 LE y
|
||||
12 4 uint32 LE width
|
||||
16 4 uint32 LE height
|
||||
20 n bytes RGBA, length = width * height * 4
|
||||
```
|
||||
|
||||
实现要求:
|
||||
|
||||
- 实时画面优先使用 `RDPR + RGBA`,避免 PNG/base64 带来的 CPU 和体积开销。
|
||||
- 历史回放可以保存 PNG data URL,便于持久化和跨版本播放。
|
||||
- 服务端发送图形帧前必须裁剪到桌面范围,避免前端越界写 Canvas。
|
||||
|
||||
---
|
||||
|
||||
## 4. RDP 协议状态机
|
||||
|
||||
### 4.1 连接阶段
|
||||
|
||||
```text
|
||||
TCP connect
|
||||
→ X.224 Connection Request
|
||||
→ X.224 Connection Confirm
|
||||
→ TLS handshake
|
||||
→ CredSSP/NLA
|
||||
→ MCS Connect Initial
|
||||
→ MCS Connect Response
|
||||
→ ErectDomainRequest
|
||||
→ AttachUserRequest/Confirm
|
||||
→ Channel Join: user + global + cliprdr + rdpdr
|
||||
→ Client Info
|
||||
→ DemandActive
|
||||
→ ConfirmActive
|
||||
→ Synchronize / Control Cooperate / Control Request Control
|
||||
→ FontList / FontMap
|
||||
→ SuppressOutput(ALLOW_DISPLAY_UPDATES)
|
||||
→ Bitmap/FastPath updates
|
||||
```
|
||||
|
||||
关键点:
|
||||
|
||||
- 每一次协议探测失败后,下一次重试要新建 TCP 连接。
|
||||
- CredSSP 的证书公钥绑定必须按服务端证书 `SubjectPublicKey` 做,不要误用完整 SPKI。
|
||||
- 收到 `DemandActive` 后才能发送 `ConfirmActive`,能力集必须包含图形、输入、虚拟通道相关能力。
|
||||
- `SuppressOutput(ALLOW_DISPLAY_UPDATES)` 对部分 Windows 主机很关键,否则登录后可能长时间无画面。
|
||||
|
||||
### 4.2 图形更新处理
|
||||
|
||||
```text
|
||||
FastPath/SlowPath packet
|
||||
→ parse update type
|
||||
→ bitmap/surface command
|
||||
→ MPPC decompress if compressed
|
||||
→ RLE decode if bitmap compressed
|
||||
→ convert BGRA/RGB565 to RGBA
|
||||
→ draw into backend framebuffer
|
||||
→ flush dirty rectangle
|
||||
→ send RDPR binary frame
|
||||
→ throttle snapshot into recorder
|
||||
```
|
||||
|
||||
实现要求:
|
||||
|
||||
- 后端维护完整 framebuffer,不能只保存最后一个小块,否则历史回放会碎片化。
|
||||
- 实时可发送脏矩形,录像建议按固定间隔保存合成后的快照。
|
||||
- 启动阶段纯黑全屏帧应过滤,减少登录闪屏和黑屏回放。
|
||||
|
||||
### 4.3 输入处理
|
||||
|
||||
| 浏览器事件 | RDP 行为 |
|
||||
|------------|----------|
|
||||
| `mouse_move` | Pointer move |
|
||||
| `mouse_down` | Pointer button down |
|
||||
| `mouse_up` | Pointer button up |
|
||||
| `wheel` | Wheel event |
|
||||
| `key down/up` | Scancode keyboard event |
|
||||
| `clipboard` | 写入 cliprdr 本地剪贴板并通知远端 |
|
||||
| `remote_copy` | 请求远端当前剪贴板文本 |
|
||||
|
||||
组合键规则:
|
||||
|
||||
- `Ctrl+C`、`Ctrl+X` 必须同时发修饰键和主键,不能把 `c`、`x` 当普通输入。
|
||||
- macOS 浏览器中的 `Command+C/V` 可映射成远端 `Ctrl+C/V`。
|
||||
- 后端审计应识别 `[RDP复制] Ctrl+C`、`[RDP剪切] Ctrl+X`、`[RDP粘贴快捷键] Ctrl+V`。
|
||||
|
||||
---
|
||||
|
||||
## 5. 剪贴板实现
|
||||
|
||||
### 5.1 本地到远端
|
||||
|
||||
```text
|
||||
Browser navigator.clipboard.readText()
|
||||
→ WebSocket { type: "clipboard", text }
|
||||
→ backend policy check clipboard_enabled
|
||||
→ cliprdr set local data
|
||||
→ send FormatList / FormatDataResponse
|
||||
→ Windows remote clipboard update
|
||||
```
|
||||
|
||||
验收:
|
||||
|
||||
1. 本地复制 `hello`。
|
||||
2. 点击“粘贴到远端”或在 RDP Canvas 执行 Ctrl/Command+V。
|
||||
3. 远端文本框出现 `hello`。
|
||||
|
||||
### 5.2 远端到本地
|
||||
|
||||
```text
|
||||
User selects text in Windows
|
||||
→ remote Ctrl+C
|
||||
→ Windows updates remote clipboard
|
||||
→ cliprdr FormatList arrives
|
||||
→ backend requests CF_UNICODETEXT
|
||||
→ backend WebSocket { type: "clipboard", text }
|
||||
→ Browser navigator.clipboard.writeText(text)
|
||||
```
|
||||
|
||||
验收:
|
||||
|
||||
1. 远端记事本输入 `rdp-copy-ok` 并选中。
|
||||
2. 在远端按 Ctrl+C。
|
||||
3. 点击“复制远端内容”或等待回传。
|
||||
4. 本地 Command+V 能粘贴 `rdp-copy-ok`。
|
||||
|
||||
常见失败:
|
||||
|
||||
| 现象 | 原因 | 处理 |
|
||||
|------|------|------|
|
||||
| 远端 Ctrl+C 只输入 c | 修饰键状态未发送或被前端拦截 | key event 带 `ctrl=true`,后端补发 Ctrl down/up |
|
||||
| 浏览器无法写入剪贴板 | 浏览器权限限制 | 必须在用户点击按钮的交互上下文执行 |
|
||||
| 工具提示远端剪贴板为空 | 未收到 FormatList 或请求过早 | Ctrl+C 后延迟 200-500ms 再请求 |
|
||||
|
||||
---
|
||||
|
||||
## 6. RDPDR 虚拟盘文件传输
|
||||
|
||||
### 6.1 远端展示
|
||||
|
||||
```text
|
||||
此电脑
|
||||
└── Go-client 上的 AIOps
|
||||
├── Upload
|
||||
└── Download
|
||||
```
|
||||
|
||||
固定命名:
|
||||
|
||||
| 项 | 值 |
|
||||
|----|----|
|
||||
| Client Name | `Go-client` |
|
||||
| Drive Name | `AIOps` |
|
||||
| UNC | `\\Go-client\AIOps` |
|
||||
| Upload | `\\Go-client\AIOps\Upload` |
|
||||
| Download | `\\Go-client\AIOps\Download` |
|
||||
|
||||
### 6.2 RDPDR 最小能力
|
||||
|
||||
必须处理:
|
||||
|
||||
1. Server Announce Request。
|
||||
2. Client Announce Reply。
|
||||
3. Client Name Request。
|
||||
4. Server Client ID Confirm。
|
||||
5. Client Device List Announce。
|
||||
6. Device Create/Open。
|
||||
7. Directory Query。
|
||||
8. File Read。
|
||||
9. File Write。
|
||||
10. Close。
|
||||
|
||||
虚拟文件系统模型:
|
||||
|
||||
```text
|
||||
/
|
||||
├── Upload/ # 后端提供给远端读
|
||||
│ └── user uploaded files
|
||||
└── Download/ # 远端写入,后端捕获
|
||||
└── remote copied files
|
||||
```
|
||||
|
||||
### 6.3 上传链路
|
||||
|
||||
```text
|
||||
Browser file input/drop
|
||||
→ FileReader base64
|
||||
→ WebSocket { type: "rdp_file_upload", name, text }
|
||||
→ policy check file_upload_enabled
|
||||
→ rdpdr.AddUpload(name, bytes)
|
||||
→ Explorer opens \\Go-client\AIOps\Upload
|
||||
→ Windows reads file from virtual disk
|
||||
```
|
||||
|
||||
验收:
|
||||
|
||||
- 上传 `php.ini` 后,远端 `Upload` 目录出现文件。
|
||||
- 远端可以打开文件。
|
||||
- 远端可以复制到桌面或其他目录。
|
||||
|
||||
### 6.4 下载链路
|
||||
|
||||
```text
|
||||
Windows copies file to \\Go-client\AIOps\Download
|
||||
→ rdpdr Device Write
|
||||
→ backend captures bytes
|
||||
→ filestage.Put(name, data, ttl=30min)
|
||||
→ WebSocket { type: "rdp_file_download", name, url }
|
||||
→ Browser opens url
|
||||
→ /api/v1/rdp-staged/:token returns attachment
|
||||
```
|
||||
|
||||
验收:
|
||||
|
||||
- 远端将 `a.txt` 拖入 `Download`。
|
||||
- 浏览器自动触发下载。
|
||||
- 本地下载文件内容与远端一致。
|
||||
|
||||
实现注意:
|
||||
|
||||
- 不要用 SMB 管理共享作为主路径。
|
||||
- 文件名必须清理路径分隔符,避免 `../`。
|
||||
- `filestage` token 必须有 TTL,下载后可删除或等待过期。
|
||||
- 大文件后续应改为分片和磁盘暂存,避免长期占用内存。
|
||||
|
||||
---
|
||||
|
||||
## 7. 审计、录像与安全策略
|
||||
|
||||
### 7.1 数据模型
|
||||
|
||||
RDP 会话表:
|
||||
|
||||
```text
|
||||
sys_rdp_sessions
|
||||
id
|
||||
user_id / username
|
||||
server_id / server_name / server_ip / server_port
|
||||
client_ip
|
||||
width / height
|
||||
status: active | closed | failed
|
||||
duration
|
||||
error
|
||||
record_data
|
||||
record_size
|
||||
start_time / end_time
|
||||
```
|
||||
|
||||
操作审计表:
|
||||
|
||||
```text
|
||||
sys_session_commands
|
||||
session_id
|
||||
session_type = rdp
|
||||
user_id / username
|
||||
server_id / server_name
|
||||
command
|
||||
output_size
|
||||
timestamp
|
||||
```
|
||||
|
||||
策略表:
|
||||
|
||||
```text
|
||||
sys_audit_settings
|
||||
session_watermark_enabled
|
||||
session_record_enabled
|
||||
clipboard_enabled
|
||||
file_upload_enabled
|
||||
file_download_enabled
|
||||
command_policy_enabled
|
||||
approval_required
|
||||
```
|
||||
|
||||
### 7.2 录像策略
|
||||
|
||||
- 实时帧可以是 RGBA 脏矩形。
|
||||
- 录像帧必须是合成快照,避免回放只看到小块。
|
||||
- 推荐最小间隔 500ms。
|
||||
- 过滤启动阶段纯黑全屏。
|
||||
- 关闭 `session_record_enabled` 后仍记录会话元数据,但不保存 `record_data`。
|
||||
|
||||
### 7.3 审计策略
|
||||
|
||||
建议记录:
|
||||
|
||||
```text
|
||||
[RDP输入] hello
|
||||
[RDP复制] Ctrl+C
|
||||
[RDP剪切] Ctrl+X
|
||||
[RDP粘贴] http://localhost:8080/servers
|
||||
[RDP上传到虚拟盘] php.ini
|
||||
[RDP下载] a.txt
|
||||
```
|
||||
|
||||
不建议记录:
|
||||
|
||||
- 每个 `mousemove`。
|
||||
- 每个 RGBA 实时帧。
|
||||
- 空白按键和无业务意义坐标。
|
||||
|
||||
### 7.4 策略拦截
|
||||
|
||||
前端禁用按钮只是体验优化,后端必须再次拦截:
|
||||
|
||||
| Event | 策略 |
|
||||
|-------|------|
|
||||
| `clipboard` | `clipboard_enabled` |
|
||||
| `remote_copy` | `clipboard_enabled` |
|
||||
| `rdp_file_upload` | `file_upload_enabled` |
|
||||
| `rdp_file_download` | `file_download_enabled` |
|
||||
| 录像写入 | `session_record_enabled` |
|
||||
|
||||
---
|
||||
|
||||
## 8. 前端实现要求
|
||||
|
||||
### 8.1 Canvas 绘制
|
||||
|
||||
```text
|
||||
WebSocket binary
|
||||
→ parse RDPR header
|
||||
→ create ImageData(width, height)
|
||||
→ imageData.data.set(rgba)
|
||||
→ canvas.putImageData(imageData, x, y)
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- Canvas 内部分辨率等于远端桌面分辨率。
|
||||
- CSS 尺寸可以缩放,但鼠标坐标必须换算回远端坐标。
|
||||
- 最大化和窗口缩放不应重新连接 RDP。
|
||||
|
||||
### 8.2 输入捕获
|
||||
|
||||
- RDP Canvas 必须可 focus,点击后接收键盘事件。
|
||||
- 捕获 `keydown` 和 `keyup`,都发给后端。
|
||||
- 浏览器默认快捷键如 Ctrl+C/Ctrl+V 需要按场景阻止默认行为。
|
||||
- macOS Command 复制粘贴要映射到远端 Ctrl。
|
||||
|
||||
### 8.3 工具面板
|
||||
|
||||
工具面板只保留高价值动作:
|
||||
|
||||
| 动作 | 说明 |
|
||||
|------|------|
|
||||
| 粘贴到远端 | 从浏览器剪贴板读取并写入远端 |
|
||||
| 复制远端内容 | 请求远端剪贴板并写入浏览器剪贴板 |
|
||||
| 上传文件 | 发送到虚拟盘 Upload |
|
||||
| 虚拟盘说明 | 展示 `Go-client 上的 AIOps`、Upload、Download 的用途 |
|
||||
|
||||
不要再展示 SMB、445、管理共享等文案。
|
||||
|
||||
---
|
||||
|
||||
## 9. 调试和验收
|
||||
|
||||
### 9.1 日志关键字
|
||||
|
||||
```bash
|
||||
tail -f logs/aiops-local.log | rg "RDP|cliprdr|rdpdr|RDPR"
|
||||
```
|
||||
|
||||
| 日志 | 含义 |
|
||||
|------|------|
|
||||
| `[RDP] ws connected` | 浏览器已连接 |
|
||||
| `[RDP] engine start` | 开始连接远端 3389 |
|
||||
| `[RDP] bitmap session=` | 收到图形帧 |
|
||||
| `[cliprdr]` | 剪贴板通道 |
|
||||
| `[rdpdr] device list announced` | 虚拟盘公告 |
|
||||
| `[rdpdr] upload staged` | 上传文件已暂存 |
|
||||
| `[rdpdr] download captured` | 下载文件已捕获 |
|
||||
|
||||
### 9.2 自动测试建议
|
||||
|
||||
| 层级 | 用例 |
|
||||
|------|------|
|
||||
| `credssp` | NTLMv2、公钥绑定、证书变体 |
|
||||
| `rle` | 8/15/16/24/32 bpp 位图解码 |
|
||||
| `display` | framebuffer 合成、脏矩形裁剪 |
|
||||
| `rdpdr` | 目录枚举、文件读写、非法路径 |
|
||||
| `filestage` | token TTL、并发下载、文件名清理 |
|
||||
| 前端 | RDPR 解析、坐标缩放、快捷键映射 |
|
||||
|
||||
### 9.3 手动验收矩阵
|
||||
|
||||
| 场景 | 预期 |
|
||||
|------|------|
|
||||
| 13 寸屏打开 RDP | 能看到边框、工具栏、Canvas |
|
||||
| 最大化窗口 | 不重连、不闪屏 |
|
||||
| 登录初期黑屏 | 回放中不出现大量黑屏段 |
|
||||
| 本地复制远端粘贴 | 成功 |
|
||||
| 远端复制本地粘贴 | 成功 |
|
||||
| 上传小文件 | `Upload` 可见、可打开、可复制 |
|
||||
| 远端下载文件 | 浏览器自动下载 |
|
||||
| 关闭剪贴板策略 | 前端禁用,后端拒绝 |
|
||||
| 关闭上传策略 | 前端禁用,后端拒绝 |
|
||||
| 实时审计 | 审计端可看到当前画面 |
|
||||
| 历史回放 | 画面完整,不是大量小块 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 常见问题
|
||||
|
||||
| 问题 | 优先排查 |
|
||||
|------|----------|
|
||||
| 只能看到 C 盘,没有 AIOps 虚拟盘 | `rdpdr` 是否完成 Device List Announce,Windows 是否刷新资源管理器 |
|
||||
| `\\tsclient\AIOps` 无权限 | 路径名/ClientName 不一致,或 rdpdr create/read/write 返回码错误 |
|
||||
| 远端 Ctrl+C 不能复制出来 | cliprdr FormatList/FormatDataRequest 是否到达,是否过早请求 |
|
||||
| 文件可打开但不能复制到桌面 | rdpdr Read/Close/QueryInfo 返回不完整,或文件属性/长度错误 |
|
||||
| 回放是小块或黑屏 | 没有 framebuffer 合成,或录像保存了实时脏矩形 |
|
||||
| 最大化闪屏 | 前端重建了 WebSocket,应只缩放 Canvas |
|
||||
| 上传大文件卡顿 | base64 + WebSocket 一次性发送,后续改分片 |
|
||||
Reference in new issue
Block a user