Skip to main content

Proxied RDP sessions

Connect with mstsc, and the gateway signs in to the Windows server with a vaulted password you never see. What is recorded is an event transcript, not a video.

Written for whoever runs IT9 min readUpdated

A proxied RDP session gives an administrator a Windows desktop on a server without giving them the server's password. They open Remote Desktop, connect to an address Qorionix gave them, type anything at all into the credential box, and land on the desktop signed in as the vaulted account. The password is fetched from the vault inside the gateway and injected into the connection on the wire.

One expectation needs correcting before you read further, because getting it wrong will cost you an audit. A recorded RDP session is not a video. There is no screen recording, no playback of what the desktop looked like, and no way to produce one from the stored data. What you get instead is described below, and for most of the questions people actually ask a session recording, it is better than a video. But if you were told this product records administrators' screens, you were told something we do not do.

What the RDP gateway actually is

It is a Remote Desktop implementation we wrote. Not a wrapper around FreeRDP, xrdp or Guacamole, and no C library underneath it — around four and a half thousand lines of Go that speak the Microsoft RDP wire protocols directly, along with the authentication and virtual-channel protocols that ride on them.

That is an unusual decision and it has a reason. To inject a credential into an RDP connection you have to sit inside the protocol at the exact point where the client tells the server who it is, replace that message, and keep the rest of the session coherent. A relay that only copies bytes cannot do it. Writing the protocol is the price of the feature, and it also means the gateway understands enough of the session to record what happened rather than only how many bytes moved.

Unlike the SSH path, the operator side of an RDP session is real protocol. You connect with a normal Remote Desktop client. There is no raw-socket workaround here.

Connecting to a session

  1. Request the session and read the endpoint

    You get a host, a port and an expiry. The port is one of a kind: a fresh port is bound for your session alone, it accepts exactly one connection, and it closes two minutes after it is created if nobody arrives.

  2. Open Remote Desktop and connect to that host and port

    In the Windows client the form is host:port. Do not connect to the target server's own address — that bypasses the gateway entirely and you will need the real password.

  3. Accept the certificate warning

    Your client will warn you that it cannot verify the gateway's identity. This is expected in every deployment today, and the reason is in the section below.

  4. Type anything into the credential prompt

    Whatever you type is discarded by the gateway. It never reaches the target. Some people type their own name so the audit trail of their own client is honest about who was sitting there; that is a reasonable habit but it has no effect on the platform.

  5. Work

    You are signed in as the vaulted account. The session ends when you disconnect, when someone terminates it, or at the maximum session duration — eight hours by default. There is no idle timeout, so disconnect when you are finished.

How the credential is injected, and how you know it worked

Early in an RDP connection the client sends a message containing the domain, username and password it wants to log on with. The gateway intercepts that message, throws away what your client sent, and writes in the vaulted account and secret. It also sets the flag that tells the server to log on automatically, and it clears the flag that claims the credentials came from a saved profile — because once the gateway has substituted them, that claim would be false.

This is enforced rather than attempted. If the gateway cannot rewrite that message for any reason, it does not fall back to passing your client's credentials through. It fails the session:

rdp: cannot rewrite Client Info PDU, refusing to forward operator credentials

Where the target supports Network Level Authentication — the mechanism where a Windows server makes you prove who you are before it will draw a login screen — the gateway performs that exchange upstream itself and delegates the vaulted credential through it. It implements only NTLMv2 with extended session security. NTLMv1, LM, anonymous authentication and Kerberos are not implemented and are not offered, so the gateway cannot silently negotiate down to something weak. The practical consequence is the honest one: a target that will only accept Kerberos cannot be proxied.

Injection is then checked against the server's own account of events. Windows sends back a message naming the identity it actually logged on, and the gateway compares that with the identity it injected. A mismatch does not get buried in a metadata field on a routine close event — it raises its own audit entry, pam.gateway.identity_mismatch, so that a session which signed in as somebody other than the account you granted is visible as an event and not as a detail.

What the gateway refuses to do

Several failures you might expect to be handled gracefully are instead refusals. Each one is a place where the graceful behaviour would have exposed the credential.

A target that will not do TLS
RDP has a legacy mode called Standard Security which encrypts with a scheme that a passive observer can defeat. The gateway does not use it and does not downgrade to it: rdp: target selected %s; the gateway requires TLS because RDP Standard Security would expose the credential to a passive observer. A very old server may therefore be unreachable through the gateway. That is the correct outcome — proxying it would put the vaulted password on the wire in a recoverable form.
A target whose certificate does not match
You can pin the target server's TLS certificate fingerprint. When it is pinned and does not match, the session ends with rdp: target certificate fingerprint %s does not match the pinned value. This is real protection against somebody standing between the gateway and your server. The SSH path now has its own equivalent — it records each machine's host key on first contact and refuses a machine that later presents a different one; see SSH host-key pinning. The two differ in one way worth knowing: SSH pinning happens by itself on first connection, whereas an RDP fingerprint is only pinned if you pin it.
RDP-level encryption layered on top of TLS
The gateway records that the target asked for it, and then refuses. Two encryption layers with one of them weak is not twice the security.
Authenticating you to the gateway with CredSSP
The gateway does not run Network Level Authentication on the leg facing you. To do that it would have to act as an authentication server for your account, which would mean knowing your password. It deliberately does not, which is also why your client's credential prompt accepts anything.

One asymmetry to note: the gateway does not verify the target's certificate against a trust store, only against a fingerprint you pin. The justification is that it records the fingerprint on every session and binds the authentication exchange to the server's public key, so a substitution is detectable after the fact and much harder to perform. If you are proxying anything sensitive, pin the fingerprint and get the guarantee up front rather than in the audit log.

Why there is no video, and what you get instead

An RDP session carries a stream of graphics updates encoded in one of four compression formats. The gateway relays them byte for byte to your client and records each one as a type, a size and — where the protocol makes it cheap to read — the rectangle of the screen that changed. It does not decode the pixels. Decoding them means implementing four separate image codecs, and that work was deliberately not done.

So the graphics half of the recording answers one question: was the screen active, and where, at a given moment. It is enough to drive a timeline and to show that something was happening at 14:32. It cannot reconstruct what was on the screen, and no future tool will be able to, because the pixel data was never stored in a decodable form.

What is stored, in full and in a structured form, is the input and the side channels:

  • Every keystroke, decoded from the scancode into a key name and, for a US-QWERTY layout, the character. Reassembled, this gives you the text the administrator typed.
  • Mouse movement and clicks, including the extended button events.
  • Clipboard activity — that the clipboard was used, and what kind of data crossed it. Copying a file list off a server is one of the more common ways data leaves it, and it is one of the few things a screen recording would show you only by accident.
  • Drive and device redirection — a mapped local drive, a redirected printer, a smart card reader. Each becomes its own recorded event.
  • Logon and error events from the server, including the identity it logged on and any error it reported.

Clipboard, file access and device redirection are promoted out of the recording body into their own database rows, because they are the events somebody comes looking for. Keystrokes, mouse movement and graphics stay inside the recording.

The stored recording declares itself as application/rdp-event-jsonl, one JSON event per line. It was once declared as video/webm, which was aspirational and made the replay endpoint lie to whatever loaded it; that has been corrected. If you see a console string that promises a recorded video, that is a copy error we have not yet fixed, and this article is the accurate description.

The session player on the Privileged Sessions screen reflects this rather than papering over it. A terminal session replays as text at its original pace; an RDP session is presented as a timeline of the individual recorded events — keystrokes, mouse activity, clipboard use, file access, window changes, screen updates and sign-on — as a readable list you can click through to jump to a moment. Still no pixels. A structured account of what happened is what exists, and it is what the player shows. (Playback of any recording does not currently work at all for a separate reason — see Session recording and transcripts.)

One caveat on the keystroke decode: the mapping from scancode to character assumes a US-QWERTY keyboard. An administrator typing on a German or French layout produces correct key names and, for some keys, the wrong character in the reconstructed text. Read a transcript from a non-US keyboard as an indication rather than as a quotation.

One policy decision made for you

The gateway is configured to allow Network Level Authentication upstream but not to require it. Requiring it would be the stricter setting, and it would make RDP proxying fail against the older Windows servers that a company without a security team very often still runs. The judgement was that a feature which refuses half of a real estate does not get used, and an unused gateway protects nothing. If your estate is uniformly modern, this is a setting worth asking us to tighten for you.

What to do next

  1. Add the Windows server as a target and set the port to the one Remote Desktop is actually listening on.
  2. Read what a recording contains — especially before you promise an auditor a session replay.
  3. Set the access policy that decides duration, approval and recording for this credential.

Was this article wrong?

If a procedure here does not match what you see, or a limit we described has changed, tell us and we will fix the page. Email us about this article, or see how to get help if you need an answer rather than a correction.

Everything in privileged access