Files
aiops/docs/rdp/README.md
T
Logan.Li e0d652d211 feat: 完善堡垒机与 Agent 智能运维能力
- 支持 Windows Agent 构建、安装脚本和统一上报协议

- 增强 RDP 剪贴板、虚拟盘和文件传输能力

- 新增 AIOps 上下文工具、告警 RCA trace 落库和回放接口

- 重构 docs 模块文档并补充终端、堡垒机、监控、AI Agent 设计
2026-07-05 23:49:36 +08:00

17 KiB
Raw Blame History

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 产品、架构、联调人员 功能范围、调用链、文件清单、现有限制
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 中应看到:

此电脑
└── Go-client 上的 AIOps
    ├── Upload
    └── Download
目录 用途
Upload 浏览器上传的文件会出现在这里,远端可复制到桌面或目标目录
Download 远端文件复制/拖入这里后,AIOps 捕获并触发本地浏览器下载

架构与调用链

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 静态虚拟通道

文件下载链路:

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 自动下载

文件上传链路:

浏览器选择/拖拽文件
  ▼
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:

{ "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:

magic:  "RDPR"
x:      uint32 little-endian
y:      uint32 little-endian
width:  uint32 little-endian
height: uint32 little-endian
rgba:   width * height * 4

历史回放仍使用 JSON bitmap 快照:

{ "type": "bitmap", "x": 0, "y": 0, "width": 900, "height": 596, "image": "data:image/png;base64,..." }

浏览器到服务端

{ "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 自身的设备重定向能力。

会话安全策略

入口:

系统管理 → 系统设置 → 安全 → 会话设置

策略字段存储在 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 已不再参与引擎选择,保留也不会改变行为

常用日志过滤:

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

验证清单

构建

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