Apache Guacamole – Remote Desktop Gateway (RDG)

[Letzte Aktualisierung: 25.10.2020] Apache Guacamole ist ein Clientless Remotedesktop-Gateway. Es unterstützt Standardprotokolle wie VNC, RDP und SSH. Dank HTML5 wird nach der Installation auf einem Server lediglich ein halbwegs aktueller Webbrowser benötigt, um auf Rechner/Server zuzugreifen. Mit erscheinen der Version 1.2.0 am Anfang des Jahres und der damit verbundenen Features (TOTP, Radius Modul oder Integration der Zwischenablage) ist es auch für große Umgebungen interessant.

Aufbau Testumgebung

1x Server mit Ubuntu 20.04 LTS, IP-Adresse: 192.168.199.2, Hostname: guacamole01

1x Server mit Windows Server 2019 (Active Directory), IP-Adresse: 192.168.199.3, Hostname: dc01

Name der DNS-Zone: lab03.wydler.eu

Installation von Guacamole

Vorwort

Standardmäßig erfolgt bei Guacamole die Verwaltung der Benutzer über die Datei user-mapping.xml (Chapter 5. Configuring Guacamole). Daher soll als Backend der Datebank Server MariaDB zum Einsatz kommen. Somit kann die Benutzerverwaltung vollständig über die Weboberlfäche administriert werden. Daher kann die Verwaltung auch durch nicht techniches Personal erfolgen.

DNS

Zu aller erst muss für den Server einen Hostnamen zugeordnet werden. Dies erfolgt in der Regel bei der Installation des Betriebssystems Ubuntu. Da der Server eine statische IP-Adresse (optimalerweise IPv4 und IPv6) muss im DNS Server noch entsprechend die Zuordnung IP-Adresse -> Hostname erfolgen. Dies erfolgt durch sogeannten A-Einträge in der jeweiligen Zone im DNS-Server.

In meinen Fall lautet der Hostname des Servers guacamole01. Der FQDN heißt guacamole01.lab03.wydler.eu. Bitte in den nachfolgenden Schritten nicht vergessen, den Namen für eure Umgebung anzupassen!

Voraussetzungen installieren

Abhängigkeiten für Guacamole installieren.

apt install -y libjpeg62-dev
apt install -y libjpeg-turbo8-dev
apt install -y libcairo2-dev libpng-dev libtool-bin libossp-uuid-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev freerdp2-dev libpango1.0-dev libssh2-1-dev libtelnet-dev libvncserver-dev libwebsockets-dev libpulse-dev libssl-dev libvorbis-dev libwebp-dev

Installation von MariaDB und Apache Tomcat.

apt install -y make tomcat9 mariadb-server

Nach den Installationsarbeiten muss die Startseite des Apache Tomcat über die (öffentliche) IP-Adresse bzw. DNS Namen (http://guacamole01.lab03.wydler.eu:8080/) erreichbar sein.

Installation der Applikation

Damit bei allen nachstehenden Befehlen die korrekte Versionsnummer von Guacamole genutzt wird, speichern wir diese zuerst ein einer Variable. Die aktuelle Version ist wie bereits oben erwähnt 1.2.0.

export guacver=1.2.0
echo $guacver

Herunterladen der notwendigen Dateien.

wget --trust-server-names "https://apache.org/dyn/closer.cgi?action=download&filename=guacamole/$guacver/source/guacamole-server-$guacver.tar.gz" -O /usr/src/guacamole-server-$guacver.tar.gz
wget --trust-server-names "https://apache.org/dyn/closer.cgi?action=download&filename=guacamole/$guacver/binary/guacamole-$guacver.war" -O /usr/src/guacamole-$guacver.war

Entpacken der Archvidatei.

tar xvzf /usr/src/guacamole-server-$guacver.tar.gz -C /usr/src/

Installation der Anwendung.

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

make
make install
ldconfig

Anlegen der Konfigurationverzeichnisse.

mkdir /etc/guacamole
mkdir /etc/guacamole/extensions
mkdir /etc/guacamole/lib
echo GUACAMOLE_HOME=\"/etc/guacamole\" >> /etc/environment

Applikation in das WebApp von Tomcat kopieren.

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

Dienste (neu) starten.

systemctl enable guacd.service
systemctl start guacd.service
systemctl status guacd.service

systemctl restart tomcat9.service

Windows RDP Verbindungsprobleme

Aktuell gibt es Probleme bei der einer Verbindung auf Basis von RDP in Verbindung FreeRDP2. In der Datei „“ wird beim Verbindungsaufbau der RDP Sitzung folgende Zeilen protokolliert.

Oct 25 14:09:28 guacamole01 guacd[45483]: FreeRDP initialization may fail: The current user's home directory ("/usr/sbin") is not writable, but FreeRDP generally requires a writable home directory for storage of configuration files and certificates.
Oct 25 14:09:28 guacamole01 guacd[45483]: guacd[45483]: WARNING:#011FreeRDP initialization may fail: The current user's home directory ("/usr/sbin") is not writable, but FreeRDP generally requires a writable home directory for storage of configuration files and certificates.

Abhilfe schafft das Anlegen einen neuen Benutzers mit einem Zufallspasswort. Dieser wird anschließend in der systemd Konfiguration für Guacd.

Neuer Benutzer anlegen.

adduser guacd --disabled-password --disabled-login --gecos ""

Konfiguration für systemd anpassen.

sed -i -e 24c"\#User=daemon" /etc/systemd/system/guacd.service
sed -i -e 25i"User=guacd" /etc/systemd/system/guacd.service

Dienste neu starten.

systemctl daemon-reload
systemctl restart guacd.service

Funktionstest

Nach dem alle Dienste gestartet sind ist die Anwendung grundsätzlich funktionstüchtig. In dem noch offenen Browser Tab den Pfad /guacamole/ anhängen. Es erscheint nun die Anmeldeseit der Applikation.

Konfiguration der Datenbankauthentifizierung

Voraussetzungen

Download der notwendigen Dateien.

wget --trust-server-names "https://apache.org/dyn/closer.cgi?action=download&filename=guacamole/$guacver/binary/guacamole-auth-jdbc-$guacver.tar.gz" -O /usr/src/guacamole-auth-jdbc-$guacver.tar.gz
wget "https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.21.tar.gz" -O /usr/src/mysql-connector-java-8.0.21.tar.gz

tar xvzf /usr/src/guacamole-auth-jdbc-$guacver.tar.gz -C /usr/src/
tar xvzf /usr/src/mysql-connector-java-8.0.21.tar.gz -C /usr/src/

Erweiterungen aus dem jeweiligen Archiv in das Konfigurationverzeichnis kopieren.

cp /usr/src/guacamole-auth-jdbc-$guacver/mysql/guacamole-auth-jdbc-mysql-$guacver.jar /etc/guacamole/extensions/
cp /usr/src/mysql-connector-java-8.0.21/mysql-connector-java-8.0.21.jar /etc/guacamole/lib/

Grundeinrichtung

Erzeugen eines Zufallspassworts für den neuen Datenbankbenutzer für die Anwendung.

export dbpw=$(openssl rand -hex 8)
echo $dbpw

Danach kann der entsprechende Benutzer, Datenbank, dazugehörige Berechtigungen und das Schema für Guacamole import werden.

mysql -u root -p -e "CREATE USER 'guacamole'@'localhost' IDENTIFIED BY '$dbpw';"
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS guacamole DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;"
mysql -u root -p -e "GRANT SELECT,INSERT,UPDATE,DELETE,CREATE ON guacamole.* TO 'guacamole'@'localhost' IDENTIFIED BY '$dbpw' WITH GRANT OPTION;"
mysql -u root -p -e "FLUSH PRIVILEGES;"

mysql -uguacamole -p$dbpw guacamole < /usr/src/guacamole-auth-jdbc-$guacver/mysql/schema/001-create-schema.sql
mysql -uguacamole -p$dbpw guacamole < /usr/src/guacamole-auth-jdbc-$guacver/mysql/schema/002-create-admin-user.sql

Erstellen der notwendigen Konfigurationsdatei mit den dazugehörigen Parameter für diese Erweiterung.

cat << EOF > /etc/guacamole/guacamole.properties
#
# Hostname and Guacamole server port
#
guacd-hostname: 127.0.0.1
guacd-port: 4822
# 
# MySQL properties
#
mysql-hostname: 127.0.0.1
mysql-port: 3306
mysql-database: guacamole
mysql-username: guacamole
mysql-password: $dbpw
EOF

Fehler: The server time zone value ‚CEST‘ is unrecognized or represents more than one time zone

Bei der Verwendung von MariaDB als Datenbank Server sind auf Grund eines Fehlers in Guacamole noch Anpassungen an dessen Konfiguration notwendig. Und zwar taucht in der Datei /var/log/syslog folgende Fehlermeldung auf:

### Error querying database. Cause: java.sql.SQLException: The server time zone value ‚CEST‘ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the ’serverTimezone‘ configuration property) to use a more specifc time zone value if you want to utilize time zone support.

Die Lösung dafür ist, die selbe Zeitzone des Servers auch für den Datenbank-Server zu konfigurieren.

cp /etc/mysql/mariadb.conf.d/50-server.cnf /etc/mysql/mariadb.conf.d/50-server.cnf.orginal

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
sed -i '30 i\# Timezone' /etc/mysql/mariadb.conf.d/50-server.cnf
sed -i '31 i\default_time_zone=Europe/Berlin' /etc/mysql/mariadb.conf.d/50-server.cnf
sed -i '32 i\ ' /etc/mysql/mariadb.conf.d/50-server.cnf

systemctl restart mariadb.service

Neustart des Apache Tomcat nicht vergessen.

systemctl restart tomcat9.service

Abschließend in den Browser wechseln, den Inhalt über die Schaltfläche „Akutalisieren“ oder über die Taste F5 neu laden. Erscheint nach wie vor die Anmeldeseite ist das Backend nun auf den Datenbank Server umgestellt.

Standardpasswort ändern

Der Benutzername lautet „guacadmin“ und das Standardpasswort ist ebenfalls „guacadmin“. Daher empfiehlt es sich nach der Installation gleich das Passwort zu ändern. Nach der erfolgreich en Anmeldung oben rechts auf das Menü klicken und anschließend den Eintrag „Einstellungen“ auswählen. Danach den Reiter Einstellungen anklicken.

Standardmäßig gibt es keine Passwortrichtlinie. Somit ist sowohl die Länge des Passworts als auch die Komplexität nicht relevant. Wer dieses Verhalten anpassen möchte, kann die Parameter hier unter den Abschnitten Enforcing password policies, Password age / expiration und Preventing password reuse nachlesen.

Reverse Proxy

Aktuell ist der Apache Tomcat Server über Port 8080/tcp im Internet erreichbar. Das ist natürlich für den vorgesehen Einsatz nicht praktikabel, da dieser Port nur selten freigeschalten ist. Abhilfe schafft ein ReverseProxy auf dem selben Server. Dafür nutze ich den Apache Webserver.

Installation von Apache Webserver

apt install apache2 -y

a2enmod rewrite
a2enmod proxy_http
a2enmod ssl
a2enmod proxy_wstunnel

systemctl restart apache2.service

Erstellen der Webserver Konfiguration für den Hostnamen.

TAB="$(printf '\t')"
cat << EOF > /etc/apache2/sites-available/guacamole01.lab03.wydler.eu.conf
<VirtualHost *:80>
${TAB}ServerName guacamole01.lab03.wydler.eu
${TAB}
${TAB}ServerAdmin hostmaster@wydler.eu
${TAB}DocumentRoot /var/www/html
${TAB}
${TAB}
${TAB}ProxyPass / http://127.0.0.1:8080/guacamole/ flushpackets=on
${TAB}ProxyPassReverse / http://127.0.0.1:8080/guacamole/
${TAB}ProxyPassReverseCookiePath /guacamole /
${TAB}<Location /websocket-tunnel>
${TAB}${TAB}Order allow,deny
${TAB}${TAB}Allow from all
${TAB}${TAB}ProxyPass ws://127.0.0.1:8080/guacamole/websocket-tunnel
${TAB}${TAB}ProxyPassReverse ws://127.0.0.1:8080/guacamole/websocket-tunnel
${TAB}${TAB}</Location>
${TAB}SetEnvIf Request_URI "^/tunnel" dontlog
${TAB}CustomLog  /var/log/apache2/guac.log common env=!dontlog
${TAB}
</VirtualHost>
EOF

Konfiguration für Apache Webserver aktivieren.

ln -s /etc/apache2/sites-available/guacamole01.lab03.wydler.eu.conf /etc/apache2/sites-enabled/guacamole01.lab03.wydler.eu.conf

Die Standardkonfiguration für den Webserver löschen.

rm /etc/apache2/sites-enabled/000-default.conf

Damit die Änderungen auch wirksam werden, muss der Apache2 neu gestartet werden.

systemctl restart apache2.service

Konfiguration der Firewall

Selbstverständlich darf die Grundkonfiguration einer Firewall grundsätzlich nicht fehlen. Ich greife hier auf UFW zurück. Diese ist inzwischen standardmäßig bei der eingesetzten Distribution und Version installiert.

ufw allow ssh
ufw allow http
ufw allow https
echo y | ufw enable
ufw status

Funktionstest

Nun wechseln wir in den Browser zurück und rufen die Weboberfläche von Apache Guacamole auf.

Die Angabe des Pfads /guacamole/ ist durch die Konfiguration des ReverseProxy nicht mehr notwendig.

Apache mit Let’s Encrypt absichern

Die Verbindungen bzw. Datenverkehr gehört heute grundsätzlich verschlüsselt. Das ist sozusagen State of the art. Nachgehend wird die Installation und Konfiguration sowie der Abruf eines Zertifikats von der kostenlosen Zertifizierungstelle Lets Encrypt beschrieben.

Voraussetzungen installieren.

apt install certbot python3-certbot-apache -y

SSL Zertifikat abrufen.

certbot --apache -m hostmaster@wydler.eu --agree-tos

Der Assistent fragt anschließend noch verschieden Parameter ab.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: N
Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: guacamole01.lab03.wydler.eu
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 1
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

Congratulations! You have successfully enabled: https://guacamole01.lab03.wydler.eu.

Die Seite wird nun ausschließlich über SSL (HTTPS) ausgeliefert. Wer gerne noch die Sicherheits erhöhen möchte, empfehle ich den Artikel Apache – Anpassung SSL Protokolle und Cipher Suites.

LDAP Authentifizierung mit Active Directory

In diesem Aschnitt geht es um die Anbindung eines LDAP Verzeichnisses, das Microsoft Active Directory.

Vorbereitungen im Active Directory

Für die Verwendung der Erweitertung „LDAP“ sind natürlich auch einige Voraussetzungen im Active Directory notwendig. Die folgenden Befehle bitte in der PowerShell ausführen. In meinem Lab habe ich erst einmal definierte Organisationseinheiten angelegt, damit ich die notwendigen Objekte klar strukturiert ablegen kann.

New-ADOrganizationalUnit -Name "lab03Serviceobjekte" -Path "DC=lab03,DC=wydler,DC=eu"
New-ADOrganizationalUnit -Name "lab03Gruppen" -Path "DC=lab03,DC=wydler,DC=eu"
New-ADOrganizationalUnit -Name "lab03Benutzer" -Path "DC=lab03,DC=wydler,DC=eu"

Der Zugriff auf Guacamole soll ausschließlich möglich sein, wenn der anzumeldene Benutzer Mitglieder der Gruppe „ggpro-guacamole-logon-access“ ist.

New-ADGroup -Name "ggpro-guacamole-logon-access" -GroupScope Global -GroupCategory Distribution -Path "OU=lab03Gruppen,DC=lab03,DC=wydler,DC=eu"

Zum Schluss lege ich noch einen sogenannten Service Benutzer an. Dieser soll später für Guacamole genutzt werden, um die Verbindung zum Active Directory aufzubauen.

$password = "87b161cbe54958da!" | ConvertTo-SecureString -AsPlainText -Force
New-ADUser -Name "guacamoleadmin" -Path "OU=lab03Serviceobjekte,DC=lab03,DC=wydler,DC=eu" -AccountPassword $password -CannotChangePassword $True -Enabled $True

Das Passwort „87b161cbe54958da“ entspricht nicht den heutigen Anforderungen und muss unbedingt individualisiert werden!

Installation und Konfiguration der Erweiterung

Notwendige Dateien herunterladen und in das Konfigurationverzeichnis kopieren.

wget --trust-server-names "https://apache.org/dyn/closer.cgi?action=download&filename=guacamole/$guacver/binary/guacamole-auth-ldap-$guacver.tar.gz" -O /usr/src/guacamole-auth-ldap-$guacver.tar.gz

tar xvzf /usr/src/guacamole-auth-ldap-$guacver.tar.gz -C /usr/src/
cp /usr/src/guacamole-auth-ldap-$guacver/guacamole-auth-ldap-$guacver.jar /etc/guacamole/extensions/

Die notwendigen Parameter werden in die Datei /etc/guacamole/guacamole.properties hinzugefügt.

SPA="$(printf ' ')"
cat << EOF >> /etc/guacamole/guacamole.properties
#
#LDAP Einstellungen für Active Directory
#
${SPA}
#The hostname of your LDAP server. If omitted, "localhost" will be used by default. You will need to use a different value if your LDAP server is located elsewhere.
ldap-hostname: dc01.lab03.wydler.eu
${SPA}
#The port your LDAP server listens on. If omitted, the standard LDAP or LDAPS port will be used, depending on the encryption method specified with ldap-encryption-method (if any). Unencrypted LDAP uses the standard port of 389, while LDAPS uses port 636. Unless you manually configured your LDAP server to do otherwise, your LDAP server probably listens on port 389.
ldap-port: 389
${SPA}
#The encryption mechanism that Guacamole should use when communicating with your LDAP server. Legal values are "none" for unencrypted LDAP, "ssl" for LDAP over SSL/TLS (commonly known as LDAPS), or "starttls" for STARTTLS. If omitted, encryption will not be used.
ldap-encryption-method: none
${SPA}
#The DN (Distinguished Name) of the user to bind as when authenticating users that are attempting to log in. If specified, Guacamole will query the LDAP directory to determine the DN of each user that logs in. If omitted, each user's DN will be derived directly using the base DN specified with ldap-user-base-dn.
ldap-search-bind-dn: CN=guacamoleadmin,OU=lab03Serviceobjekte,DC=lab03,DC=wydler,DC=eu
${SPA}
#The password to provide to the LDAP server when binding as ldap-search-bind-dn to authenticate other users. This property is only used if ldap-search-bind-dn is specified. If omitted, but ldap-search-bind-dn is specified, Guacamole will attempt to bind with the LDAP server without a password.
ldap-search-bind-password: 87b161cbe54958da!
${SPA}
#The base of the DN for all Guacamole users. This property is absolutely required in all cases. All Guacamole users must be descendents of this base DN.
ldap-user-base-dn: OU=lab03Benutzer,DC=lab03,DC=wydler,DC=eu
${SPA}
#The attribute or attributes which contain the username within all Guacamole user objects in the LDAP directory. Usually, and by default, this will simply be "uid". If your LDAP directory contains users whose usernames are dictated by different attributes, multiple attributes can be specified here, separated by commas, but beware: doing so requires that a search DN be provided with ldap-search-bind-dn.
ldap-username-attribute: samAccountName
${SPA}
#The base of the DN for all user groups that may be used by other extensions to define permissions or that may referenced within Guacamole configurations using the standard seeAlso attribute. All groups which will be used to control access to Guacamole configurations must be descendents of this base DN. If this property is omitted, the seeAlso attribute will have no effect on Guacamole configurations.
ldap-group-base-dn: OU=lab03Gruppen,DC=lab03,DC=wydler,DC=eu
${SPA}
#Das Attribut oder die Attribute, die den eindeutigen Namen der Benutzergruppen im LDAP-Verzeichnis definieren. Normalerweise und standardmäßig ist dies einfach "cn". Wenn Ihr LDAP-Verzeichnis Gruppen enthält, deren Namen durch unterschiedliche Attribute vorgegeben sind, können hier mehrere Attribute angegeben werden, die durch Kommas getrennt sind.
ldap-group-name-attribute: cn
${SPA}
#Diese Option steuert, ob das LDAP-Modul bei der Verarbeitung von Suchergebnissen aus einer LDAP-Suche Verweisen folgt oder nicht. Verweise können Zeiger auf andere Teile eines LDAP-Baums oder auf einen anderen Server / eine andere Verbindung insgesamt sein. Dies ist ein boolescher Parameter mit den gültigen Optionen "true" oder "false". Der Standardwert ist false. Wenn diese Option deaktiviert ist, werden LDAP-Verweise ignoriert, wenn der Guacamole LDAP-Client darauf stößt und der Client mit dem nächsten Ergebnis fortfährt. Wenn diese Option aktiviert ist, folgt der LDAP-Client den Verweis- und Verarbeitungsergebnissen innerhalb des Verweises, vorbehaltlich der unten angegebenen maximalen Hops-Parameter.
ldap-follow-referrals: false
${SPA}
# This option sets the timeout, in seconds, of any single LDAP operation. The default is 30 seconds. When this timeout is reached LDAP operations will be aborted.
ldap-operation-timeout: 30
${SPA}
#The search filter used to query the LDAP tree for users that can log into and be granted privileges in Guacamole. If this property is omitted the default of "(objectClass=*)" will be used.
ldap-user-search-filter: (&(objectClass=*)(memberOf=CN=ggpro-guacamole-logon-access,OU=lab03Gruppen,DC=lab03,DC=wydler,DC=eu))
${SPA}
EOF

Bitte nicht vergessen, die Werte für eure Umgebung anzupassen (z.B. das Passwort für den Service Benutzer).

Der Parameter ldap-user-search-filter ist so konfiguriert, dass ausschließlich Mitglieder der angegebenen Gruppe, sich an der Applikation anmelden dürfen.

Bei allen Parameter, die mit *base-dn enden, handelt es sich jeweils um distinguishedName der Organisationsheit, Benutzer oder Gruppe. Der Wert kann im Active Directory in den Eigenschaften des jeweiligen Objekts im Reiter „Attribut Editor“ aus dem Parameter distinguishedName kopiert werden. Der schnellere Weg ist über die PowerShell die Werte für die Objekte zulesen.

(Get-ADUser guacamoleadmin).DistinguishedName
> CN=guacamoleadmin,OU=lab03Serviceobjekte,DC=lab03,DC=wydler,DC=eu

(Get-ADgroup ggpro-guacamole-logon-access).DistinguishedName
> CN=ggpro-guacamole-logon-access,OU=lab03Gruppen,DC=lab03,DC=wydler,DC=eu

(Get-ADOrganizationalUnit -Filter  "name -eq 'lab03Benutzer' ").DistinguishedName
> OU=lab03Benutzer,DC=lab03,DC=wydler,DC=eu

Damit die Änderung wirksam wird, muss der Apache Tomcat neu gestartet werden.

systemctl restart tomcat9.service

Funktionstest

Zeit für einen Funktionstest der neuen Authentifizieurngsmethode. Dazu lege ich im Active Directory einen neuen Testbenutzer mit dem Namen „mueller“ an.

$password = "Test1234" | ConvertTo-SecureString -AsPlainText -Force
New-ADUser -Name "mueller" -Surname "Hans" -SamAccountName "mueller" -Path "OU=lab03Benutzer,DC=lab03,DC=wydler,DC=eu" -AccountPassword $password -CannotChangePassword $True -Enabled $True

Anschließend in einem Browser die Anmeldeseite von Apache Guacamole aufrufen und die Zugangsdaten des gerade angelegten Benutzer eingeben.

Die Anmeldung ist fehlgeschlagen. In diesen Fall ist dies korrekt, da der Benutzer nicht Mitglied der Gruppe „ggpro-guacamole-logon-access“ im Active Directory ist. Das holen wir nun kurz über einen Powershell Befehl nach.

Add-ADGroupMember -Identity "ggpro-guacamole-logon-access" -Members mueller

Nun wiedeholen wir nochmals die Anmeldung mit dem Testbenutzer.

Sieht doch ganz gut aus. Die Anmeldung funktioniert schon einmal problemlos. Rechts oben steht der Benutzername aus dem Active Directory. Aktuell sieht der Benutzer allerdings noch keine Verbindungen. Liegt das Benutzerkonto außerhalb des Pfades, welcher dem Attribut „ldap-user-base-dn“ zugewiesen ist, ist ebenfalls keine Anmeldung möglich.

LDAP auf LDAPS umstellen

Für die Abfrage bzw. Authentifizierung wird das Protokoll LDAP (Port 389/tcp) verwendet. Dabei handelt es sich standardmäßig um unverschlüsselten Verbindungen. Vor 10-20 Jahren hätte sich daran niemand gestört. Heutzutage ist die Verschlüsselung der Verbindungen immer vorzusehen, wenn dies technisch möglich ist. Dafür gibt es das Protokoll LDAPS (LDAP over SSL), welches für TLS-Verbindungen den Port 636/TCP benutzt.

Voraussetzungen

Vorraussetzung dafür ist, dass auf dem Domain Controller bereits LDAPS eingerichtet, konfiguriert und erfolgreich getestet ist. Ich möchte an dieser Stelle nicht näher auf die Konfiguration eingehen, da dies den Rahmen sprengen würde.

Zuerst muss das aktive Zertifikat auf dem Domain Controller exportiert werden. Grund dafür ist, dass Guacamole auf Java basiert. Java bringt traditionell einen eigenen Zertifikatsspeicher (keystore) mit. Das Zertfikat muss im Format BASE64 und ohne den privaten Schlüssel exportiert werden. In meinem Fall heißt die Datei dc01.lab03.wydler.eu.crt.

Konfigurationsanpssungen

Diese Datei mit Hilfe von WinSCP o.ä. Tools auf dem Guacamole Server unter /etc/guacamole/ ablegen. Alternativ die exportierte Datei in einem Editor öffnen und parallel dazu auf dem Guacamole Server mit Hilfe von vi /etc/guacamole/dc01.lab03.wydler.eu.crt eine neue Datei öffnen. Anschlißend per Copy & Paste den Inhalt 1:1 übertragen, speichern und die Datei wieder schließen.

Den Speicherort der Datei cacerts ausfindig machen.

find "/usr/lib/jvm/" -name "cacerts"
/usr/lib/jvm/java-11-openjdk-amd64/lib/security/cacerts

Das Zertifkat dem Keystore hinzufügen.

keytool -importcert -alias "ldaps" -keystore "/usr/lib/jvm/java-11-openjdk-amd64/lib/security/cacerts"  -storepass "changeit" -file /etc/guacamole/dc01.lab03.wydler.eu.crt -noprompt
Warnung: Verwenden Sie die Option -cacerts für den Zugriff auf den cacerts Keystore
Zertifikat wurde Keystore hinzugefügt

Die Warnung kann igoniert werden. Wir importieren in diesem Fall ein Zertifikat mit einem DNS-Namen. Möchte man noch die Zertifikate der (Zwischen) Zertifizierungsstellen importieren, ist die Option zu nutzen.

Damit Guacamole auch „Bescheid“ weiß, müssen die Parameter der Konfigurationsdatei (/etc/guacamole/guacamole.properties) angepasst werden.

#The port your LDAP server listens on. If omitted, the standard LDAP or LDAPS port will be used, depending on the encryption method specified with ldap-encryption-method (if any). Unencrypted LDAP uses the standard port of 389, while LDAPS uses port 636. Unless you manually configured your LDAP server to do otherwise, your LDAP server probably listens on port 389.
ldap-port: 636

#The encryption mechanism that Guacamole should use when communicating with your LDAP server. Legal values are "none" for unencrypted LDAP, "ssl" for LDAP over SSL/TLS (commonly known as LDAPS), or "starttls" for STARTTLS. If omitted, encryption will not be used.
ldap-encryption-method: ssl

Damit die Änderungen wirksam werden, muss Apache Tomcat neu gestartet werden.

systemctl restart tomcat9.service

Falls zwischen Guacamole Server und Domain Controller eine Firewall steht, nicht vergessen den Port 636/tcp freizugeben. Die Windows-Firewall auf dem Domain Controller muss nicht angepasst werden. Grund dafür ist, dass die notwendigen Regeln bereis vorhanden und aktiv sind.

Bei Problemem/Fehler beim Anmelden mit Zugangsdaten eines Benutzers aus dem Active Directory lohnt sich ein Blick ins Protokoll.

tail -f -n 50 /var/log/tomcat9/catalina.out

Zwei-Faktor-Authentifizierung

Nachdem die Einrichtung von Guacamole soweit abgeschlossen ist, macht man sich natürlich Gedanken über die Absicherung des Zugangs. Je nach Planung/Umgebung hat man nun Zugriff auf Server, Applikationen und Daten im LAN. Es ist inzwischen nicht mehr zeitgemäß solche Zugänge ausschließlich mit Benutzername und Passwort absichern. Oft verwenden die Nutzer das selbe Passwort für unterschiedliche Konten – unabhängig ob privat oder geschäftlich.

Abhilfe schafft in meinen Augen nur eine Implementierung einer Zwei-Faktor-Authentifizierung (2FA) in Form eines Einmalpassworts (One-Time-Passwort (OTP) ). Neben dem Benutzernamen und dem dazugehörigen Passwort muss auch das jeweilige OTP des Benutzer eingeben werden. Eine Anmeldung ist daher nur möglich, wenn alle drei Eingaben zusammen passen. Guacamole stellt dafür offizell die TOTP Erweiterung zur Verfügung.

Installation und Konfiguration

Installation der Extension.

wget --trust-server-names "https://apache.org/dyn/closer.cgi?action=download&filename=guacamole/$guacver/binary/guacamole-auth-totp-$guacver.tar.gz" -O /usr/src/guacamole-auth-totp-$guacver.tar.gz
tar xvzf /usr/src/guacamole-auth-totp-$guacver.tar.gz -C /usr/src/
cp /usr/src/guacamole-auth-totp-$guacver/guacamole-auth-totp-$guacver.jar /etc/guacamole/extensions/

Konfiguration ergänzen.

SPA="$(printf ' ')"
cat << EOF >> /etc/guacamole/guacamole.properties
#
# TOTP Settings
#
#The human-readable name of the entity issuing user accounts. If not specified, "Apache Guacamole" will be used by default.
totp-issuer: guacamole.lab03.wydler.eu
${SPA}
#The number of digits which should be included in each generated TOTP code. Legal values are 6, 7, or 8. By default, 6-digit codes are generated.
totp-digits: 6
${SPA}
#The duration that each generated code should remain valid, in seconds. By default, each code remains valid for 30 seconds.
totp-period: 30
${SPA}
#The hash algorithm that should be used to generate TOTP codes. Legal values are "sha1", "sha256", and "sha512". By default, "sha1" is used.
totp-mode: sha256
${SPA}
EOF

Bei dem Parameter „totp-issuer“ bei Möglichkeit auf Leerzeichen verzichten. Hat meinen Versuchen immer wieder Probleme verursacht.

Apache Tomcat neu gestarten.

systemctl restart tomcat9.service

TOTP für Benutzer einrichten

Nachdem die Erweiterung für Guacamole eingerichtet ist, erhalten alle Benutzer nach der erfolgreichen Anmeldung an der Weboberfläche folgenden Hinweis. Unabhängig davon ,ob es der Standardbenutzer guacadmin oder ein normaler Benutzer ist. Die 2FA ist nun für alle Benutzerkonten aktiv!

An dieser Stelle benötigt man nun eine App(likation) für das Smartphone/Tablet bzw. den PC, mit der die 2FA verwaltet werden kann. Die bekannste App ist wohl der Google Authenticator. Enthält ein QR Code mehrere Parameter, so erscheint nachdem Scannen eine Fehlermeldung. Die Erfahrung bezieht sich auf aktuelle Geräte von Apple mit iOS.

Auf der Suche nach Alternativen bin ich bie der App OTP Auth hängen gelieben. Unkomplizierte Bedienung und die OTPs sind schnell eingerichtet. Den angezeigten QR Code mit OTP Auth abscannen. Ist alles korrekt wird in der App fortlaufend (alle 30 Sekunden) ein neues OTP für das Konto angezeigt.

Das OTP für guacadmin in das Feld „Authentication Code“ eingeben. Passt dies zu dem anzumeldeten Benutzer, erscheint die Übersichtsseite von Guacamole.

TOTP für Benutzer zurücksetzen

In den meisten Umgebungen kommen Apps auf Smartphone oder Tablets zum Einsatz. Somit kann nicht ausgeschlossen werden, dass der Nutzer den Eintrag in der App oder sogar die App von einem Gerät gelöscht hat. In diesen Fällen muss für den betroffenen Benutzer der privaen Schlüssel (Secret Key) zurückgesetzt werden.

Leider ist dies nach wie vor nicht über die Weboberfläche von Guacamole möglich. Das bedeutet der Wert muss über die Kommandozeile des verwendeten Datenbank-Servers gelöscht werden. Nachstehend die Befehle für die unterstützten Datenbankmanagementsysteme.

MySQL/MariaDB

Aufbau der Verbindung zur Datenkbank.

dbuser=$(awk -F ": " '/mysql-username/ {print $2}' /etc/guacamole/guacamole.properties)
dbpw=$(awk -F ": " '/mysql-password/ {print $2}' /etc/guacamole/guacamole.properties)
dbname=$(awk -F ": " '/mysql-database/ {print $2}' /etc/guacamole/guacamole.properties)

mysql -u $dbuser -p$dbpw $dbname

Kontrolle ob es dir korrekte Datenbank ist.

SHOW TABLES;
MariaDB [guacamole]> SHOW TABLES;
+---------------------------------------+
| Tables_in_guacamole                   |
+---------------------------------------+
| guacamole_connection                  |
| guacamole_connection_attribute        |
| guacamole_connection_group            |
| guacamole_connection_group_attribute  |
| guacamole_connection_group_permission |
| guacamole_connection_history          |
| guacamole_connection_parameter        |
| guacamole_connection_permission       |
| guacamole_entity                      |
| guacamole_sharing_profile             |
| guacamole_sharing_profile_attribute   |
| guacamole_sharing_profile_parameter   |
| guacamole_sharing_profile_permission  |
| guacamole_system_permission           |
| guacamole_user                        |
| guacamole_user_attribute              |
| guacamole_user_group                  |
| guacamole_user_group_attribute        |
| guacamole_user_group_member           |
| guacamole_user_group_permission       |
| guacamole_user_history                |
| guacamole_user_password_history       |
| guacamole_user_permission             |
+---------------------------------------+
23 rows in set (0.00 sec)

Die folgende SQL-Abfrage liefert die ID des angegebenen Benutzernamen zurück. Dazu den Wert „guacadmin“ durch den betroffenen Benutzernamen ersetzen.

MariaDB [guacamole]> SELECT user_id FROM guacamole_user INNER JOIN guacamole_entity ON guacamole_entity.entity_id = guacamole_user.entity_id WHERE guacamole_entity.name = 'guacadmin';
+---------+
| user_id |
+---------+
|       1 |
+---------+
1 row in set (0.00 sec)

TOTP für den Benutzer zurücksetzen. Dazu die ID vom vorherigen Schritte als Wert für die Spalte „user_id“ eintragen.

MariaDB [guacamole]> UPDATE guacamole_user_attribute SET attribute_value='false' WHERE attribute_name = 'guac-totp-key-confirmed' and user_id = '1';
Query OK, 1 row affected (0.005 sec)
Rows matched: 1 Changed: 1 Warnings: 0

Verbindung zur Datenbank beenden.

MariaDB [guacamole]> quit
Bye

Meldet sich der Benutzer erneut bei Guacamole an, wird erneut den Assistent für das Einrichten der 2FA angezeigt.

PostgreSQL

Aufbau der Verbindung zur Datenkbank.

psql dbname dbuser
psql (12.2 (Debian 12.2-2.pgdg100+1))
Type "help" for help.

Kontrolle ob es dir korrekte Datenbank ist.

guacamole=# \dt
                        List of relations
 Schema |                 Name                  | Type  | Owner
--------+---------------------------------------+-------+--------
 public | guacamole_connection                  | table | dbuser
 public | guacamole_connection_attribute        | table | dbuser
 public | guacamole_connection_group            | table | dbuser
 public | guacamole_connection_group_attribute  | table | dbuser
 public | guacamole_connection_group_permission | table | dbuser
 public | guacamole_connection_history          | table | dbuser
 public | guacamole_connection_parameter        | table | dbuser
 public | guacamole_connection_permission       | table | dbuser
 public | guacamole_entity                      | table | dbuser
 public | guacamole_sharing_profile             | table | dbuser
 public | guacamole_sharing_profile_attribute   | table | dbuser
 public | guacamole_sharing_profile_parameter   | table | dbuser
 public | guacamole_sharing_profile_permission  | table | dbuser
 public | guacamole_system_permission           | table | dbuser
 public | guacamole_user                        | table | dbuser
 public | guacamole_user_attribute              | table | dbuser
 public | guacamole_user_group                  | table | dbuser
 public | guacamole_user_group_attribute        | table | dbuser
 public | guacamole_user_group_member           | table | dbuser
 public | guacamole_user_group_permission       | table | dbuser
 public | guacamole_user_history                | table | dbuser
 public | guacamole_user_password_history       | table | dbuser
 public | guacamole_user_permission             | table | dbuser
(23 rows)

Die folgende SQL-Abfrage liefert die ID des angegebenen Benutzernamen zurück. Dazu Wert „guacadmin“ durch den betrofffenen Benutzernamen ersetzen.

guacamole=# \gexec
SELECT user_id FROM 
    guacamole_user
INNER JOIN 
    guacamole_entity ON guacamole_entity.entity_id = guacamole_user.entity_i
WHERE
guacamole_entity.name = 'guacadmin';
 user_id
---------
       1
(1 row)

TOTP für den Benutzer zurücksetzen. Dazu die ID vom vorherigen Schritte als Wert für die Spalte „user_id“ eintragen.

guacamole=# UPDATE guacamole_user_attribute SET attribute_value='false' WHERE attribute_name = 'guac-totp-key-confirmed' and user_id = '1';
UPDATE 1

Verbindung zur Datenbank beenden.

\q

Meldet sich der Benutzer erneut bei Guacamole an, erhält dieser wie gewohnt die Daten für die Einrichtung der 2FA.

FAQ/Häufige Fragen

Frage: Ich möchte die 2FA nämlich nicht für alle User. Gibt es vielleicht eine Möglichkeit zum hier gezeigten Verhalten zu kommen?
Antwort: Mit Version 1.2.0 haben die Entwickler das Verhalten der 2FA verändert. Es ist nicht mehr möglich Ausnahmen zu definieren. Das hat einer der Entwickler auch in einem Ticket bestätigt. Siehe Allow database storage of arbitrary attributes for non-database users  und Disable TOTP for specific users (Example: If the user is authenticated via DUO.

Der Artikel wird nach und nach erweitert und Neuerungen von zukünftigen Versionen von Guacamole angepasst. Viel Spaß beim Ausprobieren. 🙂

Abonnieren
Benachrichtige mich bei
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x
Back to top