网络安全运维认证鉴权【免费下载链接】warpgateFully transparent SSH, HTTPS, Kubernetes, database and RDP/VNC bastion/PAM that doesnt need additional client-side software项目地址https://gitcode.com/gh_mirrors/wa/warpgate点击查看免费下载导读本文基于 Warpgate 仓库内的 vendor/ironrdp-session/PATCHES.md完整剖析该项目对上游ironrdp-session0.11.0RDP 会话状态机 crate的 fork 补丁清单。Warpgate 是一个不依赖客户端软件的 SSH、HTTPS、Kubernetes、数据库与 RDP/VNC 堡垒机bastion/PAM其 RDP 协议的 Web 化代理warpgate-protocol-rdp直接建立在这套被改造过的会话处理器之上。读完本文你将理解为什么上游解码器在真实 Windows/xrdp 服务器面前会出错、Warpgate 用哪些补丁解决了批量 PDU、位图填充、EGFX 图形管线渲染等问题以及如何在重新 vendor 上游版本时安全地取舍这些补丁。背景Warpgate 为什么维护一个 ironrdp-session 的 forkironrdp-session是 Devolutions IronRDP 生态中负责“驱动一个 RDP 会话”的状态机 crate负责 X224 层帧解析、慢路径slow-path与快路径fast-pathPDU 分发、位图解码与 framebuffer 合成、指针渲染、DVC动态虚拟通道管理等。Warpgate 通过根目录 Cargo.toml 以path依赖的形式引用了vendor/下的整套 IronRDP 组件ironrdp-session { path vendor/ironrdp-session }上游 0.11.0 的会话处理器在真实世界服务器尤其是 Windows RDP 服务器与 Linux 下的 xrdp面前暴露了若干缺陷而 Warpgate 的 Web 桌面代理需要把这些协议流转换为浏览器可渲染的帧序列并对目标侧登录状态、录制内容做精细控制。于是 Warpgate 维护了一份源码级补丁 warpgate.patch并配套编写了这份 PATCHES.md 作为重新 vendor 时的“补丁台账”。补丁分为四类行为修复批量 Share Control PDU 拆分、位图行填充裁剪、fast-path 事件批拆分、MCS 断连处理能力扩展Save Session Info PDU 透传、EGFX 图形管线输出渲染上游回移从上游 0.11.0 之后的版本 backport 的两个修复构建治理Cargo.toml 的 lint 降噪配置。修复一批量 Share Control PDU 拆分与重激活 share ID问题一个 MCSSendDataIndication里塞了多个 PDURDP 的慢路径数据经由 MCS 层的SendDataIndication承载。部分 Windows RDP 服务器会把多个 Share Control PDU 拼接在同一个 MCSSendDataIndication负载中。上游会话处理器把整个 MCS 负载直接交给单个 Share Control 解码器导致第一个 PDU 的totalLength字段与解码出的实际大小不一致解码随即失败。Warpgate 在 vendor/ironrdp-session/src/x224/mod.rs 中新增了split_share_control_pdu辅助函数按每个 PDU 校验过的totalLength边界切分负载再按线上顺序逐个处理。从补丁源码可以看到拆分的判定逻辑位于warpgate.patch的 x224 段fn split_share_control_pdu(data: [u8]) - SessionResultOption([u8], [u8]) { const HEADER_PREFIX_SIZE: usize 4; const SHARE_CONTROL_HEADER_SIZE: usize 6; const SHARE_CONTROL_TYPE_MASK: u16 0x000f; const SHARE_CONTROL_VERSION: u16 0x0010; // 读取 4 字节头length_lo/length_hi 是 totalLengthtype_lo/type_hi 是 PDU 类型与版本 // 版本必须是 0x0010且 PDU 类型落在 {1, 3, 6, 7, 10} 才认定为 Share Control 头 // 不满足时原样返回留给 Basic Security Header / 多路传输请求等其他 IO 通道格式 let length usize::from(u16::from_le_bytes([length_lo, length_hi])); // length 6 报错data 长度不足报错成功则返回 (pdu, remaining) Ok(Some((pdu, remaining))) }process_io_channel随即变成循环先切出第一个 PDU 处理若剩余负载非空且仍以 Share Control 头开头就继续切分、继续处理直到负载耗尽若剩余负载不再以 Share Control 头开头则报错non-Share Control data follows a Share Control PDU。值得注意的细节拆包只对 Share Control 头生效。一个不以Share Control 头开头的负载例如携带多路传输请求的 Basic Security Header会被完整保留交由原来的单一解码路径处理避免误拆。问题二Deactivation-Reactivation 分配新 share ID 时快路径失联在 Deactivation-Reactivation激活-重激活流程中服务器可能重新分配 share ID。上游的ActiveStage::set_share_id只更新了慢路径响应所用的 share ID补丁把更新同时写入快路径的帧确认处理器frame-acknowledgement processor。从 vendor/ironrdp-session/src/active_stage.rs 与 vendor/ironrdp-session/src/fast_path.rs 可以看到fast_path::Processor新增了set_share_id把新值写入内部marker_processor.share_idpub fn set_share_id(mut self, share_id: u32) { self.marker_processor.share_id share_id; }而ActiveStage::set_share_id变成同时调用两者的组合pub fn set_share_id(mut self, share_id: u32) { self.x224_processor.set_share_id(share_id); self.fast_path_processor.set_share_id(share_id); }Warpgate 侧如何驱动重激活重激活序列本身由 Warpgate 消费方驱动当ActiveStage发出ActiveStageOutput::DeactivateAll时warpgate-protocol-rdp使用初次连接返回的ConnectionResult::activation_factory来推进重激活流程。在 warpgate-protocol-rdp/src/client/mod.rs 的会话循环中let should_reactivate outputs .iter() .any(|output| matches!(output, ActiveStageOutput::DeactivateAll)); // ...处理其他输出... if should_reactivate let Some(size) reactivate(mut framed, mut active_stage, activation_factory, image).await? { // 重激活若改变了桌面尺寸重新播种 viewer 的帧缓冲并请求全量刷新 let keyframe encode_resized_keyframe(image, size); *image DecodedImage::new(PixelFormat::RgbA32, size.width, size.height); ... send_refresh_request(mut framed, mut active_stage, size).await?; }reactivate函数同文件 client/mod.rs通过ironrdp_tokio::single_sequence_step驱动连接激活状态机直到ConnectionActivationState::Finalized然后取出新的share_id并调用active_stage.set_share_id(share_id)——这正是上面补丁的消费入口。这也解释了为什么“驱动重激活”这部分逻辑写在warpgate-protocol-rdp而非补丁里。PATCHES.md 还特别提示上游已在 #1861 中合并了 PDU 拆分修复但重激活 share ID 的 hunk 仍是我们的。重新 vendor 时只保留上游尚未覆盖的部分即可。修复二Save Session Info PDU 透传让 Warpgate 感知目标登录状态问题目标是否还在登录屏消费者无从得知Server Save Session Info PDU[MS-RDPBCGR] 2.2.10.1是服务器在会话登录完成后发送给客户端的通知可附带登录账户名。上游处理器只对它做debug!日志后丢弃Ok(Vec::new())因此消费方无法判断会话是否仍停留在目标自己的登录屏幕前——而这正是用户键入凭据、Warpgate 无法感知也无法录制的窗口期。补丁内容新增输出变体补丁在 vendor/ironrdp-session/src/x224/mod.rs 的ProcessorOutput与 vendor/ironrdp-session/src/active_stage.rs 的ActiveStageOutput中各新增了SaveSessionInfo(SaveSessionInfoPdu)变体把原始 PDU 上抛给消费者/// The server reported that the session logged on ([MS-RDPBCGR] 2.2.10.1). /// Before this arrives the session is at the targets own sign-in screen; the PDU /// optionally names the account that was logged on. SaveSessionInfo(SaveSessionInfoPdu),Warpgate 的消费方式审计目标登录 保护录制内容Warpgate 在 warpgate-protocol-rdp/src/client/logon.rs 中实现了一个LogonWatcher当收到ActiveStageOutput::SaveSessionInfo时解析InfoData::LogonInfoV1/V2中的账户信息domain\user拼接发出AuditEvent::TargetLogon审计事件并调用sign_in.mark_logged_on()翻转登录状态。其中对InfoData::LogonExtended的处理尤其谨慎该变体携带自动重连包auto-reconnect packet内含客户端用于免密重连的 16 字节随机数verifier。补丁消费方特意不打印整个结构体只记录标志位与是否存在 auto-reconnect避免把敏感随机数写进日志——这是从源码注释中可见的一条安全实践。登录状态本身定义在 warpgate-core/src/protocols/desktop.rsLogonState是一个基于ArcAtomicBool的“简单 Sync 翻转”提供at_logon_screen()/logged_on()两个初始值、mark_logged_on()翻转与still_at_logon_screen()查询。这个状态最终被桌面录制器使用在 warpgate-core/src/recordings/desktop.rs 中DesktopRecorder通过track_logon_state记录登录状态should_record_keyboard只在已登录时才允许录制键盘输入fn should_record_keyboard(self) - bool { self.record_keyboard !self .sign_in .get() .is_some_and(LogonState::still_at_logon_screen) }也就是说用户停留在目标 Windows 登录屏时键入的密码等键盘输入不会被写入录制文件——这正是 PATCHES.md 所述“keep credentials typed at that sign-in screen out of session recordings”的落地实现。修复三位图行填充row padding导致图像错位的裁剪修复问题服务器在TS_BITMAP_DATA里塞填充字节RDP 服务器可能在位图数据TS_BITMAP_DATA中填充超出目标矩形的内容宽度按 4 像素取整xrdp 的行为和/或每行按 4 字节对齐取整。而DecodedImage的apply_*系列函数按矩形宽度对源数据重新切行一旦存在填充后续每一行都会被填充字节“顶偏”整幅图像发生剪切错位shear。补丁内容解码前按矩形重新打包补丁在 vendor/ironrdp-session/src/fast_path.rs 中新增repack_bitmap_to_rectangle其语义为把源数据按src_row_bytes步长逐行取前dst_row_bytes字节重排成紧密行返回None表示无需处理源步长不大于目标步长或缓冲不足无法重排fn repack_bitmap_to_rectangle( data: [u8], src_row_bytes: usize, dst_row_bytes: usize, rows: usize, ) - OptionVecu8 { if src_row_bytes dst_row_bytes { return None; } let mut out Vec::with_capacity(dst_row_bytes.checked_mul(rows)?); for r in 0..rows { let start r.checked_mul(src_row_bytes)?; out.extend_from_slice(data.get(start..start.checked_add(dst_row_bytes)?)?); } Some(out) }随后在process_bitmap_update里针对压缩与非压缩两条路径分别裁剪压缩流RDP6 与 RLE 位图解码输出按update.width紧密步长排列用crop_tight步长stride_px * bpp取前rect_width * bpp裁剪覆盖 RGB24、RGB16、RGB15、RGB8调色板等全部像素格式非压缩流每行额外按 4 字节对齐用crop_padded源步长((stride_px * bpp) 3) !3裁剪覆盖 8/15/16/24/32 bpp。裁剪逻辑通过c.as_deref().unwrap_or(buf)优雅退化不需要裁剪时直接复用原缓冲区避免无谓拷贝。PATCHES.md 提示上游 PR #1436 携带了同样的修复但尚未合并#1452 作为其重复项被关闭因此一旦某次上游 release 包含二者之一这个 fork 补丁即可丢弃。修复四backport 的两个上游修复重新 vendor 时可安全移除PATCHES.md 记录了从上游 0.11.0 之后的版本回移的两个修复二者在重新 vendor 时会自动“掉落”即无需再保留MCS Disconnect Provider Ultimatum 正确处理上游 #1692xrdp 在会话结束时会在 I/O 流上发送一个纯 MCSDisconnectProviderUltimatum。上游把它当作解码错误补丁在 vendor/ironrdp-session/src/x224/mod.rs 中先尝试按SendDataIndication解码失败后回退为尝试解码X224McsMessage命中DisconnectProviderUltimatum时输出ProcessorOutput::Disconnect(DisconnectDescription::McsDisconnect(reason))把“会话结束”以断连形式呈现而不是报解码错误。fast-path 输入按 255 事件拆分上游 #1630快路径输入 PDU 的numEvents字段是单字节最多携带 255 个事件。上游若收到超过 255 个事件的批量输入会直接失败补丁在 vendor/ironrdp-session/src/active_stage.rs 中定义了const MAX_EVENTS_PER_FRAME: usize 255用events.chunks(MAX_EVENTS_PER_FRAME)把批量输入切成多个帧分别编码为ResponseFrame输出预分配容量也相应改为events.len().div_ceil(MAX_EVENTS_PER_FRAME) 1避免整批失败。修复五Graphics PipelineEGFX输出渲染补丁内容把 EGFX 合成器输出“翻译”成通用 GraphicsUpdate这是补丁中最大的一块backport 上游 #1461渲染 EGFX 图形管线输出与 #1874会话侧ResetGraphics处理让所有基于ActiveStage的消费者无需新代码即可渲染 EGFX 内容。核心机制在 vendor/ironrdp-session/src/active_stage.rs 的process中每帧 X224 后排空合成器ActiveStage::process在每次 X224 帧处理之后通过新增的get_dvc_mut::GraphicsPipelineClient()拿到已注册的客户端合成器调用take_output_reset()与drain_output()取出复位信息与增量区域经composite_graphics_updates合成进DecodedImage最终以一个ActiveStageOutput::GraphicsUpdate(region)输出。补丁注释特别解释了为什么放在Action::X224分支而不是快路径分支EGFX 数据只经 DVC由 X224 承载到达没必要在会话最高频的快路径帧上做空转检查当图形 DVC 未注册时该逻辑整体是 no-op。ResetGraphics 处理收到ResetGraphics时用新增的DecodedImage::reset_preserving_pointer(width, height)见 vendor/ironrdp-session/src/image.rs调整图像尺寸并清空 framebuffer同时保留指针 sprite重算指针几何并重新绘制指针层。复位后graphics_output_needs_full_refresh标志置位下一次GraphicsUpdate会被扩大到覆盖整个新表面——这正是消费者得知新尺寸的途径。补丁注释明确上游同样如此处理没有新增输出变体。region 合并的工程取舍composite_graphics_updates把所有合成器 delta 的包围区域合并成一个InclusiveRectangle再上抛而不是逐个 PDU 输出。注释解释了原因消费者有权重绘任何GraphicsUpdate命中的区域而ironrdp-client对每个 update 都重建整个 framebuffer若一帧有 N 个矩形逐条输出会导致整屏拷贝 N 次合并后最坏情况是整屏一次拷贝。同时函数把 EGFX 的排他矩形exclusive right/bottom转换为会话的含尾矩形one-past-inclusive并在apply_rgba32之前先做边界检查fits跳过任何超出图像边界的 delta 并给出warn!日志——因为apply_rgba32拒绝时会返回InclusiveRectangle::empty()即(0,0,0,0)无法与坐标原点处的真实 1×1 更新区分。apply_rgba32去特性门控补丁把image.rs中的apply_rgba32_iter/apply_rgba32上的#[cfg(feature qoi)]移除使其在默认特性下也可用同时ActiveStage与x224::Processor新增get_dvc_mut后者转发到DrdynvcClient::get_dvc_mut见 vendor/ironrdp-dvc/src/client.rs。该补丁依赖vendor/ironrdp-dvc提供get_dvc_mut与vendor/ironrdp-egfx二者都已列入根 Cargo.toml 的 vendor 引用清单。Warpgate 侧的消费方式在 warpgate-protocol-rdp/src/client/mod.rs 中会话输出处理对ActiveStageOutput::GraphicsUpdate(region)调用encode_region编码为DesktopEvent交给 viewer同时report_size函数注释指出EGFX 的ResetGraphics会就地调整image尺寸下一次 update 覆盖整个新表面因此 viewer 只需要先收到新的尺寸即可——初始连接、deactivation-reactivation、EGFX ResetGraphics 三条尺寸变更路径都汇入同一个report_size通知出口。会话初始化时通过drdynvc.with_dynamic_channel(GraphicsPipelineClient::new(...))注册 EGFX 客户端同文件 client/mod.rs。构建治理Cargo.toml 的 lint 配置与重新 vendor 的注意事项除源码改动外vendor/ironrdp-session/Cargo.toml 还额外设置了[lints.rust] # Vendored code: silence upstream lints so they do not surface in our builds. warnings { level allow, priority 1 }这样 vendor 路径依赖自身的 lint 警告不会涌入 Warpgate 的构建输出。PATCHES.md 特别提醒这一段不在warpgate.patch里补丁只含源码 diff重新 vendor 时需要用编辑器手工重新应用。如何安全地重新 vendor补丁台账速查综合 PATCHES.md 的指引重新 vendor 上游ironrdp-session时的取舍规则可以归纳如下补丁状态重新 vendor 时批量 Share Control PDU 拆分上游 #1861 已合并可丢弃重激活 share ID 更新快路径仍为本 fork 独有必须保留Save Session Info PDU 透传仍为本 fork 独有必须保留位图行填充裁剪上游 #1436 未合并#1452 为重复项上游 release 包含其一即可丢弃MCS Disconnect Ultimatum#1692上游 0.11.0 之后已合并可丢弃fast-path 255 事件拆分#1630上游 0.11.0 之后已合并可丢弃EGFX 输出渲染#1461 #1874上游 0.11.0 之后已合并上游 release 包含二者即可丢弃[lints.rust] warnings allow不在warpgate.patch内手工重新应用从实现侧验证Warpgate 的 E2E 测试使用 tests/images/rdp-server/Dockerfile 构建的 xrdp xorgxrdp 容器镜像Debian bookworm、无桌面环境的 Xorg root window作为真实 RDP 后端配合 test_rdp_web.py 等测试完成完整的 NLA/CredSSP 握手与帧中继断言。xrdp 正是 PATCHES.md 中提到的“位图宽度按 4 像素填充”“会话结束发送 Disconnect Provider Ultimatum”等行为的典型来源因此这份补丁清单与其说是一组理论修正不如说是 Warpgate 在真实异构 RDP 服务器兼容性上的实战沉淀。小结Warpgate 对ironrdp-session的 fork 展示了“上游协议栈 自维护补丁”这一成熟工程模式批量 Share Control PDU 拆分解决了解码边界问题Save Session Info 透传支撑了审计与录制脱敏位图裁剪修复了 xrdp 等服务器导致的图像错位EGFX 输出渲染让图形管线内容对消费者透明可见而明确的补丁台账含上游合并状态追踪让未来的 re-vendor 工作有据可依。对于任何深度使用第三方 RDP 协议栈的团队这份 PATCHES.md 连同 warpgate.patch 本身就是一份值得借鉴的“vendor 补丁治理”范本。赞分享网络安全运维认证鉴权【免费下载链接】warpgateFully transparent SSH, HTTPS, Kubernetes, database and RDP/VNC bastion/PAM that doesnt need additional client-side software项目地址https://gitcode.com/gh_mirrors/wa/warpgate点击查看免费下载相关推荐导出QQ空间全部历史说说GetQzonehistory 能帮你留住什么导出QQ空间全部历史说说GetQzonehistory 能帮你留住什么 想把QQ空间的历史说说完整导出来GetQzonehistory 是一个开源 Pyth网页爬虫数据分析Warpgate 的 ironrdp-server 0.13.0 vendor 补丁剖析RemoteFX 首帧头块丢失与黑屏问题修复Warpgate 的 ironrdp server 0.13.0 vendor 补丁剖析RemoteFX 首帧头块丢失与黑屏问题修复 导读 本篇文章围绕 Wa网络安全运维认证鉴权xberg-tesseract 的 Tesseract WASM 补丁解析把 OCR 引擎编译进 WebAssembly 的四类关键改造xberg tesseract 的 Tesseract WASM 补丁解析把 OCR 引擎编译进 WebAssembly 的四类关键改造 本文聚焦 xberg云原生虚拟化存储后端高可用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考