LetsEncrypt wildcard certificates and Bind9 auto update with DNS challenge

SSL is the standard in web content serving nowadays and LetsEncrypt does a wonderful job offering FREE SSL certificates that would otherwise cost you a decent amount of money on a yearly basis. The only downside to the FREE service is that these certificates have a very short 90 days life and, as such, they do require frequent renewal.

I will not go into how easy it is to set-up and automatically renew your certificate if you are using just a single domain, single site setup on your Linux server because it truly is very simple.

However, when you add to the situation at hand multiple virtual hosts (multiple domains hosted), subdomains to these hosts as well as mail server and why not an IMAP server … you get to a really hairy situation. If you, on top of that are sadistic enough to run your own DNS Server (Bind) well … you are in an even harder situation because LetsEncrypt process for renewing with wildcards and auto updating DNS challenge on Bind is not straight forward.

Working assumptions
  • You are running an Apache Web Server and need ONE certificate to cover all your domains (including any subdomains)
  • You are running Bind 9 DNS Server (either locally or on a different machine)
  • You need free SSL Certificates from LetsEncrypt and want to automate the renewal process

I might skip some of the steps required for installation but I am assuming that you have at least some previous knowledge of Linux and thus I apologize if steps below are not 100% complete.

Installing certbot

In order to get LetsEncrypt free certificates running you first need to have Certbot installed. If you previously installed it from the OS package list … tough luck, please remove it (sudo apt-get remove certbotsudo dnf remove certbot, or sudo yum remove certbot )

Now install it the “proper” way via snap. If you’re running CentOS like me, make sure you enable the EPEL repo first:

sudo yum install snapd
sudo systemctl enable --now snapd.socket
sudo ln -s /var/lib/snapd/snap /snap

Once Snap is installed, proceed to install the Certbot:

sudo snap install --classic certbot

;also add a symbolic link to it so you can run it from anyehere
sudo ln -s /snap/bin/certbot /usr/bin/certbot

You are now one step closer to getting your free SSL certificate!

installing certbot plugin for bind9

Since you are running Bind9 and, since for wildcard certificates LetsEncrypt requires a DNS challenge, you will also need to install the optional certbot plugin that will enable you to automatically create the _acme-challenge TXT record for each of the domains in the certificate. The plugin is completely unintuitively called: DNS-RFC2136.

; IMPORTANT! this next command must be run first before trying to install the plugin, or it will NOT WORK
sudo snap set certbot trust-plugin-with-root=ok
; then you can go ahead and install the plugin
sudo snap install certbot-dns-rfc2136

To verify plugin is installed and ready to work please run the following command:

certbot plugins

/// OUTPUT ///
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.....
* dns-rfc2136
Description: Obtain certificates using a DNS TXT record (if you are using BIND
for DNS).
Interfaces: Authenticator, Plugin
Entry point: EntryPoint(name='dns-rfc2136', value='certbot_dns_rfc2136._internal.dns_rfc2136:Authenticator', group='certbot.plugins')


Time to now move on to setting-up Bind for accepting automatic changes.

creating a tsig key for bind

In order for an automated process, we need to have a key generated with bind-utils so that Bind can later be set-up to accept DNS modifications with the specific key. Let’s generate a stronger KEY using HMAC-SHA512 algorithm:

sudo tsig-keygen -a HMAC-SHA512 certbot

// Output:
key "certbot" {
        algorithm hmac-sha512;
        secret "iX1mbqHe/H7QnNOuGcX8iYdjTZCIj2eMYWHyDqLw0P5qEBqL35/2Uvup2tqlAMKRC3NlbSmKAFprjyQcCt3fEw==";
};

Save this key somewhere as you will need it shortly. Each time you run the above command you will get a new key/secret pair so don’t worry, the above is not used in a live production environment 😉

Add the key to bind

In order for this key to be recognized, we need to add it to the “PublicView” view in Bind configuration (typically found in /etc/namedb/named.conf)

view "PublicView" {
           ...
           key "certbot" {
                     algorithm hmac-sha512;
                     secret "iX1mbqHe/H7QnNOuGcX8iYdjTZCIj2eMYWHyDqLw0P5qEBqL35/2Uvup2tqlAMKRC3NlbSmKAFprjyQcCt3fEw==";
           };
           ....
}

It may be that your PublicView is named differently so just make sure you add the key to the right external DNS master zone.

Allowing the key to update zones

Now that Bind knows of this key’s existence it is time to tell each zone that it can be modified by it and, for security reasons, to instruct each zone to only accept _acme-challenge TXT records modifications. This greatly limits risks, even if the key gets into the wrong hands.

As mentioned, we are running multiple domains (zones) hence each of them needs to be updated in the same /etc/namedb/named.conf

zone "yourdomain.com" {
     type master;
     file "/etc/namedb/master/PublicView/yourdomain.com.DB";
     allow-query { any; };
     allow-transfer { localhost; localnets; };
     update-policy { grant certbot. name _acme-challenge.yourdomain.com. TXT; };

     .... etc ....
};

zone "otherdomain.com" {
     type master;
     file "/etc/namedb/master/PublicView/otherdomain.com.DB";
     allow-query { any; };
     allow-transfer { localhost; localnets; };
     update-policy { grant certbot. name _acme-challenge.otherdomain.com. TXT; };

     .... etc ....
};

zone "anotherdomain.com" {
     type master;
     file "/etc/namedb/master/PublicView/anotherdomain.com.DB";
     allow-query { any; };
     allow-transfer { localhost; localnets; };
     update-policy { grant certbot. name _acme-challenge.anotherdomain.com. TXT; };

     .... etc ....
};
}

These are 3 domains here … but it can be 100 for all I know. It doesn’t matter as long as you make sure they all follow the same logic. Do mind the punctuation like for the little dot following the key name certbot. and the little dot after your domain name in the update-policy. Otherwise … things won’t work.

Creating a configuration for your DNS-RFC2136 plugin

Now create a configuration file which will be read by the certbot plugin and save it.

/etc/letsencrypt/rfc2136.ini

# Target DNS server | Your BIND server
dns_rfc2136_server = 192.168.x.x
# Target DNS port
dns_rfc2136_port = 53
# TSIG key name
dns_rfc2136_name = certbot
# TSIG key secret
dns_rfc2136_secret = iX1mbqHe/H7QnNOuGcX8iYdjTZCIj2eMYWHyDqLw0P5qEBqL35/2Uvup2tqlAMKRC3NlbSmKAFprjyQcCt3fEw==
# TSIG key algorithm
dns_rfc2136_algorithm = HMAC-SHA512

We’re almost there 🙂

deploy script hook for letsencrypt

I mentioned that you can use the same certificate for Apache but also for securing your Postfix email as well as IMAP service. Since I assume you already know how to set-up encryption for them (and it is not the scope of this article) and, since Letsencrypt will always generate the same filename for the newly created certificate, it is advisable to let certbot know that it should restart the required services once a new certificate is deployed.

Create a bash script in /etc/letsencrypt/renewal-hooks/deploy/ called restart-services.sh

#!/bin/bash
# Path: /etc/letsencrypt/renewal-hooks/deploy/restart-services.sh

# Restart httpd
systemctl restart httpd

# Reload postfix
systemctl restart postfix

# Reload dovecot
systemctl restart dovecot

and make sure you make it executable

sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/restart-services.sh
Get your free Letsencrypt certificate

Having done all of the above, you should now be able to get your free certificate:

certbot certonly --dns-rfc2136 --dns-rfc2136-credentials=/etc/letsencrypt/rfc2136.ini --dns-rfc2136-propagation-seconds 30 --deploy-hook /etc/letsencrypt/renewal-hooks/deploy/restart-services.sh -d yourdomain.com -d *.yourdomain.com -d otherdomain.com -d *.otherdomain.com -d anotherdomain.com -d *.anotherdomain.com

A few explanations:

  • certonly – is used since you don’t want to install it. It will be saved on the server in a specific location from where all your services can pick it up
  • –dns-rfc2136 – tells certbot that it needs to use the Bind plugin
  • –dns-rfc2136-credentials=/etc/letsencrypt/rfc2136.ini – tells certbot where to get the credentials for Bind update (remember that in this file we’ve already saved the ip of the nameserver as well as the key name, secret and algorithm
  • –dns-rfc2136-propagation-seconds 30 – tells certbot to wait 30 seconds before attempting to verify the challenge TXT record. This should be sufficient time for zone changes to propagate. If you encounter difficulties, you can increase this value to 60 or 120seconds.
  • –deploy-hook /etc/letsencrypt/renewal-hooks/deploy/restart-services.sh – tells certbot to run restart-services.sh as soon as the new certificate is received. This way, all your services, which depend on the letsencrypt SSL certificate, will automatically restart with the new certificate loaded.
  • the list of -d yourdomain.com -d *.yourdomain.com is basically requesting (within the same certificate) both a wildcard and a domain only certificate. This way www.yourdomain.com, yourdomain.com, any_subdomain.yourdomain.com will be covered, along with the other domains in the same manner.

If all went well (which it should), the output should be:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/[servername]/yourdomain.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/[servername]/yourdomain.com/privkey.pem
This certificate expires on 2024-04-24.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

That’s it! You’re good to go. As a plus certbot should try to automatically renew the certificate, as it automatically adds a system timer to check for certificate getting close to expiration.

Do consider a donation to Let’s Encrypt for providing such a wonderful open service!


Posted

in

, ,

by