Remote desktop (RDP)
An RDP application opens a Windows server's desktop in the user's browser through Web Access. It runs the same way as an isolated browser: the RDP client runs on an RBI host, and the user's browser only receives the picture. How an application is configured in general is on configuring applications.
What an RDP application is
An application whose upstream is rdp://host[:port] (port 3389 when it names none) is an RDP target. It must also
have:
mode: isolated— an RDP target runs only isolated;identity: credentialwith akind: rdpaccount in OpenBao: the Windows account the session signs in with, and its optional domain;- either
rdp_cert_fingerprints(each entry with thesha256:prefix) to pin the server's certificate, orrdp_ignore_cert, which does not check the certificate at all; - the
rdpmodule in the licence.
An RDP target has no HTTP: the HTTP-only keys of an application are refused on it, and an http_rules entry that
names it makes the policy fail to load.
How a session runs
- The user opens the application; the page at
/_isg/rbi/viewconnects to the broker, which starts the RDP session on an RBI host's guacd. - guacd does not reach the Windows server itself. Its RDP connection comes back to the broker over the agent's
connection, and the broker dials the application's configured upstream (
APP_RDP_UPSTREAM_CONNECTED, orAPP_RDP_UPSTREAM_FAILED). Port 3389 is opened from the broker, as every other target is: the single funnel stays. - The broker reads the account from OpenBao when the session starts and hands it to guacd. The user does not see the password and does not type it.
- The session is a session record like any Web Access one, with protocol
rdp:isg sessionsnames the protocol next to the application, the web UI's sessions table shows it in its own RDP group, and its RBI lines (APP_RBI_SESSION_STARTED,APP_RBI_SESSION_CLOSED) and its upstream lines are on the record, soisg audit --sessionlists them. - An RDP session has no command log: its trace is the screen recording and the audit log.
Channels
Every channel is off by default. Four of them can be switched on in policy.yaml, per application and role, with the
rdp_channels key:
clipboard_to_remote— the clipboard from the user's computer to the remote desktop, only when the user asks for it: the view's Send clipboard button sends the local clipboard's text (the browser asks once for permission to read it). Nothing is sent on focus or unasked;clipboard_from_remote— the clipboard from the remote desktop to the user's computer;printer— printing: the remote desktop's printer produces a PDF that downloads to the user's computer;audio_out— the remote desktop's sound.
Drives and the microphone are not available. A rule names its applications (apps, required, RDP applications
only), optionally its roles (roles; empty means every role) and the channels it opens (allow, required). The
matching rules add up; when none matches, every channel stays off:
rdp_channels:
- name: win-ops-clipboard
apps: [APP-2026-000005]
roles: [operator, sysadmin]
allow: [clipboard_to_remote, clipboard_from_remote]
- name: win-sysadmin-print
apps: [APP-2026-000005]
roles: [sysadmin]
allow: [printer, audio_out]
A rule that names an application which is not an RDP target makes the policy fail to load, as an http_rules
entry naming an RDP target does; an application apps.yaml does not define is only a warning. The channels are fixed
when the session connects: a reloaded policy.yaml does not change a running RDP session, only the next connection.
The channels are enforced twice: guacd gets only the opened ones, and the broker's tunnel drops a stream of a channel
that is off. APP_RDP_UPSTREAM_CONNECTED lists the session's channels (an empty list when all are off). Every
clipboard transfer and every print is one APP_RDP_CHANNEL_TRANSFER line, written when it ends: user, app,
app_id, rbi_id, session_id, channel, mimetype, bytes (the transferred size), filename (printing only)
and blocked — an attempt on a channel that is off is a line too, with blocked true. The content itself never goes
into the log. Sound is not logged per stream; channels shows whether it was on.
Viewer grace
When the user's view closes (the tab is closed, the network drops), the RDP session waits for the user to come back,
and the same Windows session continues if the view reopens in time. On an RDP application the wait is 60 seconds by
default, not the isolated browser's viewer_grace_seconds from rbi.yaml. An application can set its own with
viewer_grace_seconds in apps.yaml (30 to 3600 seconds), an RDP target and an isolated browser alike.
Recording, live view and termination
Every RDP session is recorded as a picture, in the Guacamole format, and plays back on the session's page in the web
UI, as an isolated browser's recording does (APP_RBI_RECORDING_STORED). The recording is sent to the broker when the
session ends; until it arrives, the session's page says that it is being processed.
An administrator who may close sessions can watch a running RDP session at /sessions/live. The view is read-only:
the viewer's mouse and keyboard do not reach the remote desktop (checked on a live RDP session on 2026-11-01).
Closing the session from the sessions table ends the remote desktop too, within about 10 seconds: the open view
reads the record on its regular check, and the user's view ends with APP_SESSION_CLOSED, reason admin_close.
When the connection ends from the target's side — the user signs out of Windows, the network drops, or another
connection takes the Windows session over — the ISG session ends too: the record closes with reason target_closed,
APP_RDP_UPSTREAM_CLOSED names who ended it (ended_by: the target, or the ISG itself for every close the ISG
made), and APP_SESSION_CLOSED follows with reason target_closed. The user's view says the remote computer ended
the connection and offers no reconnect button: the application has to be opened again. Reloading the view or
closing its tab is not the target's close; the session stays.
When the ISG closes a session, the Windows session stays on the target in the disconnected state unless the target limits it. The target's prerequisite is the "Set time limit for disconnected sessions" group policy.
One live session per account
Windows hands a second connection with the same account the first one's desktop and drops the first. The ISG therefore allows one live session per RDP application account at a time, across every broker:
- The same user opens it again (a second tab, another browser): the new session takes over. The earlier record
closes with reason
superseded(itsSESSION_CLOSEDnames the user as the closer, viatakeover), its application session ends withAPP_SESSION_CLOSED, reasonsuperseded, and its view says a newer opening took it over. The new session connects. - Another user opens it: the open is refused with HTTP 409 and a page naming the holder — the account, the user,
the session and since when.
APP_ACCESS_DENIEDis written with reasonrdp_account_busy, withaccount,holder_userandholder_session_id. No session record opens.
If two users open the same account on two brokers at the same moment, the earlier record stays and the later one
closes with reason account_busy, with the same 409 page. The session record names its account (isg sessions show,
Account:).
reserved_usernames on RDP
On an RDP application reserved_usernames is a plain list of ISG user names that may not open it. It does not
guard the Windows account: the account is the stored credential, not the user's name — the same as on an
identity: credential HTTP application.