Running the xrdp process as non root - neutrinolabs/xrdp GitHub Wiki

Introduction

Scope

This information applies to version 0.9.13 of XRDP in a standard configuration with xrdp and xrdp-sesman daemons.

A default build and install of XRDP will run the xrdp-sesman(8) and xrdp(8) daemons as root. xrdp-sesman needs to run as root, as it is responsible for user session management. However, it is possible for the xrdp process to be run as a non-privileged user if the XRDP software is packaged appropriately. This reduces the damage which could be caused by an exploitable security flaw in xrdp at the expense of increased installation complexity.

Testing

Care needs to be taken in testing that the xrdp daemon is functioning correctly after reducing its privileges. For example, if the TLS certificate is not readable by the daemon, you could well be operating with standard RDP security, which is arguable less secure than running the xrdp daemon as root.

Previous work

The Debian package xrdp implements this functionality.

Access required by the xrdp daemon

The xrdp daemon needs to be able to do the following of note:-

Function Comment Debian implementation
Listen on the TCP port configured in /etc/rdp/xrdp.ini (normally 3389) This does not normally need special privilege
Read the server TLS certificate and private key see below see below
Write to the log file see below see below
(if not run in the foreground) Create the PID file see below see below
Connect to xrdp-sesman to authenticate the user Normally xrdp-sesman listens on a local TCP port and no special permissions are required to connect to it
(following user authentication) Connect to the xrdp-chansrv(8) process running inside the user session. see below see below
(following user authentication) Connect to the X server (Xvnc or Xorg) process see below see below

Read TLS files

By default the TLS certificate and private key are read from /etc/xrdp/cert.pem and /etc/xrdp/key.pem respectively, although other locations can be configured in /etc/xrdp/xrdp.ini.

Read access to the TLS certificate doesn't need to be restricted - the certificate is freely provided to any connecting client which asks for it.

However, it is important that the private key is only readable by the xrdp daemon. When xrdp is run as root, this file is normally owned by root, and the permissions mask is set to 0600.

Write to the log file

The log file is configured in /etc/rdp/xrdp.ini. A path can be specified for this, but if one is not, the path defaults to ${localstatedir}/log, where ${localstatedir} is configured at compile time.

Create the PID file

The PID file is normally written to the directory ${localstatedir}/run, where ${localstatedir} is configured at compile time.

Connect to xrdp-chansrv

The xrdp-chansrv process creates listening sockets in the socksdir directory. The xrdp daemon needs read-write access to these to create a connection with xrdp-chansrv.

Connect to the X server

If Xvnc is used the connection is made over a standard socket. No special permissions are required for this. Xvnc will be run with the UID and GID of the session user.

If Xorg is used, the Xorg server creates a listening socket in the socksdir directory. The xrdp daemon needs read-write access to this socket.

Debian implementation

This section describes how xrdp-0.9.9-1 is packaged for Debian 10 to allow xrdp to be run as a non-root user.

Creation of non-root user

The postinst dpkg script creates a user xrdp using the following command:-

adduser \
	--quiet --system --group --no-create-home \
	--disabled-password --disabled-login \
	--home /run/xrdp xrdp

On Debian 10, /var/run is symlinked to /run. So, the home directory given for the xrdp user /run/xrdp is equivalent to /var/run/xrdp where the PID of the xrdp process is stored (see below).

Systemd files

xrdp.service unit file directives

User and Group

These directives are used to start the xrdp daemon as the unprivileged user.

RuntimeDirectory

This directive is used to create and remove /var/run/xrdp. The directory is created when the daemon starts, and deleted when it exits.

ExecStartPre

An ExecStartPre script is also configured. This runs as root before the xrdp daemon is run as the xrdp user.

xrdp.service ExecStartPre script

This Bourne shell script takes the following actions:-

  • Ensures the directory /var/run/xrdp is present, is owned by root:xrdp and has a protection mask of 2775.
  • Ensures the directory /var/run/xrdp/sockdir (the sockdir) is present, is owned by root:xrdp and has a protection mask of 3777.

Interestingly, immediately after this script runs, the RuntimeDirectory script systemd directive (see systemd.exec(5)) changes the following:-

  • ownership of /var/run/xrdp to xrdp:xrdp
  • protection mask of /var/run/xrdp to 0775.
  • ownership of /var/run/xrdp/sockdir to xrdp:xrdp.

This does not affect the operation of the XRDP sub-system. Some tidying up could possibly be done here.

Read TLS files

The Debian xrdp package has a dependency on the ssl-cert package. The installation of ssl-cert creates a self-signed TLS certificate and private key for the host. Soft-links are created from /etc/xrdp/cert.pemand/etc/xrdp/key.pem` to point to these files.

The private key created by ssl-cert is owned by root:ssl-cert with a protection mask of 0640. This allows members of the ssl-cert group to access the private key.

The Debian packagers have taken the choice to not automate adding the xrdp user to the ssl-cert group - see Debian bug #860890. So in order for xrdp to use the ssl-cert self-signed TLS certificate, the following command needs to be entered manually by an administrator:-

sudo adduser xrdp ssl-cert

Write to the log file

The file /var/log/xrdp.log is pre-created in the dpkg postinst script with an ownership of xrdp:adm and a protection mask of 0640. This allows the xrdp daemon to write directly to the log file.

Create the PID file

At package build time the Makefiles generated by the configure script are modified by the Debian package rules file. The modification sets the compile-time macro XRDP_PID_PATH to /var/run/xrdp rather than the package default of /var/run. This causes the PID files for the xrdp and xrdp-sesman daemons to be deposited in /var/run/xrdp. This directory is made group-writeable by the ExecStartPre script, and so the daemon is able to create the PID file.

Connect to xrdp-chansrv

At package build time, the Debian package rules file configures the socksdir directory to be at /var/run/xrdp/sockdir.

This directory is set to be owned by :xrdp with a protection mask of 03777 by the ExecStartPre script. The extra bit in the protection mask is the set-GID bit. This ensures that files created in the socksdir inherit a group ownership of xrdp, and so the xrdp daemon is able to access the sockets in this directory.

A patch is made to common/os_calls.c:g_mk_socket_path() when the package is built to change the protection mask on a created socksdir to 03777. This patch is needed to avoid a possible race condition between the xrdp-sesman and xrdp daemons when the sub-system is started. Without the patch, it is possible for the xrdp-sesman daemon to set the permissions on the socksdir to 01777.

Connect to the X server

On Debian, Xorg is not run as set-uid root. The same considerations apply to the X server socket as apply to the xrdp-chansrv sockets described above.