Earlier in the year I wrote about getting Let’s Encrypt on my router where I grab a certificate from another system and drop it in a shared folder. That solution was…not ideal, and recently SMB shares stopped mounting on the router with some obscure CIFS error code. I suspect it has something to do with me disabling SMBv1 everywhere (for good reasons).

Turns out that whole drop it in a shared folder and retrieve it on the other system thing was overly complicated for nothing. The current solution I am using is much simpler: have the remote server use good old scp to push the certificate to the router, then pipe the update script via ssh to the router. This requires no scheduled script on the router, and the certificate is updated as soon as certbot spits out a new certificate.

First, let’s head back to where we previously installed dehydrated in /opt/dehydrated and edit our CloudFlare script. Note that at the time of writing, it looks like the official Certbot client is adding built-in support for CloudFlare, meaning the dehydrated method might no longer be necessary.

#!/usr/bin/env bash
export CF_EMAIL='<cloudflare email>'
export CF_KEY='<cloudflare key>'

/opt/dehydrated/dehydrated \
        --cron \
        --domain router.example.com \
        --challenge dns-01 \
        --hook '/opt/dehydrated/hooks/cloudflare/hook.py'
EPSILON=300
CERT=/opt/dehydrated/certs/router.example.com/fullchain.pem
KEY=/opt/dehydrated/certs/router.example.com/privkey.pem
TIMESTAMP=$(stat $CERT -c %Y)
DIFF=$(expr $(date +%s) - $TIMESTAMP)
if [ $DIFF -lt $EPSILON ]; then
        logger "certbot] A new cert has been created, uploading"
        scp -i /path/to/id_file $CERT [email protected]:/tmp/etc/cert.pem
        scp -i /path/to/id_file $KEY [email protected]:/tmp/etc/key.pem
        logger "[certbot] Triggering certificate update on remote"
        ssh -i /path/to/id_file [email protected] 'sh -s' < /opt/dehydrated/router.example.com.update.sh
else
        logger "[certbot] Cert valid"
fi

The contents of router.example.com.update.sh being the same as the scheduled script that was previously running on the router, except that now it is triggered from the remote server.

#!/bin/sh

logger "[certbot] New cert found, updating"
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

Using root isn’t really ideal, but this is running from the local network so I guess that can be accepted.

Of course, even easier would be to run the certbot client locally on the router via Entware/Optware if the router supports it, but this method only requires the remote server to have ssh and scp so it could technically work for pushing certificates to a printer’s web admin server…