Skip to content

Instantly share code, notes, and snippets.

@BackIsBachus
Last active July 17, 2018 20:53
Show Gist options
  • Save BackIsBachus/459cc41a495cea7b60faf407a66ffabf to your computer and use it in GitHub Desktop.
Save BackIsBachus/459cc41a495cea7b60faf407a66ffabf to your computer and use it in GitHub Desktop.
A (probably somehow oudated) guide to deploy Let's Encrypt certificate on Nginx

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

Table of Content

Introduction

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.

Prerequisite

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

Walkthrough

Nginx

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

Cerbot

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.

Debian

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

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

Generate the certificate

Prepare Nginx vhost

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 }.

Use Cerbot

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 or letsencrypt 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 certificate
  • chain.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!

Use the 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

Diffie-Hellman parameters

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).

HTTPS configuration

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 serverblocks)

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.

Results

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).

Automatic renewal

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.

Debian

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).

Ubuntu

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.

Conclusion (yay!)

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment