Certbot with Cloudflare DNS using DNS-01 challenge in Docker
Let's Encrypt will issue you free SSL certificates, but you have to verify you control the domain, before they issue the certificates.
The 2 major ways of proving control over the domain:
- Create a specific page on your webserver that they can reach.
- Create a temporary DNS TXT record.
I went with option #2, as my web server(s) aren't exposed to the internet, and I didn't feel like leaving a hole punched in my firewall on ports 80/443, to use Certbot.
I use Cloudflare for my DNS needs, and they have an API that allows the temporary DNS TXT records to be created/deleted.
Steps
1) Create an API Token from Cloudflare:
-
Under API Tokens, select Create Token
-
Select Use template for Edit zone DNS
- Under Zone Resources:
-
Include
-
Specific Zone
-
Select the domain we want to use for DDNS
-
This step is optional. If skipped, this API token will have permissions for all of your Cloudflare domains.
-
- Under Zone Resources:
-
Under TTL, select Start/End dates, or leave untouched for no expiration of these permissions.
-
Once generated, copy the API token and treat it like a password.
-
Testing the API token, given the
curl
command that Cloudflare provides:curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ -H "Authorization: Bearer NS4ZC3Y6MTJD0IXPIM4QJ3L8BCF-BRDYX7JZ0VQW2M" \ -H "Content-Type:application/json" {"result":{"id":"amlf9mwd02v1t837dy3bhb1rlsuuhaw2c","status":"active"},"success":true,"errors":[],"messages":[{"code":10000,"message":"This API Token is valid and active","type":null}]}%
2) Create a credentials file on your Docker host:
mkdir cloudflare
echo 'dns_cloudflare_api_token = "NS4ZC3Y6MTJD0IXPIM4QJ3L8BCF-BRDYX7JZ0VQW2M"' > cloudflare/credentials
cat cloudflare/credentials
dns_cloudflare_api_token = "NS4ZC3Y6MTJD0IXPIM4QJ3L8BCF-BRDYX7JZ0VQW2M"
3) Create the docker-compose.yml file:
version: "2"
services:
letsencrypt-cloudflare:
image: certbot/dns-cloudflare
# Dry Run
command: certonly --non-interactive --dns-cloudflare --dns-cloudflare-credentials /opt/cloudflare/credentials --agree-tos -d test.example.com --server https://acme-v02.api.letsencrypt.org/directory --dry-run
# Issue certificate
# command: certonly --non-interactive --dns-cloudflare --dns-cloudflare-credentials /opt/cloudflare/credentials --agree-tos -d test.example.com --server https://acme-v02.api.letsencrypt.org/directory
# Renew certificate
# command: renew --non-interactive --no-self-upgrade --dns-cloudflare --dns-cloudflare-credentials /opt/cloudflare/credentials --agree-tos --server https://acme-v02.api.letsencrypt.org/directory
volumes:
- ./cloudflare:/opt/cloudflare
- ./letsencrypt:/etc/letsencrypt
- ./letsencrypt/log:/var/log/letsencrypt
4) Perform a dry-run for the domain test.example.com:
docker-compose up
Starting certbot_letsencrypt-cloudflare_1 ... done
Attaching to certbot_letsencrypt-cloudflare_1
letsencrypt-cloudflare_1 | Simulating a certificate request for test.example.com
letsencrypt-cloudflare_1 | Waiting 10 seconds for DNS changes to propagate
letsencrypt-cloudflare_1 | The dry run was successful.
letsencrypt-cloudflare_1 | Saving debug log to /var/log/letsencrypt/letsencrypt.log
Notes:
- Uncomment/comment dry-run, issue certificate, or renew certificate lines as needed
- Debug logs, if/when needed, are in letencrypt/logs/ on the Docker host.
References
Let's Encrypt https://letsencrypt.org/
github.com - certbot / certbot https://github.com/certbot/certbot
certbot-dns-cloudflare - documentation https://certbot-dns-cloudflare.readthedocs.io/en/stable/
dockerhub - certbot - dns cloudflare https://hub.docker.com/r/certbot/dns-cloudflare