Skip to main content

Linux Images: XRDP Configuration

Last updated: July 31, 2026Latest Frontend Version: 2.17.4

Enable XRDP if users need a graphical desktop session via RDP. XRDP provides the RDP server, and users connect through tiCrypt's TLS tunnel infrastructure.

caution

GNOME on RHEL 8+ defaults to Wayland, which is incompatible with XRDP's Xorg backend. Use Xfce or MATE as the desktop environment for reliable RDP sessions.

1. Install Packagesโ€‹

XRDP is in the EPEL repository:

dnf install -y epel-release
dnf install -y xrdp xorgxrdp
dnf groupinstall -y "Xfce" --with-optional
Backend choice

The xorgxrdp package provides a native Xorg backend for XRDP, giving the best performance and clipboard/drive redirection support. The alternative is Xvnc (via tigervnc-server), which is more forgiving of Xorg version mismatches but has higher latency and limited channel support. Use xorgxrdp unless you encounter Xorg compatibility issues after a kernel or mesa update.

2. Configure the Session Managerโ€‹

Replace the contents of /etc/xrdp/startwm.sh with the following. This preserves user profile sourcing (/etc/profile, ~/.profile) so environment variables, PATH modifications, and module loads are available in the desktop session:

#!/bin/sh

if [ -r /etc/profile ]; then
. /etc/profile
fi
if [ -r ~/.bash_profile ]; then
. ~/.bash_profile
elif [ -r ~/.bash_login ]; then
. ~/.bash_login
elif [ -r ~/.profile ]; then
. ~/.profile
fi

unset DBUS_SESSION_BUS_ADDRESS
unset XDG_RUNTIME_DIR

exec startxfce4
caution

Do not use a bare exec startxfce4 without the profile sourcing above. Without it, users will not have their PATH, environment variables, or shell profile customizations in the desktop session.

3. Configure PAM for Session Registrationโ€‹

The default /etc/pam.d/xrdp-sesman on RHEL does not include pam_systemd.so. Without it, systemd-logind does not register the RDP session, /run/user/<uid> is not created, and desktop features that depend on XDG_RUNTIME_DIR (file manager operations, PolicyKit prompts, audio) will fail.

Add the following line to /etc/pam.d/xrdp-sesman after the pam_loginuid.so line:

sed -i '/pam_loginuid.so/a session optional pam_systemd.so' /etc/pam.d/xrdp-sesman

4. Configure Session Managementโ€‹

Edit /etc/xrdp/sesman.ini and adjust the [Sessions] section for a multi-user VM. The default Policy=Default already supports session reconnect (users reconnect to their existing desktop rather than getting a new one). Adjust the reaping settings so disconnected sessions do not accumulate indefinitely:

[Sessions]
MaxSessions=100
KillDisconnected=true
DisconnectedTimeLimit=28800
SettingValueEffect
MaxSessions100Maximum concurrent sessions (active + disconnected). Set above your expected user count with headroom.
KillDisconnectedtrueTerminate sessions that have been disconnected longer than DisconnectedTimeLimit
DisconnectedTimeLimit28800Seconds (8 hours) before a disconnected session is reaped. Users who reconnect within this window rejoin their existing desktop.
note

Do not add I (IP) to the Policy setting. tiCrypt traffic arrives through the tunnel infrastructure, so the source IP seen by XRDP is the tunnel endpoint, not the user's real IP. Adding I would prevent session reconnect.

5. Restrict Clipboard Redirectionโ€‹

By default XRDP shares the clipboard between the user's local machine and the remote desktop, which is a data-exfiltration path for secure-research VMs. To disable the clipboard entirely, set cliprdr=false in the [Channels] section of /etc/xrdp/xrdp.ini:

[Channels]
cliprdr=false

Then restart the XRDP services:

systemctl restart xrdp xrdp-sesman
Directional clipboard control (newer XRDP versions)

Newer XRDP can restrict the clipboard by direction, configured in the [Security] section of /etc/xrdp/sesman.ini (not xrdp.ini):

[Security]
RestrictOutboundClipboard=all
RestrictInboundClipboard=all

RestrictOutboundClipboard controls copying from the session out to the client (the data-exfiltration direction); RestrictInboundClipboard controls pasting into the session from the client. Both accept:

ValueEffect
noneNo restriction (default)
allBlock all clipboard transfer in that direction
text, file, imageBlock only that content type; combine as a comma-separated list, for example text,file

To let researchers paste into the VM but never copy anything out, block only the outbound direction and leave inbound unrestricted:

[Security]
RestrictOutboundClipboard=all
RestrictInboundClipboard=none
Confirm the clipboard restriction is actually in effect

Directional control is not available on older XRDP, and a version that does not recognize a key silently ignores it and leaves the clipboard open. Confirm the keys exist for your installed version with man sesman.ini (older releases exposed a similar setting in xrdp.ini), apply the change, restart with systemctl restart xrdp xrdp-sesman, then verify from a real session that copy and paste are actually blocked in the direction you intended.

6. Fix Startup Orderingโ€‹

The stock xrdp-sesman.service unit only declares After=network.target, which means it can start before D-Bus, NFS mounts, or the VM Controller's user provisioning are ready. This race condition causes intermittent RDP failures where some VMs from the same image work and others do not, or a VM works until its next restart.

Create systemd drop-in overrides to enforce correct ordering:

mkdir -p /etc/systemd/system/xrdp-sesman.service.d
cat > /etc/systemd/system/xrdp-sesman.service.d/override.conf <<'EOF'
[Unit]
After=dbus.service network-online.target
Wants=dbus.service network-online.target

[Service]
Restart=on-failure
RestartSec=2
StartLimitIntervalSec=60
StartLimitBurst=10
EOF

mkdir -p /etc/systemd/system/xrdp.service.d
cat > /etc/systemd/system/xrdp.service.d/override.conf <<'EOF'
[Unit]
After=xrdp-sesman.service
Requires=xrdp-sesman.service

[Service]
Restart=on-failure
RestartSec=2
EOF

systemctl daemon-reload
danger

The Restart=on-failure directives are critical. Without them, if an RDP connection attempt arrives before the VM Controller has finished provisioning users, xrdp-sesman can crash or enter a bad state and systemd will not restart it. This produces a permanently broken RDP service on that VM while identical VMs launched from the same image work fine.

7. Disable NSS Caching Daemonsโ€‹

systemctl disable --now sssd nscd 2>/dev/null
note

sssd and nscd cache name-service lookups, including negative results. If an RDP login attempt triggers a user lookup before the VM Controller has created that account, the "user not found" response is cached. Subsequent attempts fail even after the account exists, until the cache entry expires. On an isolated VLAN with no AD or LDAP, these services provide no benefit. Disable them so NSS lookups resolve directly from /etc/passwd.

8. Enable Servicesโ€‹

systemctl enable xrdp xrdp-sesman

9. Configure controller.tomlโ€‹

The controller must allow port 3389 for RDP tunneling. Add the following to the [tunnel] section:

[tunnel]
allowedPorts = 3389

[tunnel.services]
rdp = 3389

10. Verifyโ€‹

systemctl status xrdp xrdp-sesman
ss -tlnp | grep 3389

XRDP should be listening on port 3389. A full end-to-end test requires a deployed VM with a provisioned user.