Timo Denissen of the Professional Service Team of Univention described in February with the blog article “Desktops with Guacamole remote control” how computers can be remote controlled via the browser. In this How To I would like to show how this principle can be extended with the help of privacyIDEA and xRDP to a terminal server environment which can be used completely in the browser, integrated into the domain of the UCS and secured by 2-factor authentication.
I assume in the HowTo that a functional UCS Master already exists. I run this virtualized using Proxmox. I use a second VM for the terminal server environment.
The following steps are described in detail in this HowTo:

  1. Prepare LinuxMint with xRDP
  2. Installing and configuring privacyIDEA and RADIUS on the UCS Master
  3. Integrate xRDP with privacyIDEA
  4. Install and configure Guacamole with RADIUS Plugin

Prepare LinuxMint with xRDP

I use a LinuxMint 19.1 XFCE installation to build the terminal server on. LinuxMint runs as a virtual machine on a Proxmox cluster and does not have its own graphics card with 3D support. If this would be available and passed through to the VM, Cinnamon can also be used as a desktop. The basic installation is carried out normally with the installation wizard and the user local-admin is created.
After the restart I log on to the virtual console and configure the static IP address 10.0.0.2 and set the UCS Master (IP address 10.0.0.1) as DNS server.
I update the package sources and for administration I install openssh-server (basically on all VM’s) and store my SSH public key in the admin user accounts in ~/.ssh/authorized_keys.
In the next step I integrate LinuxMint according to the Univention instructions in the documentation for Ubuntu clients, which also works unchanged for LinuxMint. According to documentation the Ubuntu Domain Join Client is compatible with LinuxMint by now.
On the desktop I can already log in as a domain user for testing purposes.
Next I install the xRDP server:

sudo apt-get install xrdp xorgxrdp xrdp-pulse audio installer

To let xRDP use the XFCE desktop, I replace the last two lines of the file /etc/xrdp/startwm.sh

test -x /etc/X11/Xsession && exec /etc/X11/Xsession
exec /bin/sh /etc/X11/Xsession

by

usr/bin/startxfce4

Now the login works with an RDP client like Remmina from my own Linux desktop or with the Windows Remote Desktop Client.
I will not go any further into configuration of the desktop here.
The first step has now been taken. We can access our LinuxMint desktop via RDP.

Install and set up privacyIDEA

I install privacyIDEA via the Univention App Center on my UCS Master. For this I need the apps “RADIUS”, “privacyIDEA” and “privacyIDEA RADIUS”. privacyIDEA RADIUS integrates into the RADIUS server of the UCS, which is based on FreeRADIUS.
To allow the LinuxMint computer to establish a connection to the UCS Radius server, I make it know as a client to freeradius. For this I make the following entry in the file /etc/freeradius/3.0/clients.conf as user root on the UCS server

client linuxmint.example.com {
ipaddr = 10.0.0.2
secret = changeme
}

Afterwards I restart the freeradius server.

service freeradius restart

After installing privacyIDEA I login to privacyIDEA Administration via the UCS Portal. There I log in with the account “Administrator@admin” and the administrator password of the UCS domain. After the info dialog I create a new policy under “Configuration” -> “Policies” to configure the user login with OTP.

attribute name

attribute value

policy name

login_userpass_otp

scope

authentication

action

Miscellaneous

otppin: userstore

auth_cache: 2m

user realm

example.com

user resolver

users

priority

1

I still need a second policy to be able to pass on user details from the LDAP.

attribute name

attribute value

policy name

return_user_details

scope

authorization

action

Miscellaneous

add_user_in_response: check

user realm

example.com

user resolver

users

priority

1

Since I turned on auth_cache with the first policy, I need a cron job that cleans it up every day to keep the database clean. The purpose of auth_cache will be explained later.
I create the file /etc/cron.daily/pi-authcache-cleanup on the UCS server with the following content:

#!/bin/sh
/opt/privacyidea/privacyidea-venv/bin/pi-manage authcache cleanup
Then I activate the privacyIDEA RADIUS module on the UCS server via the Univention Configuration Registry web interface or command line:
ucr set privacyidea/radius/enable=1

The privacyIDEA documentation describes how the RADIUS configuration can be tested from the command line. For this purpose the software package freeradius-utils must be installed on the LinuxMint computer. To enable testing of RADIUS without OTP a second policy can be created with scope “authentication” in which the option “passthru” is set to “userstore”. If the test is successful, the policy can be deactivated, a TOTP token can be rolled out for a user (e.g. with the apps privacyIDEA Authenticator or FreeOTP Authenticator for Android and iOS) and tested again. This time the OTP value is appended directly to the password.

The second step is completed! We can authenticate users of the UCS domain with domain password + OTP via RADIUS.

Integrate xRDP with privacyIDEA

Next I will connect the authentication of xRDP to privacyIDEA. Since the RADIUS server already knows the LinuxMint as a client, I use the PAM RADIUS module.

sudo apt-get install libpam-radius-auth
In the file /etc/pam_radius_auth.conf I add the RADIUS server and the secret.
Now I add the following statement to the file /etc/pam.d/xrdp-sesman
auth required pam_radius_auth.so
so that it looks like this:
#%PAM-1.0
auth sufficient pam_radius_auth.so
@include common-auth
@include common-account
@include common-session
@include common-password
After a restart of LinuxMint I can now log in with an RDP client with username and password + OTP.
Alternatively, the privacyIDEA PAM module could also be used here.

Find out more about Guacamole!

Ensure 24/7 access to your systems from all around the globe by using Guacamole. Conveniently available in our UCS AppCenter. Read more about the neat features of Guacamole in this article.

Guacamole_logo

Setting up Guacamole with RADIUS extension

The Guacamole version, which is included in the UCS App-Store, comes without the RADIUS module. Therefore we will build Guacamole with Docker based on the GitHub repository. Although this currently requires a little more manual work, RADIUS enables integration with privacyIDEA and thus central management of the second factors. For Guacamole you could also set up your own VM with Docker. To keep the HowTo simple, I decided to use Docker and Guacamole on the LinuxMint as well.
Therefore we first install Docker and docker-compose:
sudo apt-get install docker.io docker-compose
and download the current source code of the Guacamole Client:
cd ~
mkdir dev
wget https://github.com/apache/guacamole-client/archive/master.zip
unzip master.zip
Next we create the docker-compose configuration for Guacamole:

cd ~
mkdir -p docker/guacamole
cd docker/guacamole
mkdir certs dbinit mysql-data

cat >docker-compose.yml <<EOF
version: '3'
services:
guacamole:
image: guacamole:latest
build:
context: ../../dev/guacamole-client-master
args:
- BUILD_PROFILE=lgpl-extensions
restart: always
# Set your hostname to the FQDN under which your
# sattelites will reach this container
hostname: linuxmint.example.com
env_file: ./env.secrets
environment:
- MYSQL_HOSTNAME=mariadb
- MYSQL_DATABASE=guacamole_db
- MYSQL_USER=guacamole_user
- GUACD_HOSTNAME=guacd
- RADIUS_EXT_LINKNAME=1-guacamole-auth-radius
- RADIUS_HOSTNAME=10.0.0.1
- RADIUS_AUTH_PROTOCOL=pap
- VIRTUAL_HOST=linuxmint.example.com
- LOGBACK_LEVEL=warn
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "10"
volumes:
- ./dbinit:/root/dbinit
networks:
- guac-tier
guacd:
image: guacamole/guacd
restart: always
environment:
- GUACD_LOG_LEVEL=warning
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "10"
networks:
- guac-tier
mariadb:
image: mariadb:10.4
restart: always
env_file: ./env.secrets
environment:
- MYSQL_DATABASE=guacamole_db
- MYSQL_USER=guacamole_user
volumes:
- ./dbinit/initdb.sql:/docker-entrypoint-initdb.d/initdb.sql
- ./mysql-data:/var/lib/mysql
networks:
- guac-tier

nginx-proxy:
image: jwilder/nginx-proxy
restart: always
ports:
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./certs:/etc/nginx/certs
networks:
- guac-tier

networks:
guac-tier:
EOF

Let’s take a look at what the docker-compose configuration does. First of all the Guacamole container is defined and that the image will be built from the source code we checked out before. The build profile is important to include the Radius module in that image.

services:
guacamole:
image: guacamole:latest
build:
context: ../../dev/guacamole-client-master
args:
- BUILD_PROFILE=lgpl-extensions

The following environment variables tell the Radius module where the Radius server is running and which protocol to use.

- RADIUS_EXT_LINKNAME=1-guacamole-auth-radius
- RADIUS_HOSTNAME=10.0.0.1
- RADIUS_AUTH_PROTOCOL=pap

To make Guacamole accessible via HTTPS, a Nginx is used as reverse proxy before. The docker image used for this generates its configuration for each container that defines the environment variable VIRTUAL_HOST.

- VIRTUAL_HOST=linuxmint.example.com

For the Guacamole containers the logfile rotation is configured, so that the logging of the Guacamole containers does not fill my hard disk.

logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "10"

The MySQL database container and the Nginx container are also defined. In a separate file we define the passwords and reference it from docker-compose. These should in any case be replaced by secure passwords.

cat >env.secrets <<EOF
MYSQL_ROOT_PASSWORD=changeme
MYSQL_PASSWORD=changeme
RADIUS_SHARED_SECRET=changeme
EOF

Now I can build the Guacamole Client Docker Image:

sudo docker-compose build

Before I start the environment, I generate the database initialization script using the Guacamole image.

sudo docker run --rm guacamole /opt/guacamole/bin/initdb.sh --mysql > ./dbinit/initdb.sql

I also need the certificate for the Nginx Reverse Proxy. I generate this on the UCS Master and copy it to the LinuxMint VM. The file must be named by the fully qualified domain name (FQDN) and the file extension .crt or .key. The FQDN must match the value of the VIRTUAL_HOST variable in the docker-compose.yml file.

ssh root@10.0.0.1
cd /etc/univention/ssl
univention-certificate new -name linuxmint.example.com -days 365
cd linuxmint.example.com
scp cert.pem local-admin@10.0.0.2:/home/local-admin/docker/guacamole/certs/linuxmint.example.com.crt
scp private.key local-admin@10.0.0.2:/home/local-admin/docker/guacamole/certs/linuxmint.example.com.key

Now the whole environment can be started and the log output can be monitored:

sudo docker-compose up -d
sudo docker-compose logs -f

I wait until the log output calms down and does not bring much new anymore. Then I open the Guacamole web interface: https://linuxmint.example.com/guacamole

On the web interface I can now log in with user name and password + OTP and get an empty connection overview. Authentication works with this. Now you have to configure the authorization in Guacamole. Therefore I log off again and log on with the user “guacadmin” with password “guacadmin”. First I change the password via the user menu in the upper right corner -> “Settings” -> “Preferences”.

Then I create a new connection with the name “LinuxMint” via the user menu -> “Settings” -> “Connections”. The following values are essential:

attribute name

attribute value

name

LinuxMint

protocol

RDP

Parameters -> Network

host name

10.0.0.2

Parameters -> Authentication

username

${GUAC_USERNAME}

password

${GUAC_PASSWORD}

Parameters -> Display

Color depth

True color (24-bits)

In the user menu -> “Settings” -> “Users” I create another user with my user name from the domain without password. I now assign the connection to the user. Alternatively, I could create a group in Guacamole, assign the connection to the group, and add the user to the group. Unfortunately the Guacamole RADIUS module does not support groups yet. A corresponding feature request can be found in the Issue Tracker of the project.
For users who only have permission for one connection, Guacamole opens the connection immediately after login. The variables in the username and password cause Guacamole to pass the username and password used to log in to Guacamole when the connection is opened.
The authentication cache of privacyIDEA, which we defined in the “login_userpass_otp” policy, is now relevant here. Since the password contains an OTP, the automatic login to xRDP would return an error, since a one-time passcode can only be used once according to its name. To make the login process more comfortable for the users, I decided to use the Auth-Cache. With this privacyIDEA accepts the OTP multiple times for the configured 2 minutes. You can test if a lower settings works for your environment.
In an environment with a limited number of users, security can be further improved by restricting access to the NGINX before Guacamole, e.g. with client certificate authentication.

Summary

That’s it. Using xRDP, privacyIDEA and Guacamole, a web-based open source remote desktop environment with 2-factor authentication is up and running.

Although access to local files and printers of the client with Guacamole (see Guacamole FAQ) is not yet possible as it is with Windows RDP, the solution offers companies an open source alternative for a remote desktop environment with a good security level.

Use UCS Core Edition for Free!
Download now

Comments

  1. Anubhab

    October 23, 2019 at 16:47

    That is indeed a great way to do some cost cutting by not relying on proprietary softwares.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *