The wonderful Let's Encrypt makes it easy to easy to generate and install SSL certificates for free using the certbot ACME client on a webserver. But what if, like me, you want to generate and install your certs on a server behind a firewall so that you can use subdomains in your home network while keeping that network private.

Yep, they make that easy too because certbot supports dns challenge as a method of verifying your ownership of a domain and provides dns plugins for every major DNS provider.

How DNS challenge works with certbot

The certbot docs provide a much better explanation of this than I ever could but TLDR:

  1. Certbot requests a certificate from the Let's Encrypt servers
  2. Let's Encrypt servers return challenge contents
  3. Plugin creates a TXT DNS record containing the challenge contents
  4. Let's Encrypt servers validate the TXT DNS record
  5. Let's Encrypt servers issue certificate

Let's use docker

In my case I use Cloudflare as my DNS provider and I'm going to generate the cert on my trusty Synology NAS. Now I could manually install certbot, it's dependencies and the Cloudflare plugin, but the Synology has Docker installed and there's a Docker image for the Cloudflare plugin so that's much simpler.

Getting started

The docker image needs to match a couple of letsencrypt volumes inside /var/lib and /etc/ so we'll start by creating them.

sudo mkdir /etc/letsencrypt
sudo mkdir /var/lib/letsencrypt

Set up Cloudflare credentials

To enable certbot to create TXT records via the Cloudflare API we'll need to create an API token via the cloudflare dashboard. We choose to create a token and select the Edit zone DNS template and create the token for my domain.

Create token screenshot
This token can edit your DNS, treat it like you would your password!

Now we have the token let's create a cloudflare.ini file that certbot will read the token from.

sudo vi /etc/letsencrypt/cloudflare.ini

The file contents should have the following format.

# Cloudflare API token used by Certbot
dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567

Now we can run the docker container passing in the location of the ini file containing the api token and the name of the domain to generate a certificate for. I'm using * to generate a wildcard cert so I can use this cert for lots of different subdomains in my local network.

sudo docker run -it --rm --name certbot \
            -v "/etc/letsencrypt:/etc/letsencrypt" \
            -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
            certbot/dns-cloudflare \
            certonly --dns-cloudflare \
                     --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
                     -d *

Once this command completes the cert can be found in /etc/letsencrypt/live/$domain where $domain is the name of the domain the cert was generated for, so for me that's /etc/letsencrypt/live/ Now we can grab that cert and install it wherever we want to have SSL on a subdomain.

Next steps

Let's Encrypt certs are valid for 3 months so I'll need to setup some way to automate renewal and find a more secure place to store the Cloudflare API key.