How to: LetsEncrypt in standalone mode for Unifi on Ubuntu 20.04 LTS

This is an update of my previous post, now that cert-manager is more mature, and i’ve rebuilt my server on Ubuntu 20.04 (from 18.04).

  1. install certbot
  2. install script to update unifi certificate
  3. Test
  4. Issue full certificate
  5. Install cron jobs to automate renewal

Install certbot

Certbot installation instructions are at online of course but here’s a summary:

  1. Update package list:
    sudo apt update
  2. install:
    sudo apt install -y certbot

Create a new certificate using LetsEncrypt

We’re going to use standalone mode, and first we’ll get a test certificate just to validate that everything’s working (so that we don’t trigger LetsEncrypt’s rate limits).

  1. open port 80 in ufw:
sudo ufw allow http
  1. Test certificate issuance:
sudo certbot certonly --standalone -d <hostname> -n --test-cert --agree-tos -m <email>

You should see something like this:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for <hostname>
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/<hostname>/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/<hostname>/privkey.pem
   Your cert will expire on 2021-04-08. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
  1. If that’s worked, close the firewall (sudo ufw deny http) and move on to the next step and install the certificate in unifi. Later, we’ll come back and get a ‘real’ (not staging) certificate.

Install certificate in unifi

I use an amazing certificate installation script from Steve Jenkins.

  1. Get the script:
wget https://raw.githubusercontent.com/stevejenkins/unifi-linux-utils/master/unifi_ssl_import.sh
  1. Edit the config settings in the script to add hostname, switch from Fedora/RedHat/CentOS to Debian/Ubuntu, enable LE_MODE, and disable key paths:
# CONFIGURATION OPTIONS
UNIFI_HOSTNAME=<hostname>   
UNIFI_SERVICE=unifi

# Uncomment following three lines for Fedora/RedHat/CentOS
# UNIFI_DIR=/opt/UniFi
# JAVA_DIR=${UNIFI_DIR}
# KEYSTORE=${UNIFI_DIR}/data/keystore

# Uncomment following three lines for Debian/Ubuntu
UNIFI_DIR=/var/lib/unifi
JAVA_DIR=/usr/lib/unifi
KEYSTORE=${UNIFI_DIR}/keystore

# Uncomment following three lines for CloudKey
#UNIFI_DIR=/var/lib/unifi
#JAVA_DIR=/usr/lib/unifi
#KEYSTORE=${JAVA_DIR}/data/keystore

# FOR LET'S ENCRYPT SSL CERTIFICATES ONLY
# Generate your Let's Encrtypt key & cert with certbot before running this script
LE_MODE=yes
LE_LIVE_DIR=/etc/letsencrypt/live

# THE FOLLOWING OPTIONS NOT REQUIRED IF LE_MODE IS ENABLED
# PRIV_KEY=/etc/ssl/private/hostname.example.com.key
# SIGNED_CRT=/etc/ssl/certs/hostname.example.com.crt
# CHAIN_FILE=/etc/ssl/certs/startssl-chain.crt
  1. copy to /usr/local/bin and make executable:
sudo cp unifi_ssl_import.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/unifi_ssl_import.sh
  1. Run the script to import the certificate. Look for any errors:
sudo /usr/local/bin/unifi_ssl_import.sh
  1. Navigate to your server (https://<hostname>:8443). If it worked, you’ll see a warning that the certificate isnt trusted, but when you examine the cert, it’s issued by a ‘fake’ Lets Encrypt issuer, for example:
Certificate showing a chain back to a root called 'Fake LE Intermediate X1'

Get the real LetsEncrypt certificate

Simply run the same certbot command as before, but leave off the --test-cert flag, and add the --force-renew flag to force it to replace the (unexpired) test certificate:

sudo certbot certonly --standalone -d <hostname> -n --force-renew --agree-tos -m <email>

and rerun the installation script:

sudo /usr/local/bin/unifi_ssl_import.sh

Close the browser window and reopen it, then navigate to your server again. You should now see the valid certificate:

A trusted certificate chain for the host

Automate renewal and issuance

Set up a crontab to renew the cert. Pick a randomish time. It should run every day – if the certificate is still valid, it’ll just skip

  1. load crontab – you may be asked to pick an editor – i suggest nano:
    sudo crontab -e
  2. add the schedule – use crontab guru if you arent familiar with crontab schedule expressions, and set up tasks to:
    1. request a new certificate, and
    2. install the updated certificate. I chose a time just over an hour after certificate issue.

It should look like this:

# renew any certificates due to expire soon at 05:20 each day
20 5 * * * /usr/bin/certbot renew --standalone -n --agree-tos -m <email> --pre-hook 'ufw allow http' --post-hook 'ufw deny http'
# install any updated certificates at 06:29 each day
29 6 * * * /usr/local/bin/unifi_ssl_import.sh

The --pre-hook and --post-hook commands tell UFW to open up port 80 and then close it again afterwards.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.