Let's automate Let's encrypt

Just in case you did not yet hear about “Let’s encrypt”, let me explain the context in a few words. Feel free to visit their website for more details. They’re starting their public Beta as early as 2015-12-03.

What is it about?

Services like websites are offering secure connections to ensure privacy. Protocols like HTTPS are used which require SSL certificate(s) and a private key to encrypt data in transit. Besides enabling encryption, a certificate makes sure you’re communicating with the service you intend to communicate with and not with someone who is intercepting your communication. Sending encrypted data to someone who can de-crypt it but is not the intended recipient is even worse than communicating in plain-text.

Certificates are signed by a certificate authority (CA) which acknowledges that the person which uses a certificate exists and is trustworthy. This is usually determined by checking the services domain via E-Mail validation or the person/organisations existance by checking paperwork. Software like web browsers use a predefined directory of trusted CAs to determine if they trust the judgement of a CA. For example, if Firefox trusted “Cool CA Inc.”, then certificates signed by this CA are also trusted and if i have a certificate signed by this CA, your browser trusts the connection to my website.

Since it’s quite hard and expensive to be trusted by all browser vendors only few CAs are trusted by default. Browser vendors obviously have to make sure that they can trust a CA since their judgement will affect millions of users. CAs which made it that far compensated their effort by making a fortune out of a technically trivial process of validating persons/websites and create certificates. That said, they’re also offering insurrance services but that’s rather irrelevant for most users. Taking money to enable security contradicts the general requirement of secure communication for everyone. Someone running a small website may not be able to invest a lot of money to get a SSL certificate signed by one of those CAs. One alternative would be using self-signed certificates which are not trusted by browsers and produce severe error messages when opening the website. Another alternative would be not to use encryption at all, which sadly is what many people have chose.

Let’s encrypt

This is where “Let’s encrypt” joins the party. They’re a non-profit organisation backed by organisations like Mozila, the EFF and some well established online companies. The organisation offers certificate signing for free and those certificates are trusted by all major browsers. You can find out a lot more information and details at their website.

A speciality of “Let’s encrypt” is that you can request certificate signing in a automated way and also renew your certificates this way. This is done by running a software which takes care about the request and signing process. Renewing is a standard process which usually needs to happen about every 1-3 years. Certificates are bound to a specific domain and domain ownership can change over time. Therefor unrestricted certificate validity for a specific domain can become a threat because they’re not validating the current domain owner anymore. “Let’s encrypt” wants to make sure this problem is very temporary, therefor they opted to make their certificates valid for 90 days. After or before that expiration date you need to renew the certificate and prove that you’re still owning the domain.

Automatically renewing certificates with ones that are valid for another 90 days is a big relief for webmasters that no longer have to run through a manual process. It’s a common issue that a certificate expires without notice and website visitors are getting error messages, this also gets solved by automation.

Automated renewal

So how to automate the process? “Let’s encrypt” offers a software to fetch/renew certificates and also validate if you’re still owning the domain. For that, the software needs to run on the server defined by the DNS entry for the specific domain. The software initiates the validation process (ACME) and a web service at “Let’s encrypt” communicates with that software. During that process ports 80/tcp and 443/tcp are used by default which means your webserver needs to go down for a couple of seconds to allow the software to use those ports. That downtime may be worked around in the future but for now i don’t care much about it. “Let’s encrypt” is working on integration with major web servers such as Apache or Nginx to make the process completely transparent. Until then some scripting is required, which i’d like to share.

To use automatic renew, first make sure your web server (nginx in my case) loads certificates from the letsencrypt folder.

1
2
ssl_certificate /etc/letsencrypt/live/martin.heiland.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/martin.heiland.io/privkey.pem;

Stop the web server and initiate the renew process. Note that a few options need to be set to force non-interactive renewal:

1
2
$ service nginx stop
$ /path/to/letsencrypt/letsencrypt-auto -d martin.heiland.io --renew-by-default --rsa-key-size 4096 --agree-tos --agree-dev-preview --server https://acme-v01.api.letsencrypt.org/directory auth

Note that due to a bug, this command may fail several times with a “The client sent an unacceptable anti-replay nonce” error. In this case, just repeat the command until it succeeds.

If HPKP is used, you will need to refresh your pins:

1
2
3
4
$ openssl x509 -noout -in /etc/letsencrypt/live/martin.heiland.io/fullchain.pem -pubkey | openssl asn1parse -noout -inform pem -out public.key
$ openssl dgst -sha256 -binary public.key | openssl enc -base64 > hash.key
$ PIN1=$(cat hash.key | sed 's_/_\\/_g') && sed -i 's/add_header Public-Key-Pins.*/add_header Public-Key-Pins '\''pin-sha256=\"'"${PIN1}"'\"; pin-sha256=\"sRHdihwgkaib1P1gxX8HFszlD+7\/gTfNvuAybgLPNis=\"; max-age=31622400'\'';/g' /etc/nginx/conf.d/martin.heiland.io.conf
$ rm public.key hash.key

Finally, restart the web server and check if it’s serving the updated certificate:

1
$ service nginx start

You obviously can put this process to a monthly cronjob, but you might to supervise the process a couple of times to make sure it works flawlessly.