Let's Encrypt

by Thor Erik — on  ,  ,  ,  , 

cover-image

At TruckersMP we're running with end-to-end encryption, that is, for all network connections and leave a machine, it's encapsulated in TLS. To achieve this we're using a mix of CloudFlare and Let's Encrypt.

I'll only outline how we handle Let's Encrypt since the first part is easy, just sign up for CF.

How we did it at first

Many blog posts on the web, you'll find people suggesting using Certbot, which is fine, but being usually taken off the master branch, things might suddenly stop functioning, like when they renamed the letsencrypt-auto to certbot-auto, and just left the former blank, causing no alert to be triggered, cause the script was interpetered just fine(granted, also an oversight on our part mayhaps, but at least the expiry email got caught).

How we do it now

Now, since we're using Ubuntu 16.04, a stable letsencrypt is available in the repositories already, and installs it globally, so we can simplify and manage it using a regular puppet manifest to install the package(essentially apt install letsencrypt).

Things that are the same

Once we got letsencrypt installed, we create /etc/letsencrypt-config/, /var/www/letsencrypt, and create a file for the domain we wish to sign:

# use the webroot authenticator.
 authenticator = webroot
# the following path needs to be served by our webserver
# to validate our domains
 webroot-path = /var/www/letsencrypt

# generate certificates for the specified domains.
domains = <domain>

# register certs with the following email address
email = email@domain.com

# use a 4096 bit RSA key instead of 2048
rsa-key-size = 4096

For Nginx we create /etc/nginx/letsencrypt.conf with

location ^~ /.well-known {
  alias /var/www/letsencrypt/.well-known;
}

Now all we got to do is to include that file on any http endpoint which we wish to sign:

include letsencrypt.conf;

and request the first certificate:

letsencrypt -c /etc/letsencrypt-config/domain.ini

Once that is issued, we can configure up the HTTPS endpoint, a typical endpoint for us looks like this:

server {
  listen 443 ssl http2;
  server_name <domain>;

  ssl_certificate         /etc/letsencrypt/live/<domain>/fullchain.pem;
  ssl_certificate_key     /etc/letsencrypt/live/<domain>/privkey.pem;

  ssl_trusted_certificate /etc/letsencrypt/live/<domain>/chain.pem;
  ssl_session_cache shared:SSL:10m;
  ssl_dhparam /etc/ssl/dhparams.pem;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
  ssl_ecdh_curve secp384r1;
  ssl_session_tickets on;
  ssl_prefer_server_ciphers on;
  ssl_stapling on;
  ssl_stapling_verify on;

  access_log  /var/log/nginx/<domain>.access.log;
  error_log   /var/log/nginx/<domain>.error.log;

  client_max_body_size 25M;
  fastcgi_buffers 64 4K;

  proxy_set_header 'X-Forwarded-Proto' 'https';

  include letsencrypt.conf;

  # site spesific configuration
}

One of the benefits(afaik) is that we're using GCM, which means we can take advantage of Intel's GCM acceleration which is available on the machines we use (here's an article covering the E5 v3 line of Intel CPUs).
Worth a note here is that we're using a custom dhparams.pem file, which we make by doing

cd /etc/ssl
openssl dhparam -out dhparams.pem 4096

The last part of our setup is to add the cron, which is just a plain script, /etc/cron.monthly/renew-ssl

#!/bin/bash

letsencrypt certonly -c /root/letsencrypt-config/<domain>.ini --renew-by-default >> /var/log/letsencrypt.log 2>&1 #repeat this for each domain

systemctl reload nginx >> /var/log/legsencrypt.log 2>&1

The last part of the setup is to make the above script executable:

chmod +x /etc/cron.monthly/renew-ssl