2018 Sep 25

Automatically enable HTTPS on your website with EFF's Certbot, deploying Let's Encrypt certificates.

Until recently, if you wanted to enable SSL (Secure Sockets Layer) on your site, you'd need to obtain and pay for a certificate from a CA (certificate authority). CAs are entities that cryptographically sign TLS/SSL certificates to vouch for their authenticity. Operating systems and browsers then have a list of trusted CAs that they use to verify site certificates. With the advent of Let's Encrypt, this process is now offered for free. Let's Encrypt is able to do this by relying on sponsorship and donations to fund the necessary infrastructure.

Given this new option, you're now able to set it up yourself in a custom or more automated way depending on your skills and needs. The tool that we'll discuss here, called Certbot, interfaces with Let's Encrypt and makes the process easy. This technology is also now being seen on hosting panels too as a value added service. It may or may not be advertised as Let's Encrypt though. Certbot is by far the most popular Let's Encrypt client available, although there are others. It is included in most major Linux distributions and includes convenient configuration capabilities for Apache and Nginx.

It's a great time for this to now be available given that the entire web is moving towards being made secure. Not only does it impact security, but SEO as well. Browsers are now expecting websites be secured by default with warnings appearing for any site that isn't using SSL. There's many articles going up now about why you need HTTPS

Installing Certbot

Here we're using CentOS 7, which includes the Certbot tool in its repositories. It's widespread enough now though that it should be available by default assuming you're using a common distribution.

yum -y install certbot

Now that the tool is installed, we'll need to create a new SSL certificate using our domains. For this, you'll want your web server listening on port 80. Typically the process covered in this blog post is the last thing you do when launching a new site or setting up a new server. Since you need the domain pointing at the server in order to validate it using Certbot, we keep it serving without SSL until we've set it up the first time. For this, we'll usually setup a basic version of the web server config that's meant for port 80 and another that will be swapped out when using 443 that also contains much of the SSL specific settings below.   

Assuming you have the domain pointing at the server for now just on 80, here's the steps we take with Certbot to configure the certs:

certbot certonly -d mywebsite.ca -d www.mywebsite.ca

The -d flags contain all possible domain variants this certificate will cover. It's best practice to only house one site's domains per certificate (in case you use the server to host more than one site). It will then walk you through some steps. Typically here we choose the webroot option. Certbot in this case will place temporary files in your webroot and try to access them via the script to prove you own the domain. This is also how it will renew the cert again going forward.

Saving debug log to /var/log/letsencrypt/letsencrypt.log

How would you like to authenticate with the ACME CA?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Spin up a temporary webserver (standalone)
2: Place files in webroot directory (webroot)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 

Once it's done, it will tell you the location of the new files. These paths are what will be used in your web server config.

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/mywebsite.ca/fullchain.pem. Your cert will
   expire on 2017-10-23. To obtain a new or tweaked version of this
   certificate in the future, simply run certbot again with the
   "certonly" option. To non-interactively renew *all* of your
   certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Configuring NGINX for SSL

In this case we're using NGINX, but the same certificates created above can likely be used with other software. Here we'll assume you're now working in a version of the web server config that will become the final 443 config. Below is a template that can be used as a starting point. 

The first bit is for 80, and redirects to 443. It also has a second location block for Certbot to use. This is because we don't want those requests to follow the HTTPS redirect when it's trying to renew the certificate via a scheduled cron task (see below). We also have a www to non-www redirect (not related in this case but a good idea to pick one for SEO purposes). Finally there's the SSL block. You'll notice a reference to a file the lives beside this config called mywebsite_ssl_params. It houses the SSL specifc config for NGINX. See the SSL Params section below for more information.

# all incoming on 80
server {
    listen         80;
    server_name mywebsite.ca www.mywebsite.ca;

    location ^~ /.well-known/acme-challenge/ {
      root /srv/mywebsite-website-drupal/src;
    }
    location / {
      return 301 https://$host$request_uri;
    }
    include conf.d/mywebsite_ssl_params;
}

# redirect www to non-www
server {
    listen         443;
    server_name www.mywebsite.ca;
    
    location / {
      return 301 https://mywebsite.ca;
    }
    include conf.d/mywebsite_ssl_params;
}

server {
    listen       443 ssl;
    server_name  www.mywebsite.ca mywebsite.ca;

    include conf.d/mywebsite_ssl_params;

    root   /srv/mywebsite-website-drupal/src;

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

SSL Params

This is a separate file that sits beside the NGINX config file. You can see the reference to it above in the server blocks. It doesn't need to be separated but tends to be cleaner this way. You can see the reference to the two files Certbot created at the top. The other key bits here that need attention are the ssl_dhparam and ssl_protocols settings. There's a lot that can be said for what those should be set to, but for now TLSv1.2 is the current standard. As for the ssl_dhparam, that is again another file we include. It holds the Diffie-Hellman key. There are numerous resources explaining the importance of it. Much of what appears below including the DH params can be tweaked in order to get an A+ SSL rating.

ssl_certificate /etc/letsencrypt/live/mywebsite.ca/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mywebsite.ca/privkey.pem;
ssl_protocols  TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
add_header Referrer-Policy "strict-origin";
add_header X-XSS-Protection "1; mode=block";
add_header Content-Security-Policy "default-src https: data: 'unsafe-inline' 'unsafe-eval'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains" always;

Automatic Updates

Once the site is being served over HTTPS and the config is complete, we want to setup Certbot to renew the certificates automatically. Typically this is done via a task on the server.

Since the certificates only last 90 days, we need a way to renew them before that day comes. Certbot has a renew flag that we can use to do this automatically. Once the expiry date is within 30 days, the script will try and renew/replace the certificates that were previously there. Let's Encrypt recommends this be run twice daily. Here we're just putting a cron job under the root account. The "post-hook" bit at the end is key and is seemingly not well documented. What it does it reload the web server once Certbot has renewed the certificate. It will only run then and not every time the cron job is triggered.

We also recommend as a final step to run the command below with the --dry-run flag. This will ensure the setup above can run on its own. It's also a way to debug a setup after it may not have been renewing as it should. During the initial setup of the certificates, Certbot will ask for an email address. This will be used to send out a notification in case the renewal dates are coming up and the certs have not yet been renewed. Typically if the process is running fine you'll never see an email.

#every 12 hours run certbot
0 */12 * * * certbot renew --post-hook "systemctl reload nginx"

Conclusion

This is just one way of using Let's Encrypt to obtain free SSL certificates. There is another way of using Certbot that automates the changes to your web server and instructions exist on the Certbot site to walk you through the steps depending on the software you're using. This process is what we prefer since it just provides the certificates and doesn't touch anything else. The tools are also always changing, so the above may differ going forward, as it certainly has many times since being released.