How to use Let's Encrypt certificates to secure Nginx’s SSL configuration
Estimated time to read: 6 minutes
This tutorial will guide you through setting up a nginx web server secured by a free certificate from letsencrypt.
Prerequisites
- a running instance
- if you don’t already have one, you can follow the first step using cloud-init below.
- You’ll also need to assign a floating IP to your instance. And making sure that the desired domain name points to the floating IP.
Step 1: Installing packages
Using cloud-init
If you don't have an instance running yet, go to the Fuga dashboard select compute, then instance and click on Create Instance. Select below Boot Source, a distribution, in this tutorial Ubuntu 20.04 LST is being used.
Select a Boot disk size, flavor, network, key pair, security group, and give your instance a name.
Unfold the Advanced Settings and deploy the script below:
Without using cloud-init
If you already have an instance running that you want to use, you can install the packages by running the following command:
Step 2: Configuring nginx
The first part of this is creating the directory for your website. For example:
Configuring nginx is pretty simple as the syntax is easy to understand. To start, remove the default configuration files. You can also choose to keep these files and use it as a default virtual host to keep a placeholder page.
We recommend keeping every site in its own configuration file, so now we'll create a new file. Try to keep the filename in line with your website's main domain name:
Place the following configuration in your websites' configuration file. Change all occurrences of example.com with your domain name:
server {
listen 80;
listen [::]:80;
root /var/www/example.com/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name example.com;
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
}
Next, we need to enable the configuration. This is easily done with a symlink:
Now we'll test the configuration for syntax errors and, if none are detected, reload nginx:
Now nginx is ready to serve normal HTTP requests. We'll need this for letsencrypt to work.
Step 3: Getting the certificate from lets encrypt
Letsencrypt's client, certbot, has a nginx installation plugin but according to the developers it is still considered experimental and buggy. We'll therefore choose for the manual installation method. First, we'll need to request the certificate. With most CA's this means generating a private key and CSR and submitting the CSR through their website and paying them for the certificate. Letsencrypt automates this entire process and also submits free certificates. To request a certificate for manual installation execute the following command (replace example.com with your domain name).
Note, you can use multiple -d arguments to get multiple domain names in the same certificate.
letsencrypt certonly -a webroot --webroot-path=/var/www/example.com/html/ -d example.com -d www.example.com
The letsencrypt client will now ask you to enter your e-mail address and to accept the terms of usage.
The letsencrypt client will now generate a private key and CSR, request a certificate from the CA, validate that it has control of the domain and finally download the certificate and put it on your system. The following files will now be placed on your instance:
/etc/letsencrypt/live/example.com/cert.pem - The signed certificate
/etc/letsencrypt/live/example.com/chain.pem - The certificate chain (root certificate and all intermediate certificates) needed to validate the certificate is signed by a trusted root certificate.
/etc/letsencrypt/live/example.com/fullchain.pem - A combination of both cert.pem and fullchain.pem.
/etc/letsencrypt/live/example.com/privkey.pem - The private key. Keep this file private and secure at all times. Compromise of this file (for example, emailing this file, downloaded by a third party etc.) can allow third parties to hijack your SSL certificate.
Step 4: Setting up nginx to use the new certificate
An SSL Configuration can be as simple as this:
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
However, this simple configuration means a few weaknesses will be present. You can use Qualys's SSL Test to test your configuration and find the most common weaknesses. At the time of this writing this simple SSL configuration will get you a "B" from this test. Of course, we want to do better than that. This test also still allows your website to be viewed unencrypted. Let’s fix this.
Step 5: Setting up HTTP to HTTPS redirect
This step is optional but does mean your website will be more secure and allow your users to automatically go to your SSL website.
First, open your nginx configuration file:
Remove any SSL entries in your server configuration block and add the following settings:
This will redirect all normal HTTP traffic to the HTTPS counterpart. Next, we'll need to actually setup the HTTPS server. We'll also harden the SSL security configuration. Copy the following code into your configuration file (replace example.com with your own domain):
server {
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2;
ssl_ciphers EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers On;
ssl_session_cache shared:SSL:128m;
add_header Strict-Transport-Security "max-age=31557600; includeSubDomains";
ssl_stapling on;
ssl_stapling_verify on;
# Your favorite resolver may be used instead of the Google one below
resolver 8.8.8.8;
root /var/www/example.com/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name example.com;
location / {
try_files $uri $uri/ =404;
}
location ~ /\.ht {
deny all;
}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}
Please note that this configuration excludes some older OSs and browsers from connecting. Most notably IE 6 to 10 on Windows XP, Vista or Windows 7, Safari 5, 6 on Mac OSX 10.6 and 10.8 and Android until version 4.3.
To enable all these clients (with the only exception being IE6 on windows XP), we'll need to allow TLSv1. To do this, replace the ssl_protocols in the above config with this line:
This configuration will get you at least an A on SSL Labs.Step 6: Header hardening
Besides the SSL configuration hardening, we can also add some HTTP Headers that further enhance the security of your website. Below are some recommended headers. To read more about these headers and what options you can use and which are applicable to you, please visit https://scotthelme.co.uk/hardening-your-http-response-headers/
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Xss-Protection "1";
add_header Content-Security-Policy "default-src 'self'";
Step 7: Setting up automatic renewal
Since letsencrypt only issues certificates valid for 90 days, automatic renewal is important. To do so, we'll create a renewal script, using letsencrypt's renew command. This command automatically renews all certificates with a validity of less than 30 days.
Save the following script in a logical location. We'll use /opt/renewCerts.sh
#!/bin/sh
# This script renews all the Let's Encrypt certificates with a validity < 30 days
if ! letsencrypt renew > /var/log/letsencrypt/renew.log 2>&1 ; then
echo Automated renewal failed:
cat /var/log/letsencrypt/renew.log
exit 1
fi
nginx -t && nginx -s reload
Now we need to make sure it’s owned by root and executable by root:
Next, we'll add it to cron for automatic execution:
In our example we'll run it once a week, which is more than enough to renew the certificates before they expire: