Chrome 68, due in July 2018, will mark all non-HTTPS sites as insecure . Instead of buying a certificate it seemed like an apt opportunity to use Let’s Encrypt , a free and automated Certificate Authority. It’s easy enough to get started with Certbot supporting most platforms .
Getting started
By far the easiest way of getting started is if you have access to the web content directory. Certbot will generate a file in a well known path and handle the handshaking process between Let’s Encrypt and the website to verify the websites identity. Google Analytics has a similar approach to prove site ownership and the ACME protocol documents the process.
I’m using an Alpine Linux image for my blog container and a package for Certbot exists in the standard repositories. This installs other dependencies (notably Python) which bloats the image somewhat but it’s a small price to pay in this instance. Here’s the amended Dockerfile:
| |
First we create a volume for /etc/letsencrypt where Certbot will save the certificates. This allows other containers to use the same certificate by mounting the same volume. On the server I’ve manually created a volume with the following command:
| |
We add the certbot package using apk for later use and of course expose port 443 for SSL. When we run the container we need to mount the newly created volume at the location specified in the Dockerfile:
| |
With this container built and running, we need to get the initial certificates manually. Inside the container we ask Certbot to generate a certificate for us. I’ve included both yourdomain.com and <www.yourdomain.com> as I have a redirect from www to non-www. The --webroot directive makes Certbot use the file system for certificate generation and validation and not integrate directly with the web server through a plugin - I prefer to edit the website configuration by hand.
The --agree-tos and --email flags are necessary to avoid interactive prompts for this information on first registration.
| |
You should see the output shown below. Take note of the renewal instructions - we’ll get back to that.
| |
You can find the generated certificates inside the /etc/letsencrypt/live folder.
NGINX setup
Now that we have the certificates we can set up the web server (NGINX in my case) to use them. First, let’s redirect all traffic from port 80 to port 443:
| |
This will return a 301 moved status code and keep the original sub-URI for redirection. Now to set up HTTPS :
| |
It’s important to disable SSLv3 as the protocol is vulnerable to attacks so we specify TLSv1, TLSv1.1, and TLSv1.2 explicitly as the allowed versions.
Furthermore we add HSTS as another layer of defence to avoid access of the website over HTTP by default :
The HTTP Strict Transport Security header informs the browser that it should never load a site using HTTP and should automatically convert all attempts to access the site using HTTP to HTTPS requests instead.
In other words, once the browser spots this header and once it confirms that the site has HTTPS enabled it will always use HTTPS for future requests. We can set the max-age parameter as high as it can go as I’m not planning on rolling this back to HTTP any time soon.
We also need to disable insecure ciphers to avoid downgrading of the connection to a less secure cipher. SSLlabs have a great article that they frequently update with best practices in this regard .
We enable session resumption for performance reasons. The server will store a session id of the client so that future connections with the same session id (in a short time frame) the client can resume the session instead of going through the whole negotiation process again.
Pro tip: The SSLlabs TLS testing tool is the best tool to test your SSL implementation and will highlight any potential issues. While using it read the notes about compatibility - it’s essential to get the balance right between security and compatibility.
Renewing certificates
Let’s Encrypt certificates are valid for three months so we need to run the renew process frequently to avoid sitting with an expired certificate. Certbot has this built in with the certbot renew command, mentioned in the initial output after first creation of the certificates. There are a couple of different ways to do this with containers:
- Running a sidekick container to renew the certificates
- Installing the dependencies on the host and running the command directly on the volume
- Running crontab on the host machine and exec’ing into the container
(2) is way too dirty and spoils all the isolation benefits we have with containerisation and (1) is the way I would do it with serious production applications abiding to “the Docker way”. In the spirit of doing the simplest thing possible I opted for (3). On the host machine you can edit the crontab file via the command
| |
We can create a simple entry to run the renew command in the container on a schedule:
| |
This will run the renew process and if the script renewed the certificate execute the post hook, which restarts the web server and let the change take effect.
Note
Let’s Encrypt applies rate limiting on requests to their services. While you are testing you can use their staging area to avoid hitting the request ceiling.
Photo by James Sutton on Unsplash




