Free SSL Certificates with LetsEncrypt and Nginx

Posted: March 8, 2016

For a long time now the benefits of SSL certificates have come with one major drawback. Cost. With an average single domain SSL certificate still costing within the region of £30 per year from major providers, converting a whole catalogue of domains can become an expensive task. The year on year cost makes it unattractive to many people, especially small businesses or individuals running blogs. For the longest time SSL certificates and encrypted HTTPS traffic have been reserved for big businesses and corporate players like Amazon and Google. But if you’re handling any sensitive data why not encrypt it?

Let’s Encrypt is a new certificate authority (CA) offering free and automated SSL/TLS certificates (albeit with a bit of manual configuration). Certificates issued by Let’s Encrypt are trusted by most modern browsers in production today, including Internet Explorer on Windows Vista (who’d have thought it!). Setup is relatively simple: download and run the Let’s Encrypt client to generate a certificate, then configure Nginx to respond to requests for validation.

Before issuing a certificate, Let’s Encrypt validates ownership of your domain. First, The Let’s Encrypt client running on your host creates a temporary file (a token) with the required information in it. The Let’s Encrypt validation server makes an HTTP request to retrieve the file and validates the token, which serves to verify that the DNS record for your domain resolves to the server running the Let’s Encrypt client. This relatively simple setup means all you need is SSH access to your server and the ability to modify your Nginx configuration files.

The Let’s Encrypt client does not yet officially support Nginx (support is in beta), but you can still get started right away using Let’s Encrypt with Nginx.

Required Packages

This tutorial assumes you’re using a Debian based linux distribution, and also assumes you’re running an Nginx webserver. Further, more detailed documentation on LetsEncrypt can be found here. The most important package is the letsencrypt-auto package, which can be installed quite simply using the following commands:

sudo apt-get update
sudo apt-get install -y git
sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
cd /opt/letsencrypt
sudo ./letsencrypt-auto

The last line sudo ./letsencrypt-auto runs the LetsEncrypt installer, pulling in all the required dependencies including Augeas, gcc,Python, and full CA certificates from LetsEncrypt. It’ll take a few seconds to run but after it has completed you’ll be ready to setup your first site!

Configuration

Let’s Encrypt creates its temporary authentication tokens within your webroot in a directory to ensure you own the domain in question. The full storage path is webroot-path/.well-known/acme-challenge/. The location of your webroot is important, for my test domain (test.tomkent.me) it was in /srv/tomkenttest but for you it will be different.

The first thing to do is make sure your webroot actually exists. This may sound stupid, but if it doesn’t exist Let’s Encrypt will refuse to sign the certificate, because your domain ownership verification will fail. We then need to create a template file with the values that Let’s Encrypt needs to issue a certificate, using this GitHub Gist. Without the template, we would have to specify the values on the Let’s Encrypt command line – which is a lot more complex than using the template.

Create the file /etc/letsencrypt/configs/my-domain.conf, where “my-domain” is your fully qualified domain name (in my example, /etc/letsencrypt/configs/test.tomkent.me.conf). Copy in the Gist contents and set the appropriate values in the domains (again, the fully qualified domain name),rsa-key-size, server (full URL with scheme), and email fields.

The server field should be set to https://acme-v01.api.letsencrypt.org/directory as this is the current validation server URL at the time of writing.

# the domain we want to get the cert for;
# technically it's possible to have multiple of this lines, but it only worked
# with one domain for me, another one only got one cert, so I would recommend
# separate config files per domain.
domains = test.tomkent.me

# increase key size
rsa-key-size = 2048 # Or 4096

# the current closed beta (as of 2015-Nov-07) is using this server
server = https://acme-v01.api.letsencrypt.org/directory

# this address will receive renewal reminders
email = [email protected]

# turn off the ncurses UI, we want this to be run as a cronjob
text = True

# authenticate by placing a file in the webroot (under .well-known/acme-challenge/)
# and then letting LE fetch it
authenticator = webroot
webroot-path = /srv/tomkenttest/

Next you need to modify your Nginx configuration to give LetsEncrypt access to the temporary file. If you’re modifying an nginx.conf file, you should put the following:

server {
    listen 80 default_server;
    server_name test.tomkent.me;

    location /.well-known/acme-challenge {
        root /srv/tomkenttest;
    }
    ...
}

If you’re using a control panel like Ajenti (like me) you can simply add the following to your website settings under Advanced > Custom Configuration:

location /.well-known/acme-challenge {
    root /srv/testtomkent;
}

Restart your Nginx webserver using sudo nginx -t && sudo nginx -s reload, so the configuration changes take effect.

Request A LetsEncrypt Certificate

Now that everything is setup we request the certificate from LetsEncrypt.

cd /opt/letsencrypt
./letsencrypt-auto --config /etc/letsencrypt/configs/test.tomkent.me.conf certonly

If the certificate signing is correct then the following message will be outputted to confirm that we successfully obtained a certificate and associated files, which Let’s Encrypt stores in /etc/letsencrypt/live/my-domain. The files of interest are fullchain.pem and privkey.pem.

Updating letsencrypt and virtual environment dependencies......
Requesting root privileges to run with virtualenv: /root/.local/share/letsencrypt/bin/letsencrypt --config /etc/letsencrypt/configs/my-domain.conf certonly

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/my-domain/fullchain.pem. Your cert
   will expire on date. To obtain a new version of the
   certificate in the future, simply run Let's Encrypt again.

Now we need to configure Nginx to accept SSL connections and authenticate them using the generated certificates. Go back to your Nginx configuration file and enter a new server block:

server {
    listen 443 ssl default_server;
    server_name test.tomkent.me;

    ssl_certificate /etc/letsencrypt/live/test.tomkent.me/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/test.tomkent.me/privkey.pem;

    ...
}

You can then save your file and restart Nginx using sudo nginx -t && sudo nginx -s reload. As before, if you’re using Ajenti you can setup the connection by going to the “SSL” tab and entering the following:

  • SSL certificate path = /etc/letsencrypt/live/test.tomkent.me/fullchain.pem
  • SSL key path = /etc/letsencrypt/live/test.tomkent.me/privkey.pem

To enable SSL connections you need to then go to the “Ports” tab and enter the following settings:

This will allow SSL connections on port 443. If you don’t want to set this as the default connection that is also fine, but I’d recommend keeping it on. Apply the changes and Ajenti will restart Nginx automatically. Ready to roll!

You’re almost ready to go now, if you browse to your domain using the https:// prefix you should see your website. You’ll also see a green padlock in the address bar if you’re using Chrome or Firefox, but if you don’t it’s probably because you have “mixed content” on your page – IE: you have some content loaded from non-https sources. Once you fix any mixed content warnings your site will be fully protected.

Automate Certificate Renewal

Lastly, all we need to do is set our certificates to auto renew. LetsEncrypt certificates only last a few months before they need to be re-authenticated. But thankfully, the whole process can be automated using a simple bash script and cron.

The following script should be saved somewhere – I put it in /etc/letsencrypt/renew-script.sh but the location doesn’t really matter.

#!/bin/sh

cd /opt/letsencrypt/
./letsencrypt-auto --config /etc/letsencrypt/configs/test.tomkent.me.conf certonly

if [ $? -ne 0 ]
 then
        ERRORLOG=`tail /var/log/letsencrypt/letsencrypt.log`
        echo -e "The Let's Encrypt cert has not been renewed! \n \n" \
                 $ERRORLOG
 else
        nginx -s reload
fi

exit 0

This will run the certification action as we did manually earlier, then restart Nginx if it succeeds. You can add more ./letsencrypt-auto --config /etc/letsencrypt/configs/exmaple.tomkent.me.conf certonly lines to generate multiple certificates. You’ll need to set the script to executable using chmod +x /etc/letsencrypt/renew-script.sh in order to make it executable via cron.

Finally, add it to the root user crontab using crontab -e as the root user:

0 0 1 JAN,MAR,MAY,JUL,SEP,NOV * /etc/letsencrypt/renew-script.sh

Now you’re all set and ready to go with FREE SSL certificates!

© 2012-2018 Tom Kent. All Rights Reserved