Let's Encrypt and NGINX

Tech Guacamole Ubuntu

We’re going to be setting up NGINX to use LET’S ENCRYPT and for this example we’re going to be pointing to a Guacamole server, which requires zero buffering of packets as it is using real time RDP data. So the config will take that into account. Additionally, we’re shooting for the highest rating possible on SSL Labs and only supporting the latest encryption standards!

UPDATE: I’ve gotten two reports that the cron job is failing so I changed it to what is recommended by Juan in the comments below

We’re going to start with 2 assumptions:
#1. You have configured your external DNS to point to this server…
#2. You’re port forwarding ports 80 and 443 to this server…

Let’s set a few variables for our install

EXTERNALFQDN=guac.domain.com
INTERNALFQDN=guac01.domain.local

EXTERNALFQDN is the external fully qualified domains name we’re passing traffic for
INTERNALFQDN is the internal host name of your Guacamole server

Install NGINX and Let’s Encrypt:

apt-get -y install nginx letsencrypt openssl

Make a directory to store your certs

mkdir -p /etc/nginx/ssl/$EXTERNALFQDN

Create a 4096 bit Diffie-Hellman Key (Go big or go home!)

openssl dhparam -out /etc/nginx/ssl/$EXTERNALFQDN/dhparam.pem 4096

Now let’s configure NGINX. Careful of the formatting here. ssl_ciphers should be one line if you copy and paste

cat > /etc/nginx/nginx.conf <<- EOM
user www-data;
worker_processes 4;
pid /run/nginx.pid;

events
{
        worker_connections 768;
}

http
{
        # My Certificates
        #ssl_certificate /etc/nginx/ssl/$EXTERNALFQDN/fullchain.pem;
        #ssl_certificate_key /etc/nginx/ssl/$EXTERNALFQDN/privkey.pem;

        # SSL Performance Related
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;

        # SSL Protocols and Ciphers
        ssl_prefer_server_ciphers on;
        ssl_protocols TLSv1.2;
        ssl_ciphers "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:!AES128:!aNULL:!MD5:!eNULL:!EXPORT:!DES:!PSK:!RC4";
        # DHE Key-Exchange
        ssl_dhparam /etc/nginx/ssl/$EXTERNALFQDN/dhparam.pem;

        # Random Security Stuff
        server_tokens off;
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        add_header Strict-Transport-Security max-age=63072000;

        # Common Proxy Settings
        proxy_set_header Host      $host;
        proxy_set_header X-Real-IP  $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;

        ########################
        # Default Config Stuff #
        ########################
        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 4096; #Default:2048
        include /etc/nginx/mime.types;
        default_type application/octet-stream;
        gzip on;
        gzip_disable "msie6";
        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;

        # REDIRECTS ALL PORT 80/HTTP to 443/HTTPS
        server
        {
                listen 80;
                listen [::]:80;
                server_name $EXTERNALFQDN;
                
                location ~ /.well-known/acme-challenge
                {
                    root /var/www/html/;
                }
                
                #return 301 https://$host$request_uri;
        }

        # GUACAMOLE SERVER SETTINGS
        server
        {
                listen 443 ssl;
                listen [::]:443 ssl;
                server_name $EXTERNALFQDN;

                proxy_buffering off;
                proxy_redirect  off;
                proxy_cookie_path /guacamole/ /;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                
                location ~ /.well-known/acme-challenge
                {
                    root /var/www/html/;
                }
                
                location /
                {
                        proxy_pass http://$INTERNALFQDN:8080/guacamole/;
                }
        }
}
EOM

Restart NGINX

service nginx restart

Request a cert from Let’s Encrypt

letsencrypt certonly -a webroot --webroot-path=/var/www/html -d $EXTERNALFQDN --rsa-key-size 4096

Link the Let’s Encrypt public and private keys to the NGINX Config

ln -s /etc/letsencrypt/live/$EXTERNALFQDN/fullchain.pem /etc/nginx/ssl/$EXTERNALFQDN/fullchain.pem
ln -s /etc/letsencrypt/live/$EXTERNALFQDN/privkey.pem /etc/nginx/ssl/$EXTERNALFQDN/privkey.pem

Update the NGINX config to use those certs and enforce 443 with redirection

sed -i 's/#ssl_certificate/ssl_certificate/g' /etc/nginx/nginx.conf
sed -i 's/#return 301/return 301/' /etc/nginx/nginx.conf

Restart NGINX

service nginx restart

Now we need to setup a cron job that runs every Sunday to check for certs that are near expiration and renew them automatically!

(crontab -l 2>/dev/null; echo '@weekly (certbot renew && service nginx restart) 2>&1 >/dev/null') | crontab -
← All posts