Temporarily and automatically changing firewall rules to permit Lets Encrypt certificate renewals
Another niche one.
I have some webservers for which I want a valid, not self-signed, TLS certificate.
Simple, thanks to Let’s Encrypt.
But I do not want to expose the web server to the world most of the time.
I also want my TLS certs renewed automatically (part of the Let’s Encrypt design, to encourage good hygiene), so a solution which requires me to log in to change anything is not an option.
Trickier.
So what I need is an ability for Let’s Encrypt to automatically reconfigure my firewalls to allow access for the duration of a renewal attempt, and then close that access afterwards.
Fortunately, this is eminently achievable, bringing in a few bits:
certbot
for the Let’s Encrypt renewals- bash scripts for pre and post renewal-hooks for the certbot renewals
- an application profile for
ufw
to go in the bash script - a curl command for the network firewall
Working out the firewall config changes
There are two firewalls I need to configure: the machine’s local firewall (ufw
), and the network-level firewall.
Each needs to (temporarily) allow tcp on ports 80 and 443.
ufw / local firewall
To automate changes to ufw, I created a ufw application profile:
[letsencrypt]
title=access to the world for Let's Encrypt
description=access to the world for Let's Encrypt
ports=80/tcp|443/tcp
This goes in /etc/ufw/applications.d/ufw-letsencrypt
.
I can trigger this using ufw
from the command line:
ufw allow letsencrypt
to enable it.
ufw delete allow letsencrypt
to delete it.
Network firewall
My network firewall (a FireBrick) supports profiles, and those profiles can be adjusted by a suitably-privileged user, via curl
.
So what I needed was a profile which was a simple toggle (on/off), which I applied to a firewall rule permitting 80/443 tcp.
Let’s Encrypt renewal-hooks
You can run scripts automatically when Let’s Encrypt attempts a renewal, by putting them in the relevant directory.
For me, this is /etc/letsencrypt/renewal-hooks
, and either pre
for scripts to run before renewal, and post
to run afterwards (whether successful or not).
In pre
, I have a bash script which contains just the ufw allow letsencrypt
command, and a curl
toggle for the network firewall.
In post
, it runs ufw delete allow letsencrypt
and the curl
toggle to close the network firewall.
Testing it
certbot
has a convenient testing mechanism, to check it all works from a Let’s Encrypt perspective:
certbot renewal --dry-run
This mimics a renewal, including the pre- and post- hooks, but without actually doing a renewal.
(In addition, it’s worth checking the ufw rules afterwards, to make sure that what you expect to be deleted is deleted. You might also run the post script as a cronjob, just as a backup, although there’s a risk it could interfere with a genuine Let’s Encrypt renewal.)