Skip to content

feat(pam-rdp): record RDP sessions#217

Closed
bernie-g wants to merge 3 commits intomainfrom
feat/pam-rdp-recording
Closed

feat(pam-rdp): record RDP sessions#217
bernie-g wants to merge 3 commits intomainfrom
feat/pam-rdp-recording

Conversation

@bernie-g
Copy link
Copy Markdown
Contributor

@bernie-g bernie-g commented May 6, 2026

Description 📣

Implements end-to-end RDP session recording: a Rust IronRDP-based MITM bridge taps each PDU on the gateway, the Go side streams events into the chunked-recording uploader, and byte-level capability filters force the server into a codec set the WASM replay decoder can decompress. Event timestamps are anchored to the PAM session start so reconnects within a single session play back as one continuous timeline.

Type ✨

  • Bug fix
  • New feature
  • Improvement
  • Breaking change
  • Documentation

Tests 🛠️

```sh
cd packages/pam/handlers/rdp/native && cargo test --lib
go vet -tags rdp ./packages/pam/...
```

Manually verified: Windows Server 2022 RDP playback (single connection, reconnect within session, multi-reconnect), playback total matches last frame, non-RDP session types (SSH/DB/K8s) unaffected.


bernie-g added 2 commits May 5, 2026 10:04
Tap each PDU in the post-CredSSP byte bridge and stream structured events
(target_frame / keyboard / unicode / mouse) through the existing session
logger so they land in the encrypted chunk pipeline.

Capture switches the post-CredSSP path from copy_bidirectional to a
PDU-framed bridge: read_pdu yields TPKT/FastPath frames pure-framing, no
RDP state machine, the bytes are forwarded unchanged, and the tap emits
SessionEvent variants on an mpsc channel. This preserves the
no-MCS/capability/share-state-drift property of the byte-pump it
replaces.

The FFI gains rdp_bridge_poll_event for Go to drain those events with a
timeout. TargetFrame payloads are handed across as libc::malloc'd
buffers; the Go side defers C.free after copying.

Go-side, RDPProxy.HandleConnection spawns a drain goroutine that JSON-
encodes each event and calls SessionLogger.LogTerminalEvent with
ChannelType=rdp. The chunk uploader is protocol-agnostic, so RDP
sessions now flow into pam_session_event_chunks like SSH/HTTP do.

session.LogTerminalEvent skips masking for the rdp channel because the
data field carries a base64-JSON envelope; SSH-shaped masking regexes
would corrupt valid recordings.
Three fixes that together make RDP recording playback render correctly:

- Filter Order, BitmapCodecs, and INFO_COMPRESSION on the wire so the
  server only emits Bitmap update PDUs IronRDP-session can decompress.
  Implemented as byte surgery on Confirm Active and Client Info PDUs;
  IronRDP's typed decode->encode loses unrelated fields. New cap_filter
  module + walk_caps + 14 unit tests pin the byte-preservation contract.
- Override ev.ElapsedNs with time.Since(SessionStartedAt) in the Go drain
  so reconnects within the same PAM session don't restart the bridge's
  local clock from zero. SessionUploader exposes GetSessionStartedAt
  (reconstructed from the persisted lastEndElapsedMs).
- Stamp chunk endElapsedMs from the last entry's elapsedTime instead of
  time.Since(state.startedAt) at flush moment, so the playback total
  doesn't reach past the last actual frame. readFromOffset returns the
  trailing entry's elapsed time; falls back to wallclock for non-terminal
  sessions whose entries lack the field.

Comment cleanup pass across the touched RDP files.
@infisical-review-police
Copy link
Copy Markdown

💬 Discussion in Slack: #pr-review-cli-217-fix-pam-rdp-patch-capabilities-anchor-timestamps-for-replay

Posted by Review Police — reviews, comments, new commits, and CI failures will stream into this channel.

@bernie-g bernie-g changed the title fix(pam-rdp): patch capabilities + anchor timestamps for replay feat(pam-rdp): record RDP sessions May 6, 2026
@bernie-g bernie-g closed this May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant