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:

  • proxy_buffering off disables buffering of packets sent to/from Guacamole. By default, Nginx 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.
  • The X-Forwarded-For header must be explicitly set to ensure that the IP addresses logged by Guacamole are correct. Without explicitly adding this header (and configuring Tomcat to trust this header), all connections will appear to come from the Nginx server.
  • The Upgrade and Connection headers are required parts of the WebSocket protocol. If omitted here, WebSocket will not function correctly, and Guacamole will fall back to HTTP streaming, which is less efficient.

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 Nginx configuration

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

$ sudo systemctl reload nginx

If you are using SELinux (the default on both CentOS and RHEL), you must also configure SELinux to allow HTTPD implementations like Nginx to establish network connections:

$ sudo setsebool -P httpd_can_network_connect 1

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. If you do not see any errors from Nginx, verify that you have configured SELinux to allow Nginx to connect to the network and check the SELinux audit logs (/var/log/audit/audit.log) for AVC denials.

Configuring Tomcat to trust "X-Forwarded-For" from Nginx

For the client address sent by Nginx via "X-Forwarded-For" to be correctly trusted as the true client address, you will need to add a "RemoteIpValve" entry within /etc/tomcat/server.xml. If this is not specified, the client address will be logged as the address of the internal proxy, which is not usually desirable.

The easiest way to add the required entry is to copy the example server.xml file provided with the glyptodon-guacamole package, replacing the old /etc/tomcat/server.xml:

$ sudo cp /usr/share/guacamole/server.xml /etc/tomcat/

The example server.xml file defines:

  • A single HTTP connector listening on port 8080.
  • RemoteIpValve with all settings at their default values.

By default, the RemoteIpValve will trust "X-Forwarded-For" from all private networks (10.0.0.0/8172.16.0.0/12192.168.0.0/16169.254.0.0/16, and both IPv4 and IPv6 localhost). If you need this range to be narrowed, or if you have already made manual edits to server.xml, you will need to make these changes manually.

If editing server.xml manually (rather than using the example server.xml), a <Valve> which trusts "X-Forwarded-For" from most common private addresses would be specified as:

<Valve className="org.apache.catalina.valves.RemoteIpValve"/>

This <Valve> must be added within the relevant <Host> section. In most cases, the easiest place to add this is simply toward the end of the server.xml file:

        ...

        <Valve className="org.apache.catalina.valves.RemoteIpValve"/>

      </Host>
    </Engine>
  </Service>
</Server>

If needed, this can be narrowed by providing your own value for the internalProxies attribute specifies a regular expression which matches the IP addresses of any proxies whose "X-Forwarded-For" headers should be trusted. For example, to trust only "X-Forwarded-For" received from localhost:

<Valve className="org.apache.catalina.valves.RemoteIpValve"
       internalProxies="127\.\d{1,3}\.\d{1,3}\.\d{1,3}|0:0:0:0:0:0:0:1|::1"/>

Applying the updated Tomcat configuration

Once an appropriate RemoteIpValve has been specified, Tomcat must be restarted to force rereading of server.xml:

$ sudo systemctl restart tomcat