Automating Letsencrypt Wildcard Certificate renewal with Mail in a Box.

I use letsencrypt, a fantastic free service to secure my websites. However, like many people I have multiple sub-domains for my blogs, gitea and whatever else I fancy spinning up. These are all hosted on a separate box, not my MIAB box. So when Letsencrypt announced their wildcard certificate I jumped on board.
The only problem I had with wildcard certificates were the extra steps required automate the whole process. The issue was that Letsencrypt (or certibot, the program that does the work) required txt records to be updated in publicly available DNS records.
As I also use Mail in a Box for my public DNS and email I had to write some scripts and entries in crontab to automate the renewal process. Thankfully Mail-in-a-box has an API for doing exactly this.

Notes and Caveats:

  • I’ve seen other blog entries that do the same thing (minus the automation) for a single certificate. However when I generated my certs some time ago, I followed these instructions. As such my cleanup.sh script did not work as expected, so run it separately, not via the –manual-cleanup-hook call.
  • Read the documentation if you have problems: https://certbot.eff.org/docs/using.html#pre-and-post-validation-hooks
  • If you figure out how to make the cleanup script work as a hook – please let me know in the comments šŸ˜‰
  • I assume you already have certibot/letsencrypt installed
  • You will need to substitute your credentials in the scripts – I have a specific account I use for just this, so my personal email/admin account isn’t compromised with the password stored in plain text.
  • Make sure your scripts have the executable bit set (chmod +x).
  • My crontab entries will email root, which is aliased out, to my personal email address.
  • Cron jobs (see comments in scripts) will run at midnight and 5 minutes past midnight respectively, every 5 days.

The Scripts:

These are super basic:
/root/renewal.sh

#!/bin/bash
#add to crontab like this:
#0 0 */5 * * /root/renewal.sh | mail s "Lets Encrypt Certificate Renewal" root >/dev/null 2>&1
sudo certbot certonly -n --manual-public-ip-logging-ok --server https://acme-v02.api.letsencrypt.org/directory --manual --manual-authhook ./authenticator.sh --preferred-challenges dns -d "dom.ain, *.dom.ain"

/root/authenticator.sh

 #!/bin/bash

curl -s -X PUT -d "$CERTBOT_VALIDATION" --user user@dom.ain:<Password>  https://<your.maib.url>/admin/dns/custom/_acme-challenge.dom.ain/txt 

/root/cleanup.sh

#!/bin/bash
#Add to crontab:
#5 0 */5 * * /root/renewal.sh | mail -s "Lets Encrypt Certificate Renewal" root >/dev/null 2>&1
# get the txt record
TOREMOVE=`curl -s -X GET --user user@dom.ain:<password> https://<your_MIAB_Server>/admin/dns/custom/_acme-dom.ian/txt  | grep "value" | awk '{print $2}'| sed 's/"//g'`
echo "removing $TOREMOVE"
curl -s -X DELETE -d "$TOREMOVE" --user user@dom.ain:<password> https://<your_MIAB_Server>/admin/dns/custom/_acme-dom.ian/txt 
service apache2 restart

Finally, if you want to check the certificate run:

certbot certificates

How to configure a Unifi Controller behind an Apache Reverse Proxy with LetsEncrypt

Background:

I had to do quite a bit of searching in order to get Unifi to work correctly behind an Apache reverse proxy. I found that many people had come up with their own solutions with various odd, to say the least, configuration options in Apache that were mostly unnecessary. It took a little more searching, but eventually I did find how to prevent the WSS error from appearing too.

Before Beginning:

I assume that you have:

  • Already configured Apache and Lets Encrypt previously.
  • DNS already configured correctly and you can easily add another sub-domain.
  • Already installed and configured Unifi Controller on a box, or VM somewhere.

As Unifi runs on a high (+1024) port, I installed the controller directly onto my Apache2 server.

By the end of the process you should have a functional Unifi controller on unfi.domain.com

Configuration:

Before beginning, ensure that you’ve created a new sudomain and pointed it to your public IP. Next, use lets encrypt to expand your certificate file to include the new domain. I usually run this in standalone mode and turn off apache2 while expanding the certificate.

sudo service apache2 stop
sudo letsencrypt certonly -d unifi.domain.com -d www.domain.com -d subdomain.domain.com

Once complete, start apache again.

Create a new site in /et/apache2/sites-available/ called unfi.domain.com-le-ssl.conf
Edit the file to contain the text below. Be sure to edit the references to your SSL certificate files, document root, servername, etc and IP address of your unifi host. Be aware that my unifi controller runs on the same host as my apache server. If needed, you can get the lets encrypt information from one of your other sites configuration files.

<IfModule mod_ssl.c>
<VirtualHost unifi.domain.com:443>
 # The ServerName directive sets the request scheme, hostname and port that
 # the server uses to identify itself. This is used when creating
 # redirection URLs. In the context of virtual hosts, the ServerName
 # specifies what hostname must appear in the request's Host: header to
 # match this virtual host. For the default virtual host (this file) this
 # value is not decisive as it is used as a last resort host regardless.
 # However, you must set it for any further virtual host explicitly.
 #ServerName www.example.com

ServerAdmin webmaster@domain.com
# DocumentRoot /var/www/html

# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
 # error, crit, alert, emerg.
 # It is also possible to configure the loglevel for particular
 # modules, e.g.
 #LogLevel info ssl:warn

ErrorLog ${APACHE_LOG_DIR}/error.log
 CustomLog ${APACHE_LOG_DIR}/access.log combined

# For most configuration files from conf-available/, which are
 # enabled or disabled at a global level, it is possible to
 # include a line for only one particular virtual host. For example the
 # following line enables the CGI configuration for this host only
 # after it has been globally disabled with "a2disconf".
 #Include conf-available/serve-cgi-bin.conf
SSLCertificateFile /etc/letsencrypt/live/domain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/domain.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
ServerName unifi.domain.com

ProxyRequests Off
ProxyPreserveHost On

# HSTS (mod_headers is required) (15768000 seconds = 6 months)
Header always set Strict-Transport-Security "max-age=15768000"

<Proxy *>
Order deny,allow
Allow from all
</Proxy>

SSLProxyEngine On
SSLProxyVerify none

SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off

AllowEncodedSlashes NoDecode
ProxyPass "/wss/" "wss://127.0.0.1:8443/wss/"
ProxyPassReverse "/wss/" "wss://127.0.0.1:8443/wss/"
ProxyPass "/" "https://127.0.0.1:8443/"
ProxyPassReverse "/" "https://127.0.0.1:8443/"

</VirtualHost>

</IfModule>

Then enable the site with:

sudo a2ensiteĀ unifi.subdomain.com-le-ssl.conf;sudo service apache2 reload

And that should do it! Any questions or comments, please post below.