Adventures in Engineering

Continuous TLS

This is a quick code dump post to share a script for automating LetsEncrypt certificate renewals for my blog!

What is LetsEncrypt?

LetsEncrypt is a certificate authority who’s goal is to provide free certificates to help encrypt the web! It’s backed by well known internet and privacy organisations such as EFF, Mozilla, Akamai and Cisco.

My blog uses a continuously renewed LetsEncrypt certificate using AWS CloudFront and S3 and deployed using Codeship during my existing build process for developerjack.com.

This executes as the last step in my codeship pipeline so that any environmental failures don’t prevent the deployment of new posts and code.


#!/bin/bash

HOSTNAME=$( cut -d ',' -f 1 <<< "$LE_DOMAIN" )
EXPIRE_THRESHOLD=$((86400 * 30)) #30 days


function lebot() {
    if hash letsencrypt 2>/dev/null; then
        letsencrypt "$@"
    elif hash certbot 2>/dev/null; then
        certbot "$@"
    else
        echo >&2 "letsencrypt was not found in any of its forms. Aborting."; exit 1;
    fi
}


echo "" | openssl s_client -connect $HOSTNAME:443 -servername $HOSTNAME -prexit 2>/dev/null | sed -n -e '/BEGIN\ CERTIFICATE/,/END\ CERTIFICATE/ p' | openssl x509 -checkend $EXPIRE_THRESHOLD -noout

if [ $? -eq 0 ]
then
  echo "Certificate is good for another day!"
else
  echo "Certificate has expired, or will soon (or there was an error)!"
  echo "Attempting certificate renewal"

  lebot --agree-tos -a letsencrypt-s3front:auth \
     --letsencrypt-s3front:auth-s3-bucket $S3_BUCKET \
     --letsencrypt-s3front:auth-s3-region $S3_REGION \
     -i letsencrypt-s3front:installer \
     --letsencrypt-s3front:installer-cf-distribution-id $CF_DISTRIBUTION \
     -d $(echo ${LE_DOMAIN//,/ -d }) \
    -m $LE_EMAIL \
     --renew-by-default --text \
    --work-dir ./le/work \
    --cert-path ./le/cert \
    --key-path ./le/key \
    --config-dir ./le/config \
    --logs-dir ./le/logs
fi

A few notes

LE_DOMAIN is a comma separated list of domains for which the certificate will be issued. The first of these domains is used for the verification challenge before LetsEncrypt will issue a certificate. e.g. LE_DOMAIN=developerjack.com,www.developerjack.com

LE_EMAIL is the contact email for my certificate – I get notified when its due to expire.

Remaining variables are somewhat self-explanitory for AWS environments, just copy and paste them into your environment.

The directory overrides are so that the LetsEncrypt executable doesn’t try and access /etc/* directories which are protected in Codeship build environments.

Setup

I currently have the following script configured in the Codeship deployment pipeline

pip install letsencrypt-s3front --upgrade
chmod a+x letsencrypt-renew.sh
./letsencrypt-renew.sh

By installing the letsencrypt-s3front plugin, pip will automagically install the LetsEncrypt package too.

And there you have it – every commit on my blog (and side apps too by copy-paste) ensures that my site remains on a valid TLS connection!

P.S. the combination of CloudFront and a LetsEncrypt certificate results in an A SSL rating – all for 10 minutes work!

developerjack