Using Duo for Multi-factor Authentication

Guacamole provides support for Duo as a second authentication factor, automatically verifying user identity with Duo after the user is initially authenticated. To make use of Duo support, some other authentication mechanism will need be configured, as well, such as MySQL, PostgreSQL, or LDAP. Only once authentication has succeeded through another installed method will Duo be used to verify the identity of the user.

Installing Duo support for Guacamole

Glyptodon Enterprise packages Guacamole’s Duo support within the glyptodon-guacamole-auth-duo package:

$ sudo yum install glyptodon-guacamole-auth-duo

The Guacamole-side installation of Duo support within Glyptodon Enterprise consists solely of the glyptodon-guacamole-auth-duo package. Nothing else needs to be installed except for Guacamole itself and some other means of authentication. If Guacamole has not yet been installed and confirmed to work with some other authentication method, that should be done first before attempting to set up Duo.

Registering Guacamole with Duo

For Duo to be integrated with any application, that specific instance of the application must first be registered with the Duo service. Duo does not provide a specific integration option for Guacamole, but Guacamole’s Duo support uses Duo’s generic authentication API which they refer to as the “Web SDK”. To use your Guacamole deployment with Duo, you will need to add it to your Duo account as a new “Web SDK” application from within the “Applications” tab of the admin panel.

Once this has been done, Duo will expose several properties specific to your Guacamole deployment: the integration key, secret key, and API hostname. These values can be found within the application’s “Details” section in your Duo account, and will need to be copied into /etc/guacamole/guacamole.properties:

$ sudo vi /etc/guacamole/guacamole.properties

The relevant properties can be found in the “DUO-1” section:

##
## [DUO-1] Duo application integration details
##
## The API hostname, integration key, and secret key provided for you by Duo
## when you registered Guacamole in Duo's "Admin" panel. Each of these values
## is required and is generated by Duo.
##

#duo-api-hostname:    XXXXXXXX.duosecurity.com
#duo-integration-key: 0123456789ABCDEF0123
#duo-secret-key:      0123456789ABCDEF0123

Generating the application key

The Duo “Web SDK” requires that an arbitrary and random key be generated for each application. This key resides strictly on the side of the application, and is not registered with Duo.

Any random value containing at least 40 characters will suffice. To quickly grab 40 random characters from /dev/random:

$ tr -dc 'a-zA-Z0-9' < /dev/random | head -c40; echo
xqZKJODwg7ouwxdqU9hvuaWhE6lQFspijY0ofg8I
$

This value must then be copied within the duo-application-key property, which can be found in the "DUO-2" section of guacamole.properties:

##
## [DUO-2] Duo application key
##
## An arbitrary and random key to use when communicating with the Duo service.
## This key MUST be manually generated, and MUST BE AT LEAST 40 CHARACTERS.
##

#duo-application-key: abcdefghijklmnopqrstuvwxyz0123456789ABCD

Completing installation

Guacamole will generally only load new extensions and reread guacamole.properties during the startup process. To apply the configuration changes, Guacamole (and thus Tomcat) must be restarted:

$ sudo systemctl restart tomcat

Setting up SSL termination with Apache

SSL termination is the recommended method of encrypting communication between users’ browsers and Guacamole, and involves configuring a reverse proxy like Nginx or Apache to handle strictly the SSL/TLS portion of the conversation with the Tomcat instance hosting Guacamole, handling encrypted HTTP externally while passing unencrypted HTTP to Tomcat internally.

This documentation deals with configuring Apache to handle SSL/TLS termination for Guacamole, and assumes that you already have an Apache server properly configured for SSL/TLS, including the necessary private key and certificate. If you have not already done so, be sure to set up both Apache and Guacamole, confirming that each works properly independently before proceeding.

Proxying Guacamole through Apache

If Apache has been configured for SSL/TLS, there should be a <VirtualHost> section within this configuration that defines the certificate and private key used by Apache, and which requires Apache to listen on the standard HTTPS port (443). To proxy Guacamole through Apache such that Guacamole communication is encrypted, two additional <Location> sections will need to be added within this <VirtualHost> section:

<Location /guacamole/>
    Order allow,deny
    Allow from all
    ProxyPass http://HOSTNAME:8080/guacamole/ flushpackets=on
    ProxyPassReverse http://HOSTNAME:8080/guacamole/
</Location>

<Location /guacamole/websocket-tunnel>
    Order allow,deny
    Allow from all
    ProxyPass ws://HOSTNAME:8080/guacamole/websocket-tunnel
    ProxyPassReverse ws://HOSTNAME:8080/guacamole/websocket-tunnel
</Location>

where “HOSTNAME” is the hostname or IP address of the internal Guacamole server.

These <Location> sections configure proxying of the HTTP and WebSocket protocols respectively. Apache handles the HTTP and WebSocket protocols separately, and thus requires separate configuration for the portion of the web application which uses WebSocket. Both sections are required for Guacamole to work correctly behind Apache, and the mod_proxy_wstunnel module must be installed and enabled.

Of particular importance is the flushpackets=on option within the ProxyPass directive used for HTTP in front of Guacamole. This option disables buffering of packets sent to/from Guacamole. By default, Apache will buffer communication between itself and the browser, effectively disrupting the stream of events and updates required for remote desktop. Without disabling buffering, the Guacamole connection will at best be slow, and at worst not function at all.

With minor changes to each <Location> section, Apache can also be leveraged to change the path used for Guacamole. Doing so requires only changing the path specified for each <Location> section header, and adding the ProxyPassReverseCookiePath directive to rewrite the paths of any cookies set by the web application:

<Location /new-path/>
    Order allow,deny
    Allow from all
    ProxyPass http://HOSTNAME:8080/guacamole/ flushpackets=on
    ProxyPassReverse http://HOSTNAME:8080/guacamole/
    ProxyPassReverseCookiePath /guacamole/ /new-path/
</Location>

<Location /new-path/websocket-tunnel>
    Order allow,deny
    Allow from all
    ProxyPass ws://HOSTNAME:8080/guacamole/websocket-tunnel
    ProxyPassReverse ws://HOSTNAME:8080/guacamole/websocket-tunnel
    ProxyPassReverseCookiePath /guacamole/ /new-path/
</Location>

Applying the updated configuration

After the above changes have been made, Apache must be reloaded to force rereading of its configuration files:

$ sudo systemctl reload httpd

If Guacamole is not accessible through Apache after the service has been reloaded, check the Apache logs and/or journalctl to verify that the syntax of your configuration changes is correct. Such errors will result in Apache refusing to reload its configuration, or refusing to start up entirely.

Setting up SSL termination with Nginx

SSL termination is the recommended method of encrypting communication between users’ browsers and Guacamole, and involves configuring a reverse proxy like Nginx or Apache to handle strictly the SSL/TLS portion of the conversation with the Tomcat instance hosting Guacamole, handling encrypted HTTP externally while passing unencrypted HTTP to Tomcat internally.

This documentation deals with configuring Nginx to handle SSL/TLS termination for Guacamole, and assumes that you already have an Nginx server properly configured for SSL/TLS, including the necessary private key and certificate. If you have not already done so, be sure to set up both Nginx and Guacamole, confirming that each works properly independently before proceeding.

Proxying Guacamole through Nginx

If Nginx has been configured for SSL/TLS, there should be a server section within this configuration that defines the certificate and private key used by Nginx, and which requires Nginx to listen on the standard HTTPS port (443). To proxy Guacamole through Nginx such that Guacamole communication is encrypted, a new location section will need to be added within this server section:

location /guacamole/ {
    proxy_pass http://HOSTNAME:8080/guacamole/;
    proxy_buffering off;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
    access_log off;
}

where “HOSTNAME” is the hostname or IP address of the internal Guacamole server.

While a typical proxy configuration for Nginx may only specify the proxy_pass and proxy_http_version directives, Guacamole requires additional configuration due to the nature of the application:

With minor changes to the location section, Nginx can also be leveraged to change the path used for Guacamole. Doing so requires only changing the path specified for the location section header, and adding the proxy_cookie_path directive to rewrite the paths of any cookies set by the web application:

location /new-path/ {
    proxy_pass http://HOSTNAME:8080/guacamole/;
    proxy_buffering off;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
    proxy_cookie_path /guacamole/ /new-path/;
    access_log off;
}

Applying the updated configuration

After the above changes have been made, Nginx must be reloaded to force rereading of its configuration files:

$ sudo systemctl reload nginx

If Guacamole is not accessible through Nginx after the service has been reloaded, check the Nginx logs and/or journalctl to verify that the syntax of your configuration changes is correct. Such errors will result in Nginx refusing to reload its configuration, or refusing to start up entirely.