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.

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.

Last updated