Renewing Let's Encrypt Wildcard Certificates

Background

Let’s Encrypt is a service known for how easy it is to enable SSL on websites (which you must know if you’re reading this article about renewing!). Earlier this year Let’s Encrypt added support for wildcard certificates, and with the added complexity of supporting multiple subdomains on one certificate came added complexity to renew these certificates.

The other day I encountered what looks like a common error when trying to renew one of my wildcard certificates. On running certbot renew I got this error:

Attempting to renew cert ($SUBDOMAIN.$DOMAIN) from /etc/letsencrypt/renewal/$SUBDOMAIN.$DOMAIN produced an unexpected error: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.',). Skipping.
All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/$SUBDOMAIN.$DOMAIN/fullchain.pem (failure)

Yikes. This is a lot of text to say the renewal didn’t work! Let’s break this error down.

The first line is simply saying there is a problem with the manual renewal plugin. It mentions the renewal config to let’s take a look at that. The second half of the renewal file is what we’re interested in:

[renewalparams]
account = $ACCOUNT
server = https://acme-v02.api.letsencrypt.org/directory
authenticator = manual
pref_challs = dns-01,
manual_public_ip_logging_ok = True

This config was generated when you run the command to request a certificate. In my case (if you’re using a wildcard certificate your case is probably similar) the command was sudo certbot certonly --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory -d $DOMAINS

What matters is what we used a manual authenticator, so let’s remember that. However, we should give certbot’s manpage a look to decipher all of this, so,we’ll do that in the next section.

Enough talk, what’s the problem?

Ok, ok, I hear ya. Let’s look at the manpage for certbot quick.

Look at the —manual flag for certbot. The flag specifies that it will “[O]btain certificates interactively, or using shell script hooks.” Well if we look at the —force-interactive flag we see that it cannot be used with certificate renewal! So, the overall problem we have is that renewal with the —manual flag must use a shell script hook, but we didn’t specify any!

What is interactivity anyways?

In our case, interactivity is when we involve the user in the certificate deployment or renewal process. Think back to when we originally verified our domain. Remember certbot asking us to deploy a code to, in the case of DNS verification, a TXT record before pressing enter to continue? This process with the user is interactivity.

Interactivity is important because when we use the manual plugin, certbot has no idea how to deploy proof of ownership.

Fixing the problem

So now that we know what’s causing the problem, we can finally fix it! I’m going to attempt to replicate the default (interactive) behavior, but there are many ways you can choose to solve this. For the sake of brevity I’m not going to go into detail on them, but two ideas are to either change the renewal config from using the manual plugin, or to create a script to do the renewal for you.

So, here’s what I did to ”fix” the problem. I created a script (shown below) which will create a file with the title of the validation token. When the script pauses, I either open a new TTY or SSH session and copy this token1. I then update my TXT record to match this token, and then let the script continue. If everything worked, you will have a few new certificates waiting for you!

#!/bin/bash
touch $CERTBOT_VALIDATION
echo $CERTBOT_VALIDATION  
read -n 1 -p "Continue? [y/n]" continuebool

  1. My original intent was to have the token write to stdout, hence the echo statement. However, I wasn’t able to get it to print before continuing with the validation. ↩︎