feat(tunnel): RDP bridge tunnel mode (VA-20) #20

Merged
David merged 2 commits from feat/rdp-bridge-va-20 into main 2026-05-19 03:43:57 +02:00
Owner

Summary

Closes VA-20. Adds USAGE_RDP = 7 to the rendezvous tunnel dispatcher. On open, the agent attempts a TCP connect to 127.0.0.1:3389 with a short timeout and, on success, proxies bytes both ways between the relay WS and the local xrdp daemon. Unlike the VNC bridge (VA-19) the agent does not spawn the helper: xrdp is a system service with PAM + xrdp-sesman integration owned by the host's init system. The agent assumes loopback bind, which matches the default Debian / Fedora / Arch packaging.

Stacked on top of VA-19

This branch is built on top of feat/vnc-bridge-va-19 (PR #19) so the docstring + pub(super) control_echo move from that PR are included. Once #19 merges this branch fast-forwards to a clean 1-commit diff against main.

What's in the diff (this commit)

  • crates/meshagent/src/host/rdp.rs (new): run + bidirectional pump (same shape as host::vnc::pump, duplicated rather than shared to keep the modes independently revisable).
  • crates/meshagent/src/host/tunnel.rs: USAGE_RDP constant, dispatch arm, docstring entry.
  • crates/meshagent/src/host/mod.rs: register the new module.
  • README.md: extend the "Runtime dependencies" section with xrdp + xrdp-sesman.

Error model

  • TCP connect refused -> RdpError::DaemonUnavailable(io::Error).
  • TCP connect doesn't return inside 2 s -> RdpError::ConnectTimeout.

A missing or stopped daemon is the common failure case, so the timeout is intentionally short so the operator sees a clean error fast.

Test plan

  • cargo test --workspace --lib (3 new rdp tests pass: pump round-trip, rtt-echo, daemon-unavailable).
  • cargo clippy --workspace --all-targets -- --deny warnings (clean).
  • cargo fmt --all --check (clean).
  • just pre-commit (full docker check: fmt + clippy + build + tests, all green).
  • Manual: install xrdp on a dev host, open a USAGE_RDP = 7 tunnel from the SPA, connect from mstsc.exe end-to-end. Deferred until the server side knows how to issue the new usage code.
## Summary Closes VA-20. Adds `USAGE_RDP = 7` to the rendezvous tunnel dispatcher. On open, the agent attempts a TCP connect to `127.0.0.1:3389` with a short timeout and, on success, proxies bytes both ways between the relay WS and the local `xrdp` daemon. Unlike the VNC bridge (VA-19) the agent does not spawn the helper: xrdp is a system service with PAM + `xrdp-sesman` integration owned by the host's init system. The agent assumes loopback bind, which matches the default Debian / Fedora / Arch packaging. ## Stacked on top of VA-19 This branch is built on top of `feat/vnc-bridge-va-19` (PR #19) so the docstring + `pub(super) control_echo` move from that PR are included. Once #19 merges this branch fast-forwards to a clean 1-commit diff against `main`. ## What's in the diff (this commit) - `crates/meshagent/src/host/rdp.rs` (new): `run` + bidirectional `pump` (same shape as `host::vnc::pump`, duplicated rather than shared to keep the modes independently revisable). - `crates/meshagent/src/host/tunnel.rs`: `USAGE_RDP` constant, dispatch arm, docstring entry. - `crates/meshagent/src/host/mod.rs`: register the new module. - `README.md`: extend the "Runtime dependencies" section with `xrdp` + `xrdp-sesman`. ## Error model - TCP connect refused -> `RdpError::DaemonUnavailable(io::Error)`. - TCP connect doesn't return inside 2 s -> `RdpError::ConnectTimeout`. A missing or stopped daemon is the common failure case, so the timeout is intentionally short so the operator sees a clean error fast. ## Test plan - [x] `cargo test --workspace --lib` (3 new rdp tests pass: pump round-trip, rtt-echo, daemon-unavailable). - [x] `cargo clippy --workspace --all-targets -- --deny warnings` (clean). - [x] `cargo fmt --all --check` (clean). - [x] `just pre-commit` (full docker check: fmt + clippy + build + tests, all green). - [ ] Manual: install xrdp on a dev host, open a `USAGE_RDP = 7` tunnel from the SPA, connect from `mstsc.exe` end-to-end. Deferred until the server side knows how to issue the new usage code.
feat(tunnel): RDP bridge tunnel mode (VA-20)
All checks were successful
Check / clippy + fmt + tests (pull_request) Successful in 1m38s
482a7b1aeb
Add USAGE_RDP=7 tunnel mode that proxies the rendezvous WebSocket to a host-installed `xrdp` daemon on 127.0.0.1:3389. Unlike the VNC bridge (VA-19, USAGE_VNC=6) the agent does not spawn the helper: xrdp is a system service with its own session manager (`xrdp-sesman`) and PAM integration, so the host's init system (systemd / etc.) owns its lifetime. The agent assumes loopback bind, which is the default in Debian / Fedora / Arch packaging.

Lifecycle: the agent attempts a TCP `connect` to 127.0.0.1:3389 with a 2 s timeout. If that fails the tunnel returns `RdpError::DaemonUnavailable` (or `ConnectTimeout`) so a missing daemon is observable from logs instead of presenting as a hung tunnel. On success it pumps bytes both ways, echoing the SPA's `ctrlChannel` rtt probes and dropping other text frames, the same shape as `host::vnc::pump`. The pump is duplicated rather than shared so the two tunnel modes stay independently revisable; the duplication is small and a shared helper would couple `host::vnc` to `host::rdp`.

Documents xrdp + xrdp-sesman as a host runtime dep in README.md, alongside the VNC helpers.

Tests: 3 new unit tests covering bidirectional byte pump through an in-process echo server, `ctrlChannel` rtt-probe echo, and the daemon-unavailable error path from `run` (the test tolerates the false-pass case where a real xrdp happens to be running on the test host).

#VA-20 State Done

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merge branch 'main' into feat/rdp-bridge-va-20
All checks were successful
Check / clippy + fmt + tests (pull_request) Successful in 2m9s
1aeb2fc6ed
David merged commit 1bd398f875 into main 2026-05-19 03:43:57 +02:00
David deleted branch feat/rdp-bridge-va-20 2026-05-19 03:43:57 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-agent!20
No description provided.