I’ve been pretty absent from this blog, so from now I’m going to try to write something at least once a week, on Monday. I may not be able to keep this up, but the first stop to form a habit is to start doing it once.
My requirements for blogging these days are basically: a web UI, I don’t have to maintain deployments, no effort security maintenance, some backups. And frankly the backups are not that important.
Since I already had my domain at Namecheap for a few years, I ended up choosing their EasyWP offering, which is pretty basic but should be fine for now.
While doing this, I took the chance to learn a bit about LetsEncrypt, because it seemed fun.
Let’s Encrypt is a nonprofit Certificate Authority. A CA is basically an entity that can provide a cryptographical proof that you are really you (or rather that some domain like www.riffraff.info is really serving content provided by the owner of www.riffraff.info). For a long time you had to pay for this kind of service, but then Let’s Encrypt appeared and started providing this service for free, with the stated intent of making the internet a safer and better place. Yes, they’re awesome.
The way all this works is by essentially providing some APIs and command line tools that allow you to:
- request a certificate for domain.com
- get some sort of challenge to prove that you actually own that (e.g. if you really are who you say you are put this string in your DNS records or put this file on your website)
- receive a certificate once Let’s Encrypt verifies the challenge is met
The fun part about this is that they will only provide you with short-expiring certificates (2 months), because they want you to automate this. Not automating a renewal will always result in you messing up by forgetting to renew a certificate, like it may happen with anything that you only need to do every few years (checks driving license quickly).
But fear not! The nice thing about certbot is: you can plug in any kind of script you want, simply providing them as command line options, like this
certbot certonly --manual-auth-hook some-script.sh --manual-cleanup-hook some-other-script.sh -d my.domain.foo
So, how do we do this for EasyWP? Well, there’s no API for domain management but there is SFTP access to upload custom files. This means we can upload a file and use the HTTP verification method.
When you run certbot, it will talk to the Let’s Encrypt servers and receive a challenge, which it will pass through the environment variable
$CERTBOT_TOKEN to the
The job of your script is thus to get this value and put it in a correspondingly named file in a specified HTTP accessible directory
set -x -e
# assume there is a .well-known/acme-challenge/ directory already
echo $CERTBOT_VALIDATION > $FULL_PATH
echo "put -R ./$FULL_PATH /$FULL_PATH" | sftp $SFTP_USER
Notice you will need to provide the sftp password on the command line for this to work. You can use expect, lftp or whatever to automate this properly, this is just for example purposes.
Once this runs, certbot will tell the Let’s Encrypt servers to check if the challenge is met, and if it is it will receive a new certificate, which you can upload through the EasyWP admin UI.
But: what about the cleanup hook? Well, once everything is done, certbot will run the hook to allow you to get rid of the leftover files. Once again, you will have
$CERTBOT_TOKEN so your cleanup script can look like this
set -x -e
# keep this challenge around, but make it clear we used it
mv ".well-known/acme-challenge/$CERTBOT_TOKEN" ".well-known/acme-challenge/used.$CERTBOT_TOKEN"
The full command line would look like this, and notice you can get multiple certs at once
certbot certonly -v --manual --preferred-challenges=http --manual-auth-hook $PWD/authenticator.sh --manual-cleanup-hook $PWD/cleanup.sh -d domain.foo,other.domain.foo
This is of course more work then necessary as you can just pay a bit more to get an SSL certificate from Namecheap, or you can (I think) proxy everything through Cloudflare, but I had fun playing it with it, and I hope this can be useful to someone else 🙂