All pages
Powered by GitBook
1 of 3

SSL Termination with NGINX

How to manually configure Keeper Connection Manager SSL termination using NGINX

This documentation assumes that you already have an instance of NGINX properly configured for SSL/TLS, including the necessary private key and certificate, and that Guacamole has already been installed using Keeper Connection Manager. If you do not already have an instance of NGINX ready, please set up an instance of Nginx before proceeding. If you do not already have Guacamole installed, please see the installation instructions.

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.

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 / {
    proxy_pass http://HOSTNAME:8080;
    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_set_header Host $host;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    access_log off;
    client_max_body_size 200m;
}

where “HOSTNAME” is the hostname or IP address of the internal Guacamole server. This can also be replaced with "http://127.0.0.1:8080" in most cases.

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, WebSocket will not function correctly, and Guacamole will fall back to HTTP streaming, which is less efficient.

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 (manual deployment only)

This section applies only if you have manually deployed Guacamole under your own version of Tomcat. This will usually only be the case if:

  • You have chosen to manually deploy Guacamole under your own install of Apache Tomcat or JBoss, rather than use the provided version of Tomcat.

  • You are maintaining a deployment of Glyptodon Enterprise that was originally installed before the 2.5 release (2021-09-16).

If you deployed Guacamole automatically (the default and recommended method), this has already been configured for you within the bundled version of Tomcat.

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 kcm package, replacing the old /etc/tomcat/server.xml:

$ sudo cp /opt/keeper/share/guacamole/server.xml /etc/tomcat/

The example server.xml file defines:

  • A single HTTP connector listening on port 8080.

  • A 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/8, 172.16.0.0/12, 192.168.0.0/16, 169.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

Installing and Configuring Nginx for SSL Termination

Detailed configuration instructions for SSL Termination with Nginx

Nginx is not directly available within the CentOS or RHEL repositories, but is available within the EPEL repository. The EPEL repository must be enabled first before Nginx can be installed:

$ sudo yum install epel-release

Once EPEL has been enabled, Nginx can be installed by installing the "nginx" package. Installing this package will install a version of Nginx that is newer than version 1.3 and thus is explicitly supported by Keeper Connection Manager:

$ sudo yum install nginx

As with other standard CentOS / RHEL packages providing a service, the Nginx service will not be started by default after the "nginx" package is installed. It must be started manually, and then configured to automatically start if the system is rebooted:

$ sudo systemctl start nginx
$ sudo systemctl enable nginx

By default, Nginx will listen on port 80 and handle only unencrypted HTTP connections, serving the static contents of /usr/share/nginx/html. The rest of this document will cover reconfiguring Nginx such that it serves only HTTPS (using HTTP only to redirect browsers back to HTTPS), with a placeholder for the minimal additional configuration needed to provide SSL termination. The resulting configuration will be used instead of Nginx' default configuration, and will be split up across two modular files:

Filename
Description

/etc/nginx/conf.d/redirect-http.conf

Configures Nginx to respond to all HTTP requests with an HTTP 301 ("Moved Permanently") redirect to the same resource under HTTPS.

/etc/nginx/conf.d/guacamole.conf

Configures Nginx to accept HTTPS connections using a specified certificate and private key. Once SSL configuration is complete, this file will also configure Nginx to proxy HTTPS connections to Guacamole such that HTTP is used only internally (SSL termination).

Configure Nginx to redirect all HTTP traffic to HTTPS

The main configuration file for Nginx, /etc/nginx/nginx.conf, contains a server block which serves the contents of /usr/share/nginx/html over HTTP:

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }

The contents of this server block will conflict with the HTTP redirect that will need to be created, and should be commented-out or removed. Comment out every line with a # at the beginning:

#    server {
#        listen       80 default_server;
#        listen       [::]:80 default_server;
#        server_name  _;
#        root         /usr/share/nginx/html;
#
#        # Load configuration files for the default server block.
#        include /etc/nginx/default.d/*.conf;
#
#        location / {
#        }
#
#        error_page 404 /404.html;
#        location = /404.html {
#        }
#
#        error_page 500 502 503 504 /50x.html;
#        location = /50x.html {
#        }
#    }

With the conflicting server block removed, create a new file, /etc/nginx/conf.d/redirect-http.conf, to contain the new server block and redirect. The server block required is fairly straightforward. Rather than serve static content, it instructs Nginx to issue an HTTP 301 redirect ("Moved Permanently") for all requests, informing the browser that whatever they are requesting can instead be found at the same location using HTTPS:

server {

    listen 80;

    # Redirect all other traffic to HTTPS
    location / {
        return 301 https://$host$request_uri;
    }

}

To apply the new configuration, simply reload Nginx:

$ sudo systemctl reload nginx

Configure Nginx to accept HTTPS connections

To configure Nginx to accept HTTPS connections, you must obtain an SSL certificate for the domain associated with your server. There are two primary ways of doing this:

  1. Let's Encrypt: A non-profit certificate authority that provides automated certificate issuance and renewal. Let's Encrypt certificates are free.

  2. Obtaining a certificate from a certificate authority: Several commercial certificate authorities exist, many of which are also domain registrars. Unlike Let's Encrypt, obtaining a certificate from a commercial certificate authority will usually cost money.

The Let's Encrypt service uses a utility called "certbot" to automatically retrieve a certificate after the Let's Encrypt service has remotely verified that you control your domain. This utility is provided within the CentOS / RHEL repositories and must first be installed:

$ sudo yum install certbot

The Let's Encrypt service will verify that you control your domain be reaching back over the internet, attempting to establish an HTTP connection with your server at the domain provided and reading the contents of the .well-known/ directory within the web root. This directory will be created and populated automatically by certbot when it runs, but the /etc/nginx/conf.d/redirect-http.conf file created earlier will not allow this directory to be read due to the nature of the HTTPS redirect. The server block within redirect-http.conf must first be edited to add a location which functions as an exception to this redirect, allowing Let's Encrypt to remotely access .well-known/:

server {

    listen 80;

    # Let's Encrypt requires access to /.well-known/ for its webroot plugin
    location /.well-known/ {
        root /usr/share/nginx/html;
        break;
    }

    # Redirect all other traffic to HTTPS
    location / {
        return 301 https://$host$request_uri;
    }

}

To apply the new configuration, reload Nginx:

$ sudo systemctl reload nginx

The certbot tool can then be used to automatically retrieve a certificate for your domain. For example, if your Guacamole server is running at "remote.example.com", you can obtain a certificate from Let's Encrypt by running:

$ sudo certbot certonly -d remote.example.com --webroot --webroot-path /usr/share/nginx/html

The resulting certificate and private key will then be stored within a subdirectory of /etc/letsencrypt/live/ with the same name as your domain. To apply the certificate and additionally host content via HTTPS, a new configuration file needs to be created: /etc/nginx/conf.d/guacamole.conf. Similar to the HTTP redirect, this file will contain a server block that defines how Nginx should serve HTTPS connections:

server {

    listen 443 ssl;

    # Location block pointing to Tomcat will go here

    ssl_certificate /etc/letsencrypt/live/remote.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/remote.example.com/privkey.pem;

}

Configuring automatic renewal

Let's Encrypt certificates are intentionally short-lived, expiring after 90 days. To ensure that a new certificate is retrieved, it is recommended that certbot run daily. This can be achieved by creating a script within /etc/cron.daily called /etc/cron.daily/certbot containing the following:

#!/bin/bash

# Attempt to renew certificates daily, reloading Nginx if an updated
# certificate is issued
certbot renew --quiet && systemctl reload nginx

Be sure to grant execute permission on the script so that the cron service will be able to execute it:

$ sudo chmod +x /etc/cron.daily/certbot

The certbot tool will then be automatically invoked at least once per day, renewing the certificate and reloading Nginx as needed.

If you have already obtained a certificate and corresponding private key from a certificate authority, there is very little extra configuration needed for Nginx to start using that certificate. To apply the certificate and additionally host content via HTTPS, a new configuration file needs to be created: /etc/nginx/conf.d/guacamole.conf. Similar to the HTTP redirect, this file will contain a server block that defines how Nginx should serve HTTPS connections:

server {

    listen 443 ssl;

    # Location block pointing to Tomcat will go here

    ssl_certificate /etc/pki/tls/certs/remote-example-com-cert.pem;
    ssl_certificate_key /etc/pki/tls/private/remote-example-com-key.pem;

}

where /etc/pki/tls/certs/remote-example-com-cert.pem is your certificate and /etc/pki/tls/private/remote-example-com-key.pem is your private key. Your certificate authority may have additional instructions regarding how their certificates should be used with Nginx, but the above configuration should work well for most cases. Be sure to check the documentation provided by your certificate authority to see whether they have specific recommendations.

The /etc/pki/tls/ directory structure is standard to CentOS and RHEL. It is best practice to leverage this directory structure as intended, with the certs/ directory containing the non-sensitive certificate and the private/ directory containing the sensitive private key. The permissions of your private key should also be set such that non-root users of your system will not be able to read the key.

Finalizing the configuration (pointing Nginx at Guacamole)

With Nginx now deployed with a proper SSL certificate, the final step in providing SSL termination for Guacamole is to configure Nginx as a reverse proxy for Guacamole. This process involves adding a location block within the Nginx server block for HTTPS, in this case the /etc/nginx/conf.d/guacamole.conf configuration file that was just created. Documentation is provided for the full content and meaning of the required location block, which should be added in the space noted by the "Location block pointing to Tomcat will go here" placeholder comments above.

Client Certificate Configuration

Optional NGINX Client Certificate configuration for advanced protection

Client Certificate Overview

To implement device-based access security with Keeper Connection Manager, this can be accomplished using NGINX client certificates. A client certificate is installed into the web browser of your user's approved devices, and the server will only accept communication from a device with the client certificate installed.

The steps to activate this advanced level of protection is described in the steps below.

(1) Create a Certificate Authority (CA) Key

Generate a CA Key with a strong auto-generated passphrase. Make sure to store the passphrase in your Keeper vault.

openssl genrsa -des3 -out ca.key 4096

(2) Create a CA Certificate

A certificate is created with the CA Key. When answering the questions, you can leave the Common Name and Email empty. Save the information that you entered for Country, State, Locality, and Organization, because you may need these later when renewing the certificate.

openssl req -new -x509 -days 365 -key ca.key -out ca.crt

Side Note: to analyze the certificate parameters, you can run the below command.

openssl x509 -in ca.crt -noout -text

(3) Create a Client Key

For the end-user devices, a client key must be generated. You can decide if you would like to generate one key for all devices, or each user can generate their own key and request a certificate. The process is up to you. Generate a client key with a strong auto-generated passphrase. Make sure to store the passphrase in your Keeper vault.

openssl genrsa -des3 -out client.key 4096

(4) Create a CSR

For each Client Key, generate a CSR to create a signed certificate.

openssl req -new -key client.key -out client.csr

(5) Sign the CSR with the CA Key

openssl x509 -req -days 365 -in client.csr \
 -CA ca.crt -CAkey ca.key \
 -set_serial 01 -out client.crt

You'll need to enter the CA passphrase from Step 1 to sign the request.

At this point, you now have a signed Client Certificate (client.crt).

(6) Convert the Client Certificate to PKCS#12

To import the certificate into a web browser, a pfx file in PKCS#12 is typically required. Generate the client.pfx file using the command below. A passphrase will be required. This passphrase will be provided to each of the users who need to install the certificate, so it should be used specifically for this purpose.

openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.pfx

(7) Add to NGINX Config

Add the below line to your Keeper Connection Manager NGINX configuration file to block access without a certificate. Make sure to upload the CA certificate to the folder path designated.

ssl_client_certificate /etc/nginx/client_certs/ca.crt;
ssl_verify_client optional;

In the Location block, add this section to send the user a 403 error if the client cert is not installed:

        if ($ssl_client_verify != SUCCESS) {
                return 403;
        }

Make sure that the ca.crt file is located in a folder that NGINX can access.

After updating the configuration, restart NGINX.

sudo systemctl restart nginx

(8) Test the configuration

Before installing the client certificate on the user's machine, load up the Keeper Connection Manager login screen to ensure that a 403 error is sent:

403 Error without Client Certificate

(9) Install the Client Certificate

For each end-user client device that will need access to Keeper Connection Manager, you will need to install the client certificate into the user's browser or machine. The installation of client certificates varies by platform.

On Windows

Double-click or right-click the client certificate (client.pfx) from Step 6 and enter the client certificate passphrase.

Install Windows Client Certificate

Restart the browser.

The next time Keeper Connection Manager is loaded, you can approve the certificate.

Select Client Certificate

On Mac OS - Chrome

Import the client.pfx file by double-clicking or loading into the Keychain login Certificates section. In the "Trust" section of the certificate, mark as Always Trust.

Import Certificate
Mark as Trusted

Restart the browser and load the Keeper Connection Manager login screen to select the certificate.

Select Client Certificate

On Mac OS - Firefox

Open Firefox > Preferences > search for Certificates and select Your Certificates tab. Click "Import" and select the client.pfx certificate file. Complete the import.

Import Certificate on Firefox

After successful import, the Keeper Connection Manager login screen will load.

Troubleshooting

After setting up the client certificate configuration, the following errors are common.

If NGINX fails to start, journalctl -xe might show something like "Permission denied:fopen('/path/to/ca.crt','r'). This might occur if the folder permissions and file permissions are not set properly.

If the folder and file permissions are set properly and you're still receiving this error, the restorecon command will repair the SELinux security context for the file:

restorecon ca.crt