Goal of this article is to document the setup of a Guacamole HTML5 remote XFCE desktop via VNC on Ubuntu 22.04.x. The Guacamole setup is based on Ubuntu 20.04.x LTS - Guacamole HTML5 Remotedesktop Gateway installieren mit Apache Reverse Proxy (in german, written by Bernhard Linz)

Install the guacamole server

So, let’s assume we have a fresh Ubuntu 22.04.x installation on a remote machine with DNS name test.example.org running a ssh server. First, we become root:

sudo -i

Next, we install everything that is needed to run the Apache Tomcat server:

apt install make libssh2-1-dev libtelnet-dev libpango1.0-dev libossp-uuid-dev libcairo2-dev libpng-dev libssh2-1 libvncserver-dev libvorbis-dev  gcc libssh-dev libpulse-dev tomcat9 tomcat9-admin tomcat9-docs ghostscript libwebp-dev libavcodec-dev libavutil-dev libswscale-dev libjpeg-turbo8-dev libtool-bin libossp-uuid-dev libavformat-dev freerdp2-dev libwebsockets-dev libssl-dev

Now, on calling http://test.example.org:8080 we should see the standard Apache Tomcat “It works!” page.

As of April 2023, the current Guacamole version is 1.5.1, so we download the following files:

cd /usr/src
wget https://downloads.apache.org/guacamole/1.5.1/source/guacamole-server-1.5.1.tar.gz
wget https://downloads.apache.org/guacamole/1.5.1/binary/guacamole-1.5.1.war

So we have server source code and client binary.

Now we unpack the server code

tar xvzf guacamole-server-1.5.1.tar.gz

and compile it:

cd /usr/src/guacamole-server-1.5.1
./configure --with-systemd-dir=/etc/systemd/system

That should result in an output like this:

------------------------------------------------
guacamole-server version 1.5.1
------------------------------------------------

   Library status:

     freerdp2 ............ yes
     pango ............... yes
     libavcodec .......... yes
     libavformat.......... yes
     libavutil ........... yes
     libssh2 ............. yes
     libssl .............. yes
     libswscale .......... yes
     libtelnet ........... yes
     libVNCServer ........ yes
     libvorbis ........... yes
     libpulse ............ yes
     libwebsockets ....... yes
     libwebp ............. yes
     wsock32 ............. no

   Protocol support:

      Kubernetes .... yes
      RDP ........... yes
      SSH ........... yes
      Telnet ........ yes
      VNC ........... yes

   Services / tools:

      guacd ...... yes
      guacenc .... yes
      guaclog .... yes

   FreeRDP plugins: /usr/lib/x86_64-linux-gnu/freerdp2
   Init scripts: no
   Systemd units: /etc/systemd/system

Type "make" to compile guacamole-server.

If that’s the case as all “yes”-entries are present, we can compile:

make
make install

Last we have to configure the dynamic linker run-time bindings:

ldconfig

Now we can start the systemd service that the installation has provided:

systemctl start guacd.service
systemctl status guacd.service

which should give us an output like

● guacd.service - Guacamole Server
     Loaded: loaded (/etc/systemd/system/guacd.service; disabled; vendor preset: enabled)
     Active: active (running) since Fri 2023-04-21 14:52:28 UTC; 6s ago
       Docs: man:guacd(8)
   Main PID: 47807 (guacd)
      Tasks: 1 (limit: 2233)
     Memory: 10.2M
        CPU: 10ms
     CGroup: /system.slice/guacd.service
             └─47807 /usr/local/sbin/guacd -f

Apr 21 14:52:28 test181 systemd[1]: Started Guacamole Server.
Apr 21 14:52:28 test181 guacd[47807]: Guacamole proxy daemon (guacd) version 1.5.1 started
Apr 21 14:52:28 test181 guacd[47807]: guacd[47807]: INFO:        Guacamole proxy daemon (guacd) version 1.5.1 started
Apr 21 14:52:28 test181 guacd[47807]: guacd[47807]: INFO:        Listening on host 127.0.0.1, port 4822
Apr 21 14:52:28 test181 guacd[47807]: Listening on host 127.0.0.1, port 4822

We stop the server in order to amend configurations:

systemctl stop guacd.service

First we create the directory

mkdir /etc/guacamole

There we create a file /etc/guacamole/guacamole.properties with the content

basic-user-mapping: /etc/guacamole/user-mapping.xml

For our purposes, this file reads like

<user-mapping>
        <!-- Per-user authentication and config information -->
        <!-- FIRST USER -->
        <authorize 
                username="test01"
                password="52c88bca7c5b3ab3e7ed8901ea1bc83f"
                encoding="md5">
                <!-- First authorized Remote connection -->
                <connection name="SSH Admin">
                        <protocol>ssh</protocol>
                        <param name="hostname">10.10.10.10</param>      
                        <param name="port">22</param>                    
                        <param name="username">test01</param>     
                        <param name="enable-sftp">true</param>
                </connection> 
                <!-- second authorized Remote connection -->
                <connection name="Terminal Server VNC">
                        <protocol>vnc</protocol>
                        <param name="hostname">127.0.0.1</param> 
                        <param name="port">5901</param>         
                        <param name="server-layout">de-de-qwertz</param>
                </connection>
        </authorize>
</user-mapping>

Of course we have to amend hostname, username, and password for our system.

The password hash can be created by

echo -n 'secretPassword' | md5sum

Finally, we have to append the Guacamole home to the environment:

echo GUACAMOLE_HOME="/etc/guacamole" >> /etc/environment

It makes sense to reboot at this point.

Now we enable the Guacamole service for automated start:

systemctl enable guacd.service

Next, we install the client:

cp /usr/src/guacamole-1.5.1.war /var/lib/tomcat9/webapps/guacamole.war

Then we have to link the property directory to the place where it is expected:

ln -s /etc/guacamole /usr/share/tomcat9/.guacamole

To speed up the tomcat restart, we have to create the following file

touch /usr/share/tomcat9/bin/setenv.sh
chmod +x /usr/share/tomcat9/bin/setenv.sh

with this content:

#!/bin/sh
export CATALINA_OPTS="$CATALINA_OPTS -Djava.security.egd=file:/dev/./urandom"

Now we can start the guacamole server:

systemctl start guacd.service

If we now browse to

http://test.example.org:8080/guacamole/

we should be offered a login and then the connection options “SSH Admin” and “Terminal Server VNC”.

The SSH connection should work already, for the VNC we need to prepare some more things, as follows.

Install the VNC server

Let’s assume that we have a user test01 as our (sudo-enabled) standard user for the machine we just prepared, and that we are now logged in as that user.

We install the needed stuff:

sudo apt install -y xfce4 xfce4-goodies tightvncserver autocutsel xfonts-base xfonts-100dpi xfonts-75dpi

Then we create a vnc password for our user:

vncpasswd

Next, we have to create the vnc startup file in our home directory and make it executable:

touch ~/.vnc/xstartup
chmod 755 ~/.vnc/xstartup

The contents of this file is

#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
exec startxfce4 

For starting the VNC server on boot, we normally would need a service file /etc/systemd/system/vncserver@.service, as suggested by Install Xfce VNC remote desktop on Ubuntu. Unfortunately, for Ubuntu 22.04.x this method does not longer work (it will give some mysterious “/usr/bin/startxfce4: X server already running on display :1” log entry), and we have to find a workaround. This goes as follows:

We want to login automatically on boot, so we create a service unit /lib/systemd/system/getty@.service as follows:

#  SPDX-License-Identifier: LGPL-2.1-or-later
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Getty on %I
Documentation=man:agetty(8) man:systemd-getty-generator(8)
Documentation=http://0pointer.de/blog/projects/serial-console.html
After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
After=rc-local.service

# If additional gettys are spawned during boot then we should make
# sure that this is synchronized before getty.target, even though
# getty.target didn't actually pull it in.
Before=getty.target
IgnoreOnIsolate=yes

# IgnoreOnIsolate causes issues with sulogin, if someone isolates
# rescue.target or starts rescue.service from multi-user.target or
# graphical.target.
Conflicts=rescue.service
Before=rescue.service

# On systems without virtual consoles, don't start any getty. Note
# that serial gettys are covered by serial-getty@.service, not this
# unit.
ConditionPathExists=/dev/tty0

[Service]
# the VT is cleared by TTYVTDisallocate
# The '-o' option value tells agetty to replace 'login' arguments with an
# option to preserve environment (-p), followed by '--' for safety, and then
# the entered username.
ExecStart=-/sbin/agetty --autologin test01 --noclear %I $TERM
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
IgnoreSIGPIPE=no
SendSIGHUP=yes

# Unset locale for the console getty since the console has problems
# displaying some internationalized messages.
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION

[Install]
WantedBy=getty.target
DefaultInstance=tty1

In the Service section, we replace test01 by the username of our local user.

If the service should not be enabled by default, we make the service available at boot time by

cd /etc/systems/system/getty.target.wants/
sudo ln -sf /lib/systemd/system/getty@.service .

To start the VNC server after the automatic login, we append the following line to our ~/.bashrc:

echo "/usr/bin/vncserver :1 -depth 24 -geometry 1920x1080 -localhost" >> ~/.bashrc

Time for a last reboot!

Then, we should be able to login to http://test.example.com:8080/guacamole with the credentials we stored in /etc/guacamole/user-mapping.xml, click on “Terminal Server VNC”, type in the VNC password we have defined before, and see a virtual XFCE screen in our browser.

That’s it! Now we have set up a Guacamole HTML5 remote XFCE desktop via VNC for Ubuntu 22.04.

For security reasons, it makes absolutely sense to add a 2FA as described in the already mentioned (german) article by Bernhard Linz. For security reasons, it makes also sense to map the Tomcat (http port 8080) via Apache or nginx reverse proxy to https, as described here in my blog.