guest@mukunda.com:/blog# ./cat 2022/certbot-with-dns-challenge.txt
Name: Certbot with DNS Challenge
Date: July 23rd, 2022
Over time with my test environments I've slowly improved my
SSL certificate setup. It comes naturally when you face odd
problems during testing due to certificate validation
errors, especially when the product you're dealing with
keeps quiet about connection failure reasons.
At some point I wrote a few scripts to easily issue
certificates to test domains from a selfmade CA certificate,
but the problem with this approach is issues with revocation
lists (since the CA doesn't really exist) and getting the
root certificate installed on client machines. Just a whole
lot of hassle when testing.
So, today I took the time to get a proper testing setup.
Firstly, a real domain--I'll use mukunda.com, even for my
local machines. Experienced admins will always warn you
against using fake domains or TLDs for testing, and I've
pretty much learned the hard way about .local, .lan, and
other fake domains.
Let's Encrypt provides automated certificate signing via
the ACME protocol. With Certbot, you can script interaction
with Let's Encrypt to automatically generate and update
certificates.
I'll be making a wildcard certificate for *.mukunda.com.
Once certbot is installed, it's pretty straightforward, but
there are a lot of modules to make things easier for certain
setups or web servers. For this case, I just want the basic
certificates for testing purposes.
I'm on Windows, and I use it like this:
certbot certonly --manual `
--manual-auth-hook "C:\me\certbot-auth-hook.py" `
-d *.mukunda.com
This starts a wildcard certificate generation process. First
you will get the ACME challenge, and you need to update a
DNS record to satisfy it. Certbot ships with plugins that
handle DNS updates for many hosts, but I didn't see one for
Dreamhost. For Dreamhost, I wrote a small script to do the
update via their API.
The --manual-auth-hook option tells certbot to call a
custom script to perform the DNS updates. It sets a few
environment variables for the script to handle, mainly:
CERTBOT_DOMAIN - The domain being challenged.
CERTBOT_VALIDATION - The validation string to apply.
My script looks something like this, where it basically
updates my domain and then waits for the DNS propagation
change (which certbot does not handle).
<...>
certbot_domain = os.environ.get("CERTBOT_DOMAIN")
certbot_validation = os.environ.get("CERTBOT_VALIDATION")
print("Domain:", certbot_domain)
print("Validation string:", certbot_validation)
if certbot_domain == "mukunda.com":
update_dreamhost_dns("_acme-challenge.mukunda.com", "TXT", certbot_validation)
print("Sleeping for 10 minutes to allow DNS to propagate.")
time.sleep(600)
else:
print("Unhandled domain:", certbot_domain);
If you want a reference to the full script, see here.
While developing and testing, you can check the
letsencrypt.log file to see any errors that your script
generated during the last attempt.
Keep in mind that there are limits enforced for ACME
challenges from Let's Encrypt, mainly that you can only make
5 requests per hour, so you want to work out any script
testing before making actual attempts.
Once it succeeds, your crisp certificates will be saved to
the default location. The domain generation parameters are
saved in the certbot data folder under "renewal", so you can
simply run
certbot renew
to re-run any commands to renew your certificates. The
certificates otherwise expire after 3 months.
On Windows, certbot automatically adds a system task to
renew periodically. Here's the final output:
Send the author a comment
<< Index