Skip to content

feat(server): let the embedder decide when a connection gets an auto-reconnect cookie - #2046

Merged
Benoît Cortier (CBenoit) merged 2 commits into
Devolutions:masterfrom
lamco-admin:feat/server-auto-reconnect-on-request
Oct 9, 2026
Merged

Benoît Cortier (CBenoit) merged 2 commits into
Devolutions:masterfrom
lamco-admin:feat/server-auto-reconnect-on-request

Conversation

@glamberson

@glamberson Greg Lamberson (glamberson) commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • The server sends an auto-reconnect cookie (MS-RDPBCGR 2.2.4.2) to every connection at activation and reissues it hourly (3.3.6.2). MS-RDPBCGR 5.5 has the cookie sent as soon as the user has been authenticated, but an embedder that accepts a connection before the user has proved anything, for instance to draw its own logon screen after a credential validator hands off, gives that connection a cookie it can later present in place of credentials and skip the screen.
  • RdpServerBuilder::with_auto_reconnect_on_request and RdpServer::set_auto_reconnect_on_request switch to issuing cookies only on request: Through AutoReconnectCookieHandle::set once the embedder has authenticated the user, or to a client that came back with a cookie the server verified. The hourly reissue then goes only to a connection that holds a cookie. Off by default, so nothing changes unless an embedder opts in.
  • ConnectionInfo::auto_reconnect tells the embedder that a connection came back with a verified cookie, so it can admit it without asking again.
  • With cookies on request, the embedder vouches for each authenticated connection. The cookie it issues replaces the previous one at once, which is how the regeneration on connect in MS-RDPBCGR 5.5 is met for that connection. A connection the embedder never vouches for neither receives a cookie nor invalidates one, so for such a connection the regeneration doesn't happen. That is deliberate, because a connection that has proved nothing must not be able to invalidate the legitimate user's cookie. A vouch belongs to the connection that was current when it was sent. If that connection is replaced before the server reads the vouch, the vouch is dropped and the replacement gets no cookie until the embedder vouches for it. RdpServer::set_auto_reconnect_on_request documents this contract.

Validation

cargo xtask check fmt/lints/tests/typos/locks all pass. New end-to-end tests in ironrdp-testsuite-extra cover the following. By default the client receives a cookie at activation. On request it receives none until the embedder issues one, and then it does. A cookie the embedder issues invalidates the previous one, so a client presenting the earlier cookie is rejected while the newest is accepted. A vouch the server hasn't read when its connection is replaced doesn't reach the replacement, while a vouch for the live connection still does. The hourly reissue, driven by paused time, still reaches a connection that holds a cookie after a Deactivation-Reactivation pass, and never reaches a connection the embedder did not vouch for. Each test fails if the behaviour it covers is changed. Whether a connection holds a cookie is kept in the connection's own state, so it starts clear for each connection and a Deactivation-Reactivation pass keeps it eligible for the hourly reissue. The client side of the Deactivation-Reactivation sequence in test_deactivation_reactivation is now a shared run_reactivation helper that the new tests reuse, and they connect through the existing connect_client. ironrdp-testsuite-extra enables tokio's test-util feature for the paused clock.

@github-actions github-actions Bot added breaking-change Includes a breaking change, and requires special scrutiny at the boundaries kind/protocol Affects RDP or related protocol behavior risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny size/M Size: up to 449 counted lines and 10 files; exceeds S in either measure needs-review A human reviewer is the current next actor risk/unknown Risk could not be determined automatically; needs maintainer-level scrutiny and removed risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny labels Sep 29, 2026
@github-actions github-actions Bot added risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny and removed needs-review A human reviewer is the current next actor risk/unknown Risk could not be determined automatically; needs maintainer-level scrutiny breaking-change Includes a breaking change, and requires special scrutiny at the boundaries labels Sep 30, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opt-in 'cookie on request' mode is sound: default behavior is unchanged (both predicates are always-true when off), ConnectionInfo is #[non_exhaustive] so the new field is non-breaking, and the issued-cookie tracking is reset per connection before the embedder's events drain. One real conformance/security consequence is published: in on-request mode a connection the embedder never vouches for triggers neither the activation-time send nor the hourly rotation, so commit_auto_reconnect_rotation never regenerates the stored cookie on connect; since verify_auto_reconnect_cookie accepts the current and previous cookies and a verified cookie bypasses credential validation, a prior client's cookie stays valid indefinitely, contrary to MS-RDPBCGR 5.5. Lower-severity items: the new e2e helper duplicates the existing handshake harness (two specialists, merged), the load-bearing reactivation branch is untested, and the unchanged with_auto_reconnect_cookie doc now overstates when the cookie is sen…

  1. [skeptical] with_auto_reconnect_cookie doc still claims unconditional send at activation — low 🟡 — crates/ironrdp-server/src/builder.rs
    The unchanged doc (lines ~439-447) states the server sends the cookie 'right after activation' and generates a fresh random 'whenever a client connects'. With with_auto_reconnect_on_request(true), a builder-provisioned cookie is not sent at activation for a fresh connection (issues_auto_reconnect_at_activation returns false) and no rotation occurs on connect. The new with_auto_reconnect_on_request doc is a bare cross-reference to the setter, so the interaction is not surfaced where an embedder combining both methods would look. Claim-accuracy gap introduced by the behavior change; the doc was not modified by this PR, so line fields are null.

Comment thread crates/ironrdp-server/src/server.rs Outdated
Comment thread crates/ironrdp-testsuite-extra/tests/e2e.rs Outdated
Comment thread crates/ironrdp-server/src/server.rs Outdated
@github-actions github-actions Bot added ai-reviewed/1 One automated review completed needs-author-action The pull request author is the current next actor labels Sep 30, 2026
@glamberson
Greg Lamberson (glamberson) force-pushed the feat/server-auto-reconnect-on-request branch from adb4a9c to 5fe1bcb Compare September 30, 2026 13:35
@github-actions github-actions Bot added risk/medium Behavioral change that does not substantially alter a core public API size/L Size: up to 899 counted lines and 20 files; exceeds M in either measure automation-failed Exact-head automated classification or review failed or was unavailable and removed needs-author-action The pull request author is the current next actor risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny size/M Size: up to 449 counted lines and 10 files; exceeds S in either measure labels Sep 30, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opt-in, off-by-default mode that defers auto-reconnect cookie issuance to embedder vouching and gates the hourly rotation to vouched connections. Core server state machine is correct: default mode is provably unchanged (both new gates short-circuit true), the per-connection issued flag is not derivable from auto_reconnect_sent, and reactivation preserves eligibility as documented, with e2e coverage in both directions. Three findings survive review: (1) a queued vouch is an unaddressed process-wide event that survives takeover cleanup and is delivered to the replacement connection, handing a usable cookie to a connection that proved nothing; (2) on-request mode deliberately departs from the MS-RDPBCGR 5.5 flush-and-regenerate guarantee for unvouched connections, which is documented but keeps a prior user's cookie usable if an embedder does not vouch for a different user's connection; (3) duplicated cookie watch loops in the new test helper. One optional inlining suggestion was rejected…

Comment thread crates/ironrdp-server/src/server.rs
Comment thread crates/ironrdp-server/src/server.rs Outdated
Comment thread crates/ironrdp-testsuite-extra/tests/e2e.rs
@github-actions github-actions Bot added ai-reviewed/2 Two automated reviews completed needs-author-action The pull request author is the current next actor labels Sep 30, 2026
@github-actions github-actions Bot removed the risk/medium Behavioral change that does not substantially alter a core public API label Oct 6, 2026
@CBenoit

Benoît Cortier (CBenoit) commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

PR automation is failing because of a picky-krb 0.12.5 incompatibility, fixed on master by #2074. Please rebase on master to fix it.

Update: no rebase needed anymore. picky-krb 0.12.5 was yanked from crates.io (re-released as 0.13.0), so the API check builds again without changes to this branch. PR automation has been re-run here and passes.

@github-actions github-actions Bot added risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny needs-review A human reviewer is the current next actor and removed risk/unknown Risk could not be determined automatically; needs maintainer-level scrutiny automation-failed Exact-head automated classification or review failed or was unavailable needs-review A human reviewer is the current next actor labels Oct 7, 2026
@glamberson
Greg Lamberson (glamberson) force-pushed the feat/server-auto-reconnect-on-request branch from f8443c7 to eea6a19 Compare October 8, 2026 13:21

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #2046 adds an opt-in auto-reconnect cookie mode to IronRDP's server: `with_auto_reconnect_on_request` / `set_auto_reconnect_on_request` defer cookie issuance until the embedder vouches via `AutoReconnectCookieHandle` or a client returns with a verified cookie; hourly rotation is gated on a new per-connection `auto_reconnect_issued` bit; queued `SetAutoReconnectCookie(Some)` vouches are dropped in `discard_stale_session_events` when on-request mode is active; and `ConnectionInfo::auto_reconnect` exposes verified reconnects. Default behavior is preserved because every new gate short-circuits when the flag is off, and the discard-loop rewrite is behaviorally equivalent for existing events. Tests cover issuance timing, vouch invalidation, stale-vouch dropping, and hourly rotation across reactivation. I independently verified the single code-compressor candidate in pr-head: `connect_active_client` in e2e.rs (lines 1586-1597) is generic over a `FnOnce` connector factory but has exactly o…

Comment thread crates/ironrdp-testsuite-extra/tests/e2e.rs Outdated
@github-actions github-actions Bot added ai-reviewed/3 Final automated review completed needs-author-action The pull request author is the current next actor and removed ai-reviewed/2 Two automated reviews completed labels Oct 8, 2026
@github-actions github-actions Bot added breaking-change Includes a breaking change, and requires special scrutiny at the boundaries and removed needs-author-action The pull request author is the current next actor labels Oct 8, 2026
@github-actions github-actions Bot added the needs-review A human reviewer is the current next actor label Oct 8, 2026
…reconnect cookie

The server sends an auto-reconnect cookie (MS-RDPBCGR 2.2.4.2) to every
connection at activation and reissues it hourly (3.3.6.2). MS-RDPBCGR 5.5
has the cookie sent as soon as the user has been authenticated, but an
embedder that accepts a connection before the user has proved anything,
for instance to draw its own logon screen after a credential validator
hands off, gives that connection a cookie it can later present in place
of credentials and skip the screen.

RdpServerBuilder::with_auto_reconnect_on_request and
RdpServer::set_auto_reconnect_on_request switch to issuing cookies only on
request: through AutoReconnectCookieHandle::set once the embedder has
authenticated the user, or to a client that came back with a cookie the
server verified. The hourly reissue then goes only to a connection that
holds a cookie. Off by default, so nothing changes unless an embedder
opts in.
connect_active_client took a connector factory, and its only caller
used it to apply an optional cookie. It takes the Option of the cookie
itself now, which drops the generic parameter, the where clause and the
closure at the call site.
@glamberson
Greg Lamberson (glamberson) force-pushed the feat/server-auto-reconnect-on-request branch from d10ff36 to 94ca777 Compare October 8, 2026 23:42
@github-actions github-actions Bot added needs-review A human reviewer is the current next actor and removed needs-review A human reviewer is the current next actor breaking-change Includes a breaking change, and requires special scrutiny at the boundaries labels Oct 8, 2026
@CBenoit
Benoît Cortier (CBenoit) merged commit bf019ca into Devolutions:master Oct 9, 2026
43 checks passed
@github-actions github-actions Bot removed the needs-review A human reviewer is the current next actor label Oct 9, 2026
Greg Lamberson (glamberson) added a commit to lamco-admin/IronRDP that referenced this pull request Oct 10, 2026
The auto-reconnect end-to-end test added by Devolutions#2046 builds a TestDisplay
literal that predates the new field, so the extra suite stopped
compiling once this branch met master.

This branch was successfully deployed

1 active deployment
llm-providers — 94ca777a Deployed Oct 8, 2026 by glamberson via Classify pull request #2194
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-reviewed/3 Final automated review completed kind/protocol Affects RDP or related protocol behavior risk/high Substantial core public API impact, or fail-closed triage; needs maintainer-level scrutiny size/L Size: up to 899 counted lines and 20 files; exceeds M in either measure

Development

Successfully merging this pull request may close these issues.

2 participants