Automating Certbot DNS Challenge Renewals for SSL Certificates Without Server Integration


3 views

When initially creating certificates using certbot -d example.com --manual --preferred-challenges dns certonly, we're essentially opting out of automated renewals. The manual method requires human intervention for each DNS challenge, which becomes tedious when managing multiple domains.

After initial setup, your renewal/ directory contains config files with:

authenticator = manual
pref_challs = dns-01

This configuration means running certbot renew won't work automatically - it will still prompt for DNS entries. Your bash script approach is currently necessary but not ideal for automation.

For new certificates using pure DNS challenge (without server integration):

certbot certonly --manual \
  --preferred-challenges=dns \
  --manual-auth-hook /path/to/dns-authenticator.sh \
  -d example.com -d *.example.com

This requires creating an authentication hook script that handles your DNS provider's API.

Here's a complete solution using Cloudflare API as example (adapt for your DNS provider):

#!/bin/bash
# dns-authenticator.sh

# Cloudflare API credentials
CF_EMAIL="your@email.com"
CF_API_KEY="your_api_key"
CF_ZONE_ID="your_zone_id"

# Get domain from Certbot
DOMAIN=$(expr match "$CERTBOT_DOMAIN" '.*\.$.*\..*$')
[ -z "$DOMAIN" ] && DOMAIN="$CERTBOT_DOMAIN"
SUBDOMAIN=$(echo "$CERTBOT_DOMAIN" | sed "s/\.$DOMAIN//")

# Add TXT record
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
  -H "X-Auth-Email: $CF_EMAIL" \
  -H "X-Auth-Key: $CF_API_KEY" \
  -H "Content-Type: application/json" \
  --data "{\"type\":\"TXT\",\"name\":\"_acme-challenge.$SUBDOMAIN\",\"content\":\"$CERTBOT_VALIDATION\",\"ttl\":120}"
  
# Propagation delay
sleep 25

For existing certificates, update renewal configs in /etc/letsencrypt/renewal/:

# Change from:
authenticator = manual
# To:
authenticator = manual
manual_auth_hook = /path/to/dns-authenticator.sh
pref_challs = dns-01,

Finally, set up a cron job:

0 0,12 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"

This checks twice daily and only acts when certificates are near expiration.


When working with Let's Encrypt certificates obtained through manual DNS challenges, the renewal process has some unique characteristics:

# Sample renewal configuration file (renewal/example.com.conf)
authenticator = manual
pref_challs = dns-01
manual_public_ip_logging_ok = True

The critical point is that certbot renew won't automatically handle DNS challenges for manual authentications. You'll still need to:

  1. Create DNS TXT records manually
  2. Wait for propagation
  3. Confirm the challenge completion

For initial certificate creation with DNS challenge:

certbot certonly \
  --manual \
  --preferred-challenges dns \
  -d example.com \
  -d *.example.com \
  --manual-public-ip-logging-ok \
  --agree-tos

This will prompt you to add DNS TXT records for each domain. The format will look like:

_acme-challenge.example.com. 300 IN TXT "gfj9Xq...Rg85nM"

Your script approach is fundamentally correct, but needs enhancement for production use:

#!/bin/bash
DOMAINS=("example.com" "test.example.org")

for domain in "${DOMAINS[@]}"; do
  echo "Processing ${domain}"
  certbot certonly \
    --manual \
    --preferred-challenges dns \
    -d "${domain}" \
    --manual-public-ip-logging-ok \
    --non-interactive \
    --manual-auth-hook /path/to/auth-hook.sh \
    --manual-cleanup-hook /path/to/cleanup-hook.sh
done

Create auth-hook.sh to handle DNS updates automatically through your provider's API:

#!/bin/bash
DOMAIN=$(echo "$CERTBOT_DOMAIN" | sed -r 's/.+\.(.+\..+)/\1/')
SUBDOMAIN=$(echo "$CERTBOT_DOMAIN" | sed -r 's/(.+)\..+\..+/\1/')

# Cloudflare API example
curl -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "{\"type\":\"TXT\",\"name\":\"_acme-challenge.${SUBDOMAIN}\",\"content\":\"${CERTBOT_VALIDATION}\",\"ttl\":120}"

For enterprise environments:

  • Store API credentials securely (Vault/AWS Secrets Manager)
  • Implement proper error handling in hooks
  • Set up monitoring for renewal failures
  • Consider using certbot's renewal hooks for post-processing
# /etc/letsencrypt/renewal-hooks/post/restart-services.sh
systemctl reload nginx