I’ve been looking into migrating to Let’s Encrypt for a while now, but due to my server setup for some reason the webroot method just wasn’t working for me (and is ugly in general). For a very long time I’ve always validated my domains ownership via a DNS TXT record and I find that much more elegant (personal preference).
I didn’t realize Let’s Encrypt had a
--preferred-challenges dns option, which as far as I know only works with the manual plugin. I tried it. It was ugly. The reason being it was, obviously, a manual process. Therefore every time you run the
certbot command you need to manually edit your TXT record with the value that will be generated.
I usually tend to stay away from third-party solutions, but from the sparse documentation I’ve seen dehydrated seemed like the way to go. It seems pretty well vetted so I’ll go with that. Here’s how my setup ended up looking like.
git clone https://github.com/certbot/certbot.git /opt/certbot git clone https://github.com/lukas2511/dehydrated.git /opt/dehydrated # I lied, I first forked the above repo and cloned my fork because no real reasons cd /opt/dehydrated git clone https://github.com/NatsumiHoshino/letsencrypt-cloudflare-hook.git hooks/cloudflare pip install -r hooks/cloudflare/requirements.txt
Next, I simply need to create a bash script for each domain I want to validate. It would look something like:
#!/usr/bin/env bash export CF_EMAIL='<cloudflare email>' export CF_KEY='<cloudflare key>' /opt/dehydrated/dehydrated \ --cron \ --domain mydomain.example.com \ --challenge dns-01 \ --hook '/opt/dehydrated/hooks/cloudflare/hook.py'
Then this script can be run on as a service (my preferred method) or as a cron job.
However this is where it gets interesting. I have some devices on which it is not easy to install the Let’s Encrypt client, such as my router or printer (because why not?). However, with this approach, I can validate ANY domain I own. For my router, I append the following to the above script:
if [ /mnt/my-cifs-share/cert.pem -ot /opt/dehydrated/certs/router.example.com/fullchain.pem ]; then logger "[certbot] A new cert has been created, uploading" cp -f /opt/dehydrated/certs/router.example.com/fullchain.pem /mnt/my-cifs-share/cert.pem cp -f /opt/dehydrated/certs/router.example.com/privkey.pem /mnt/my-cifs-share/key.pem else logger "[certbot] Cert still valid" fi
What this does is copy the new certificate and private key to a mounted share if the file is newer, in other words if a certificate renewal has occurred. On the router side, I handle the remaining tasks:
#!/bin/sh if [ /cifs1/cert.pem -nt /tmp/etc/cert.pem ]; then logger "[certbot] New cert found, updating" cp -f /cifs1/cert.pem /tmp/etc/cert.pem cp -f /cifs1/key.pem /tmp/etc/key.pem tar -C / -czf /tmp/cert.tgz /tmp/etc/cert.pem /tmp/etc/key.pem nvram setfb64 https_crt_file /tmp/cert.tgz nvram commit logger "[certbot] Restarting httpd" service httpd restart rm /tmp/cert.tgz else logger "[certbot] Cert valid, exiting" fi
I have this script scheduled to run twice daily and it checks for a new certificate in the mounted share. If the certificate has been renewed it will copy the new files over, save the new certificate to nvram, and restart httpd. As usual though I’ll have to wait until the next renewal to see if things go as planned…