# AIOps Web RDP 远程桌面 > 作者:Logan.Li > 更新:2026-07-03 > 归档副本:`/opt/rdp/docs/rdp.md` · 变更记录:`/opt/rdp/CHANGELOG.md` --- ## 概述 AIOps 提供浏览器内 Windows 远程桌面(Canvas 渲染 + WebSocket)。当前已移除第三方 `grdp` 兜底实现,后端固定使用 AIOps 自研 RDP 协议栈。// Logan.Li | 环境变量 | 行为 | |----------|------| | `RDP_ENGINE` | 已不再参与引擎选择,保留环境变量不会改变行为 | | `RDP_DEBUG=1` | 打开 `[RDP:TRACE]` 协议级日志 | | 层级 | 状态 | 路径 | |------|------|------| | X.224 / TPKT / TLS | ✅ 自研灰度 | `backend/pkg/rdp/transport/` | | CredSSP / NTLMv2 (NLA) | ✅ 自研灰度 | `backend/pkg/rdp/credssp/` | | MCS / GCC / 许可证 / 激活 | ✅ 自研灰度 | `backend/pkg/rdp/mcs_probe.go`、`session_connect.go` | | FastPath 位图 / MPPC / RLE | ✅ 自研灰度 | `bitmap_decode.go`、`fastpath_recv.go`、`mppc.go`、`rle/` | | 键鼠 / 剪贴板 / auto_exec | ✅ 自研灰度 | `input_fastpath.go` | | 文件传输 (SMB + 暂存回退) | ✅ 自研 | `pkg/winsmb/`、`pkg/rdpfilestage/` | | 前端 Canvas + 工具面板 | ✅ 自研 | `RdpDialog.tsx`、`RdpToolsPanel.tsx` | 自研协议栈不依赖第三方 RDP 实现,也不直接依赖控制器、数据库或 WebSocket。已删除的第三方目录备份在 `/opt/rdp/third-party/grdp-aiops-backup-20260703`。// Logan.Li --- ## 2026-07-03 变更记录 ### 自研 RDP 引擎 | 变更项 | 说明 | |--------|------| | 引擎入口 | `NewEngine()` 固定返回 `SessionEngine` | | 兜底依赖 | 已移除 `github.com/nakagami/grdp` 和 `backend/third_party` | | Dockerfile | 不再复制 third_party 模块 | | 新增模块 | `session_engine.go`、`session_connect.go`、`packet_io.go`、`fastpath_recv.go`、`bitmap_decode.go`、`input_fastpath.go`、`mppc.go`、`rle/` | | 自研会话激活 | `ConfirmActive` 后可选读 FontMap,发送 `SuppressOutput(ALLOW_DISPLAY_UPDATES)` 触发服务端推帧 | | 自研位图输出 | FastPath / SlowPath Bitmap + SurfaceCmd → PNG base64 → WebSocket `type:bitmap` | | 自研输入通道 | Fast-Path Input:扫描码键鼠、Unicode 剪贴板、`auto_exec`(Win+R 粘贴命令) | | 协议日志 | `RDP_DEBUG=1` 输出 `[RDP:TRACE]`,定位连接、bitmap、input 阶段 | ### 仍保留的探测 API(诊断用) | 函数 | 用途 | |------|------| | `ProbeHandshake` | X.224 安全协议协商 | | `ProbeCredSSP` | NLA 凭据验证 | | `ProbeMCSConnect` | MCS/GCC 握手探测(读完即关连接) | | `ProbeSessionFrames` | 建立会话后统计位图矩形数量 | ### 已知限制 / 待完善 - `mcs_probe.go` 超 700 行红线,后续需按 MCS/GCC/PDU 拆文件 - 未实现 cliprdr 虚拟通道双向同步、RDPGFX/H.264、动态虚拟通道 - 部分 Windows 主机在频繁探测后可能 `connection reset`(目标机策略/限流,非 NLA 失败) --- ## 架构与调用链 ``` RdpDialog.tsx │ WebSocket /ws/rdp?id=&width=&height=&token= ▼ controller/rdp.go │ loadRDPCredential() → engine.Run() ▼ pkg/rdp/engine.go → SessionEngine ├── session_engine.go readLoop + handleEvent ├── session_connect.go OpenSession (CredSSP + MCS + SuppressOutput) ├── packet_io.go TPKT / FastPath 读写 ├── fastpath_recv.go 服务端更新解析 ├── bitmap_decode.go RLE/翻转 → PNG ├── input_fastpath.go 键鼠 / 剪贴板 / auto_exec ├── mppc.go MPPC 解压 └── rle/ 位图 RLE 解压算法 ``` --- ## 后端文件清单 ### 协议栈 `backend/pkg/rdp/` | 文件 | 说明 | |------|------| | `engine.go` | `Engine` 接口、`RDP_ENGINE` 引擎选择 | | `client.go` | `ProbeHandshake` / `ProbeTLSHandshake` / `ProbeCredSSP` | | `session_engine.go` | 图形会话主循环、帧推送、输入分发 | | `session_connect.go` | `OpenSession`、SuppressOutput 激活 | | `mcs_probe.go` | MCS Connect、GCC、ClientInfo、ConfirmActive 构建与探测 | | `packet_io.go` | `ReadWirePacket`、`WriteFastPathInput` | | `fastpath_recv.go` | FastPath 更新、MPPC、SurfaceCmd 解析 | | `bitmap_decode.go` | 位图解码与 PNG 编码 | | `input_fastpath.go` | Fast-Path Input 事件编码 | | `mppc.go` | MPPC-64K 解压器 | | `keymap.go` | 浏览器 `KeyboardEvent.code` → RDP 扫描码 | | `transport/` | TPKT、X.224、TLS | | `credssp/` | CredSSP + NTLMv2 + ASN.1 | | `rle/` | RLE 位图解压(`DecompressInto`) | ### 控制器 / 模型 | 文件 | 说明 | |------|------| | `controller/rdp.go` | WebSocket 会话、审计录制 | | `controller/rdp_session.go` | 会话列表 / 回放 / 终止 API | | `controller/rdp_file.go` | 文件 list/upload/download/stage | | `controller/rdp_cred.go` | 凭据加载辅助 | | `model/rdp_session.go` | `RDPSession` 表 | ### 文件传输 | 文件 | 说明 | |------|------| | `pkg/winsmb/` | SMB 管理共享直传 `C:\Users\Public\AIOpsShare\Download` | | `pkg/rdpfilestage/` | 服务端暂存 + 一次性 token | | `frontend/.../RdpToolsPanel.tsx` | 工具面板(通用命令 / 文件 Tab) | | `frontend/.../rdpConstants.ts` | 虚拟 Download 盘路径常量 | --- ## 前端 | 文件 | 说明 | |------|------| | `RdpDialog.tsx` | Canvas 渲染、WebSocket、键鼠捕获、工具栏 | | `RdpToolsPanel.tsx` | 剪贴板同步、文件上传下载、auto_exec 回退 | | `api/server/rdp-files.ts` | 文件传输 REST | | `api/audit/rdp.ts` | 会话审计 API | --- ## WebSocket 帧协议 ### 服务端 → 浏览器 ```json { "type": "session", "message": "123", "width": 1280, "height": 800 } { "type": "status", "message": "正在连接 RDP ..." } { "type": "error", "message": "..." } { "type": "bitmap", "x": 0, "y": 0, "width": 64, "height": 64, "image": "data:image/png;base64,..." } { "type": "clipboard","text": "远端剪贴板内容" } ``` ### 浏览器 → 服务端 ```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 } { "type": "clipboard", "text": "本地剪贴板" } { "type": "auto_exec", "text": "C:\\Users\\Public\\AIOpsShare\\Download\\setup.exe" } ``` --- ## CredSSP 多策略重试 每条策略使用**新建 TCP 连接**(`client.go`): 1. `v6 + binding`(默认) 2. `v6 + binding + UseCBT` 3. `v6 + binding + ForceMIC` 4. `v2 + raw`(兼容旧服务端) 关键修复:`credssp.subjectPublicKey()` 使用证书 `SubjectPublicKey` 原始 ASN.1,而非 PKIX SPKI。 --- ## 外部项目借鉴边界 | 项目 | 可借鉴点 | 不直接采用原因 | |------|----------|----------------| | FreeRDP | 协议完整性、通道、编解码、客户端能力矩阵 | C 生态和体量过大,不适合直接嵌入 | | Apache Guacamole | Web 网关、协议代理、录制与前端桥接分层 | 依赖 guacd/C,不符合单 Go 后端目标 | | IronRDP | 现代模块化、状态机测试、安全边界 | Rust 生态,适合作为测试和分层参考 | | Teleport | 身份、RBAC、审计、Desktop Gateway 架构 | 产品目标偏堡垒机,不是轻量 RDP client | | tomatome/grdp / nakagami/grdp | Go 协议状态机、能力集、bitmap/input 参考 | 第三方实现已移除,仅保留 `/opt` 备份用于协议对照 | ## 第三方代码归档与文档 归档与变更记录目录: ``` /opt/rdp/ ├── CHANGELOG.md # 变更记录 ├── README.md # 索引 ├── docs/rdp.md # 完整说明(与本文档同步) └── third-party/ ├── nakagami-grdp-v0.8.7/ ├── rdp-web-refs/ └── grdpprobe/ ``` > 第三方源码只能作为协议行为和工程分层参考;涉及 GPL 的项目不得直接搬运实现。// Logan.Li --- ## 验证命令 ```bash # 编译 cd backend && CGO_ENABLED=1 go build -o aiops . # NLA 探测 go run /tmp/rdp_cred_probe.go # ProbeCredSSP # 会话位图统计(需可达的 Windows:3389) go run /tmp/rdp_session_probe_main.go # Docker 端到端 ./run.sh docker up # 浏览器:服务器管理 → Windows 主机 → RDP 连接 ``` --- ## 相关路由 | 路径 | 说明 | |------|------| | `GET /ws/rdp` | WebSocket 图形会话 | | `GET /api/v1/rdp-sessions` | 会话审计列表 | | `GET /api/v1/servers/:id/rdp-files` | 远端文件列表 | | `POST /api/v1/servers/:id/rdp-files/upload` | 上传 | | `GET /api/v1/servers/:id/rdp-files/download` | 下载 |