I’m hosting my own mail server to receive mail for the domains I own. My personal mail is running somewhere else. It’s the last public server I’m operating out there for my own purposes and it’s more for training purposes. Most of the ports of this server are hidden: By my own firewall on the server but by the firewall of the provider as well. It’s relatively low maintenance and so far I was too lazy to migrate everything to a different server, giving the task of managing the mail server away. I did automate most of the tasks managing the system. Except one.
The only non-automated task and the one that was really annoying to me was the renewal of the certificates for TLS. I’m using Let’s Encrypt to obtain the certificates. So it’s a task I have to do every 90 days.
BTW: Soon they will offer certificates only valid for a few days and I think I will use them. In this light, no automation is not an option.
There are two ways to confirm that you are in control of the domain you want to get the certificate for:
- Proof of having control of the DNS configuration by adding a TXT record with a challenge.
- Or putting a file in a directory on your webserver and Let’s Encrypt checks for this file.
But my mail server had just a webserver for admin interfaces and so there was no need to have a public webserver. What you can’t reach is less probable to be an attack vector. This is the reason why I used firewall rules to block public access to the webserver and different means to access port 80/443 on the mail server (it was really easy when I had a fixed IP but my current ISP doesn’t offer this for consumers).
So the servers of Let’s Encrypt couldn’t reach a webserver on my mail server to validate the domain ownership when requesting a certificate. I have chosen the TXT record as proof of control as it didn’t need any service on the server. However — most GUIs of domain name servers are a major pain and don’t have a decent way to automate stuff. For quite some time I did this renewal process manually every 90 days. And more than once I forgot to do this.
Until I had enough of this. I was frustrated about my laziness to fix this. I really thought about getting an additional domain at a service supporting the automated mechanisms of Let’s Encrypt to insert the TXT record automatically. To my excuse: My job and another hobby I acquired 2 years ago took a lot of brain cycles.
But I still didn’t want to make the webserver on the system publicly accessible, not even for a short time. My logfiles would be gargantuan already without rotating because of all the attempts to brute force passwords on the mail server, and even more so without fail2ban doing its job. So I have no hope this would be a good idea to keep it open. It just looked prudent to have no public port 80/443 there. Would be another source of log entries. I just don’t want to wade through another long logfile. I know there are tools for this but this would be yet another tool I have to maintain.
However, two things occurred to me: “I’m the only person using this webserver” and “I just need the Let’s Encrypt stuff for a few seconds every 60 days”. Perhaps I could solve this with a horrible hack.
What the process is doing:
- Shut down your webserver.
- Open up the firewall on port 80.
- Renew the certificate in standalone mode (so
certbotis using its own webserver). - Close the firewall on port 80.
- Restart your webserver.
It’s quite easy to set up:
# certbot certonly --standalone --pre-hook "perl -e 'sleep int(rand(3600))'; systemctl stop apache2; ufw allow http" --post-hook "ufw deny http; systemctl start apache2" -d example.co`
From now on the automatic processes will do their job as indicated by the output of the command:
Certbot has set up a scheduled task to automatically renew this certificate in the background.
In /etc/letsencrypt/renewal/example.com.conf you will find the pre-hook and post-hook lines with the commands you have specified by command line options before.
pre_hook = perl -e 'sleep int(rand(3600))'; systemctl stop apache2; ufw allow http
post_hook = ufw deny http; systemctl start apache`
Seems to work reasonably well so far — as long as your webserver must not stay online all the time, of course.