Automating Letsencrypt renewals with DNS-01 challenges

Preface

When researching options before writing the code referred to in this article, I came across a comment from someone I understand to be involved in the admin/author side of certbot. While I can’t find the link any more, they basically replied to someone stating that certbot would never provide plugins for DNS providers, which made sense since there are so many. However, shortly after finishing off this software, I came across the shiny new release of certbot, which  has some pre-written plugins for, you guessed it, DNS providers.

These plugins are not yet packaged for Ubuntu, so while I tried to do a quick test, I ended up still using my new script. If you’re starting out with certbot 0.22.0 or higher and get their plugins, they are probably a better option however this stuff will still work and is an option for you. And I’ll keep the code live for anyone who wants it, if for no other reason than it’s something else others can pick as an example to learn things from.

Introduction

Many of you will have heard of Letsencrypt, a service that enables creation of SSL certificates for use on websites (and anywhere else technically) for free; cost being one of the barriers to wider adoption of secured websites. I use this service for several sites, including this one.

I had a particular issue when it came to certificate renewal time that wasn’t supported automatically. In this post, I’ll explain a little about Letsencrypt and its client application certbot, and about “challenges” which are how Letsencrypt verifies you should be given a certificate you ask for. I’ll only be dealing with the authentication side of certbot: while it and a number of other clients are able to install the certificates for you as well, I did this part manually to fit within my Nginx configuration the way I wanted, but there is plenty of information out there if you want to learn more about that side.

My Setup

If you’ve read my previous posts you’ll likely have an idea of this already, but a simple overview for those who haven’t.

I run my domains on servers hosted in AWS on Ubuntu servers. I use Nginx (Open Source Version) as a hosting and proxying platform. My websites are, for the most part, exposed via CloudFlare (Free Tier).

For me, these certificates are securing the leg between CloudFlare and my server so users don’t normally see them, but they form one leg of the end-to-end security chain..

Letsencrypt

Letsencrypt LogoLetsencrypt can be managed by a number of software clients, however, the main one and the one I use is called certbot. Certbot allows the issuing of new certificates and the renewal of existing ones; renewal being important because the main caveat of these certificates is that they are only valid for 90 days. And the key part of this process is validating ownership in a challenge/response style setup, which can be done 3 different challenge methods.

HTTP-01

Probably the most common or most easily achieved method of validating your domain is HTTP-01. It involves posting a specified file in a specified location on the website. There are plugins for certbot that make this really easy for a number of hosting setups, including Nginx which I run.

For me, this wasn’t so practical. As I mentioned the DNS wasn’t pointing to the new server yet, and I didn’t want to mess around putting stuff on the old servers. Also, I had an additional site that I wanted a certificate for but I wasn’t able to modify the content to support this method.

TLS-SNI-01

I haven’t actually heard of anyone using this in the wild, and I haven’t tried it as it’s not functional when using CDN in front of your servers.

DNS-01

So this is the one I chose. DNS-01 involves adding a TXT record to your DNS with the specified value. For me this was great, it didn’t matter where my website was pointed yet, or if I could modify files. Because I could edit the DNS this way I was able to ensure zero downtime getting the certificates issued and in place before I did the cutover.

The downside to DNS-01

DNS-01 got me going well, however, when it comes to renewal time it’s not something that can be automatically supported. HTTP-01 has various options and settings to dump files in a specified location to automate that, but DNS is much more diverse and not all providers even offer an API. I gave it some thought and confirmed for my self that I couldn’t change to HTTP-01, mainly as I had this system issue certificates for domains that didn’t point to this server still (yeah maybe I’m a special case, but it is what it is).

Certbot has hooks to do things before and after any validation steps, so I decided I’d make it automated myself!

Automating DNS-01 challenges with CloudFlare

CloudFlare offers a great API, even on its free tier, so I decided to write a hook to automate the necessary updates. Since I’m that way inclined too, I’ve made it freely available, hosted here.

Basically, it’s a NodeJS script that runs through the following logic:

  • Given a domain and a validation code
  • Get a list of the users’ domain zones from CloudFlare and find which one is appropriate for this domain
  • Check that zone to see if we already have an ‘_acme-challenge’ TXT record for the domain
  • If we don’t create it. If we do, update it. In both cases, set it to the validation code provided.
  • Query DNS and see if it’s updated yet and if not, look at the TTL for the query and wait that long plus a 10-second buffer and try again up to a configurable number of times until it is updated.
  • Done.

This basically sets up the new validation information and waits until it is deployed, then returns allowing certbot to do it’s check and provided that succeeds, get the newly issued certificates. Now, of course, I’m simplifying a few things. It’s not certbot doing ALL the validation etc, but between certbot and the server they work it out; the finer detail isn’t too important for us just here.

What you get in the end is the ability to run a command like the following, and have it automatically manage the DNS-01 challenge setups required, which means it can be put into a cron job and not require manual intervention every couple of months!

sudo certbot renew --manual --manual-auth-hook "/path/to/node /path/to/hook.js"

As it turns out, the hook can also be used for certonly certificate issuing; it creates and manages validation just as well as the renew does. There is more detail and examples for installation and usage in the readme file in the code repository so if you want to check it out that’s a good next place to look.

Summary

Wow, that was a lot longer way of saying “I made a script to support DNS-01 challenge automation on certbot manual renewals” than I expected…

Anyway, please feel free to check it out, have a look at the readme which has some helpful info on getting it going, and I’ll try to address any questions or issues that come up. It’s far from perfect, but it works well for me so for now, it’ll do. Perhaps in the future I’ll try and improve any error handling etc. but no doubt that’ll come if I start seeing more errors I need to handle!

If you manage a website and you don’t have it secured with SSL, DO IT!!! There really is no good reason not to anymore.

Thanks for reading!

Leave a Reply

Your email address will not be published. Required fields are marked *