This guide will walk you through the steps of how to deploy HTTPS for your websites using the free and trusted Certificate Authority Let's Encrypt!
WARNING: This game is maybe outdated and does not take into account the latest features of Let's Encrypt, it's mostly a backup of a personal guide that is applicable for my setup.
Last updated: August 3rd 2016
- Introduction: where we discover Let's Encrypt Let's Encrypt
- Prerequisite: where we check the software versions
- Walkthrough: where the actual guide starts
- Nginx: where we install Nginx
- Certbot: where we install Certbot
- Generate the certificate: where things get interesting
- Prepare the Nginx Vhost: where we sharpen our axe
- Use Certbot: where we cut the tree
- Use the certificate: where we use the tree
- Diffie-Hellman parameters: where we may wait a long time
- HTTPS configuration: where we build the thing we wanted
- Results: where we hope it holds together
- Automatice renewal: where we don't want to have to come back
- Conclusion (yay!): where you are happy it works
Securing your self-hosted website used to be hard because you often only had 2 choices:
- Pay some company for a time limited (1-3 years usually) trusted certificate
- Use a self-signed certificate and have everyone panic when they visit you website for the first time
But that was before... Before Let's Encrypt arrived like a charming prince on their white horse. This new Certificate Authority came with a few promises:
- Free and Trusted certificates
- Automatic certificate renewal (with no downtime)
- Open source software
So now anyone who was self-hosting their website and other services could get free trusted certificates.
In this article we'll go over how to deploy a HTTPS certificate for your website if you use Nginx as your web server (I may do a version of this for Apache later).
This guide will be using 2 domain names behind which we'll host our website: example.com
and www.example.com
.
This tutorial has been tested on Ubuntu 16.04 (Xenial) and Debian 8 (Jessie) and should work the same way on other recent OS releases. You may need to follow slightly different steps but the general idea remains the same. Tested on the following versions:
- Ubuntu 16.04
- Nginx 1.10.0
- OpenSSL 1.0.2h
- Debian 8
- Nginx 1.6.2
- OpenSSL 1.0.1t
If you haven't already installed Nginx do it by running
$ sudo apt-get install nginx
==Note:== Make sure you don't have another webserver already running (like Apache) before going proceeding
Certbot is the new name given to the Let's Encrypt client that is now managed by the EFF (Electronic Frontier Foundation).
Let's go to Certbot website, select our webserver (Nginx here) and our operating system from the dropdown menu at the top (let's keep this page open we'll need it later). We're going to follow the Install steps only for now. These steps may change so check Certbot website before copying them.
Certbot is only available in the backports of Debian so we'll need to add this to our list of sources for apt
. Let's follow Debian's intructions on how to do that: add the following line to your sources.list
files (probably located at /etc/apt/sources.list
).
deb http://ftp.debian.org/debian jessie-backports main
Then let's update our package list by running
$ sudo apt-get update
We can now install the Certbot packet by running
$ sudo apt-get install certbot -t jessie-backports
Ubuntu's repository only has an older version of certbot but it works fine too. In order to install it simply run
$ sudo apt-get install letsencrypt
We are going to use the webroot plugin of Certbot to generate (and renew) our certificate. It general idea of this is:
- Certbot and the Let's Encrypt server agree on a file name and its content to be accessible at a some URL (
http://example.com/.well-known/acme-challenge/<random_file_name>
) - Certbot creates this file
- The Let's Encrypt server tries to retrieve this file
- If it fails no certificate is delivered
- If it worked the Let's Encrypt server generates the certificate
We'll give Certbot a dedicated folder to that (so that we do not mess with our website code). So create the folder /var/www/certbot
:
$ sudo mkdir -p /var/www/certbot
Now let's create the host file for that will be sued to display our website at the end of this guide. Since I'm using example.com
and www.example.com
I'm naming it this file example
(name it however you want). In this file we'll tell Nginx to redirect the request from the Let's Encrypt server to a dedicated folder. Here is the content to put in /etc/nginx/sites-available/example
with your favourite text editor:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com
location '/.well-known/acme-challenge' {
root /var/www/certbot/;
try_files $uri /$1;
}
location / {
return https://$host$request_uri;
}
}
==Note:== The last location
block is not necessary to obtain the certificate it will however allow us to permanently redirect all the requests to the HTTP address to the HTTPS address of the website.
Now activate this host file with nginx_modsite or manually with
$ sudo ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/example
and reload Nginx configuration with
$ sudo nginx -s reload
==Tip:== If you want to be sure that your configuration is correct before reloading you can run sudo nginx -t
until you don't see any errors.
==Note:== If you the reload or the configuration check throws you errors check if you don't have a missing ;
or an unclosed block because of a missing {
or }
.
We are finally doing it! We are going to run the following command (I'll break it down below):
- Debian
$ sudo certbot certonly --webroot -w /var/www/certbot -d example.com -d www.example.com --rsa-key-size 4096
- Ubuntu
$ sudo letsencrypt certonly --webroot -w /var/www/certbot -d example.com -d www.example.com --rsa-key-size 4096
Since it is the first time you run this command you will probably get a prompt asking you if a contact email and to accept the Terms of Let's Encrypt.
==Breakdown:==
certbot certonly
orletsencrypt certonly
means we are not using using any special plugin that does something more than getting a certificate (for example Apache plugin can install the certificate on its own)--webroot
means we are using the webroot method (described above)-w /var/www/certbot
set the webroot path for the domains mentioned after-d example.com -d www.example.com
generate a certificate for those domains
You can add the --rsa-key-size 4096
option at the end of the comand to set the key size of the certificate to 4096 bits (the default is 2048 bits). To be honest this is optional since 2048 is still big enough, has more compatibility with older devices and cost less CPU cycles (and by the time it won't be enough we probably will have switched to elliptic curves). The French National Security Agency of Information Systems (ANSSI) advises to use 2048-bits so one could prospect that state groups can already break them.
You should be able to see 4 files in /etc/letsencrypt/live/example.com
:
cert.pem
, the domain certificatechain.pem
, chain of trust of Let's Encrypt going up to the CA (we'll use this one)fullchain.pem
, cert+chain in one file (we'll use this one)privkey.pem
, the certificate private key DO NOT MAKE THIS KEY BECOME PUBLIC (we'll also use this one)
==Note:== The good thing here is that those files are symbolic links the always point to the latest issued version of this certificate!
For now Nginx can't access the certificate files (only root
can) so we'll create the folder /etc/nginx/ssl
and link the relevant files in it:
$ sudo mkdir /etc/nginx/ssl
$ sudo ln -s /etc/letsencrypt/live/example.com/fullchain.pem /etc/nginx/ssl/example.crt
$ sudo ln -s /etc/letsencrypt/live/example.com/privkey.pem /etc/nginx/ssl/example.key
$ sudo ln -s /etc/letsencrypt/live/example.com/chain.pem /etc/nginx/ssl/example.chain
DH parameters are used by the web server for the key exchange process and if the right cipher is used it ensures Perfect Forward Secrecy (PFS, ie. if you someone breaks your certificate they will only be able to decrypt the next communications but not the ones that happened before). The default parameters generated and used are 1024 bits is considered weak so we are going to generate a stronger one (2048 or 4096).
==Tip:== Generating DH parameters can be either long, sorta long, really long or hella long (especially if you generate a 4096 bits) so be prepared to wait during this step. If you are using screen
or tmux
you can start the generation in a tab and continue the guide while it runs.
We're going to put it in the same folder as the certificate so that we have everything in one place for Nginx:
$ sudo openssl dhparam -dsaparam -out /etc/nginx/ssl/dhparam.pem 4096
==Note:== Change 4096
by 2048
if you only want to generate a 2048 bits dhparam (and wait way less).
We are now going to add the HTTPS configuration to the Nginx vhost file. We are going to use the recommended configuration from Mozilla SSL Configuration Generation. Select Nginx as your webserver and most importantly the correct version for Nginx and OpenSSL (to check Nginx version run nginx -v
, to check OpenSSL version run openssl version
). If you version is not on the list select the closest previous version (so that you can be sure that the configuration will work). Depending on how secure you want to make your website and how compatible with old devices you want it to be you may select the Modern or the Intermediate profile: the biggest drawback to the Modern profile right now is that any smartphone with ==Android < 5.0== can't access your website.
Let's re-open /etc/nginx/sites-available/example
and start adding the new configuration: let's copy-paste the second server
block from the generator (the on that starts with listen 443 ssl
) below the first one. Let's detail what we need to change in order for it to work:
ssl_certificate /etc/nginx/ssl/example.crt;
ssl_certificate_key /etc/nginx/ssl/example.key;
ssl_session_cache
, I do not use this line here (see below)ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_trusted_certificate /etc/nginx/ssl/example.chain;
resolver
, I personnaly do not use this line as I am not entirely sure how to use it properly
In order to correctly handle SSL session resumption add the following line at the top of the config file you are editing (outside of the server
blocks)
ssl_session_cache shared:ssl_session_cache:10m;
Finally add you website configuration (reverse proxy, CGI, static directory, ...) just before the closing }
of the HTTPS server
block.
Before reloading Nginx make sure your configuration file is correct by running nginx -t
, if it's all good then you can reload Nginx.
You should normally be able to access your website with a HTTPS connection and the redirection from the HTTP to the HTTPS. If you have troubles check the HTTPS configuration against the generator, if you are not missing any ;
, {
or }
or if there isn't any problem in your website configuration (the part you added at the end).
Our certificate is all set, fantastic! However Let's Encrypt certificates only last 90 days so we need to renew it. We could come to the server every 80-90 days to manually renew it but that's not really practical (also we might forget to do it). So let's renew it automatically.
On Debian 8 Cerbot installs a cron on its own to check the certificates for renewal twice a day (it is the frequency recommended by Let's Encrypt in case they initiate a revocation of certificates).
To be sure that the cron job is installed, check if /etc/cron.d/certbot
exists.
If it exists we are going to ask Nginx to reload itsel twice a day too to take into account the change of the certificate: if we renew our certificate without reloading Nginx it would be as if nothing happened.
As root run
$ crontab -e
At the bottom of the file add the following line:
1 1,13 * * * nginx -s reload
This means that every day at 1:01am and 1:01pm Nginx configuration will be reloaded (e do this because the certbot vron runs randomly between 12am-1am and 12pm-1pm). Save and exit the editor.
==Note:== If /etc/cron.d/certbot
does not exist the crontab line becomes
26 3,15 * * * certbot renew -q && nginx -s reload
The random minute number is to be sure not to overload Let's Encrypt servers (which may hapen if everybody tries to renew their certificate at the exact same time).
We are going to add a cron job that runs at least once a day (Let's Encrypt recommends twice a day but we are probably not a high traffic/visibility website so once a day will do). This cronjob will run try to renew the certificates if they are within the renewal window (a few days/weeks before expiration). As root run
$ crontab -e
At the bottom of the file add the following line:
52 3,15 * * * letsencrypt -q renew && nginx -s reload
This means that every day at 3:52am and 3:52pm Certbot will try to renew the certificate and reload Nginx. Save and exit the editor.
That's it, it's over, we did it! If you want to test your HTTPS configuration you can use several online tools such as SSL Labs that will give you a fair indication of wether your configuration is pretty good, ok or not really secure. SSL Labs is interesting because it gives you advice on how to get a better score!