instagram arrow-down
Kalle Lilja

Archives

Automated Let’s Encrypt – UniFi Controller

Free automated SSL solution for UniFi

Securing the UniFi Controller web interface with an SSL certificate (HTTPS) is not only important, it’s mandatory in my eyes, especially if the controller is publicly available for use via the app or directly by customers/site owners.
Luckily this process can be entirely automated and rendered free by using a Linux based controller in combination with Let’s Encrypt.

Let’s Encrypt verifies the certificates by looking up the desired hostname with DNS, verifying connectivity with HTTPS tcp:443 and finding the files the request created, read more here.
This is all well and good when applying Let’s Encrypt certificates to websites, but it does get a bit more complicated when combining it with the UniFi Controller, as it doesn’t run a simple website on tcp:443, it runs on tcp:8443.

The way we’ll automate this is by using certbot in combination with a cronjob.
Certbot will stand up a quick and dirty HTTPS enabled site on the machine to host the Let’s Encrypt request files, once the certificate is approved and downloaded the site will terminate.
The now approved certificate comes in two .pem parts, these can’t be used by the UniFi Controller as is and has to be combined and converted to a .p12 file, this will be done using openssl.
Once converted the certificate can be installed on the UniFi Contoller side of things.
Lastly the automated renewal can be set up as the certificates only stay valid for 3 months.

What you’ll need

– SSH Access to the Ubuntu/Debian based machine running UniFi Controller
– A DNS name set up (FQDN)

Setup

I’ve created and distributed the installation procedure/command reference over on GitHub as well as below.

# Prerequisites
sudo mkdir /usr/local/bin
sudo apt-get install wget openssl
# Make sure sudo has a Crontab
sudo crontab -e
# REMEBER TO CHANGE;
# UNIFI.CONTROLLER.NAME
# -password pass:PASSWORD
# -srcstorepass PASSWORD
# Make sure to change the same vaules at the Cron Job stage aswell.

# Open https (tcp:443) using UFW
sudo ufw allow 443/tcp
sudo ufw --force enable
# This can be done in other ways, for example sudo iptables -A INPUT -p tcp -m tcp --sport 443 -j ACCEPT

# Set up LetsEncrypt with Certbot
# Navigate to system folder
cd /usr/local/bin
# Download certbot-auto
sudo wget https://dl.eff.org/certbot-auto
# Make Certbot-auto executable
sudo chmod a+x certbot-auto
# Install Certbot
yes | ./certbot-auto
# Generate a request for LetsEncrypt
# --standalone-supported-challenges deprecated as of 0.9.0 - https://certbot.eff.org/docs/using.html?highlight=standalone#certbot-commands
#./certbot-auto certonly --standalone --standalone-supported-challenges tls-sni-01 --register-unsafely-without-email --agree-tos -d UNIFI.CONTROLLER.NAME
./certbot-auto certonly --standalone --preferred-challenges tls-sni --register-unsafely-without-email --agree-tos -d UNIFI.CONTROLLER.NAME
 
# Convert cert to PKCS #12 format
sudo openssl pkcs12 -export -inkey /etc/letsencrypt/live/UNIFI.CONTROLLER.NAME/privkey.pem -in /etc/letsencrypt/live/UNIFI.CONTROLLER.NAME/fullchain.pem -out /usr/local/bin/lecert.p12 -name ubnt -password pass:PASSWORD
 
# Backup current keystore
sudo cp /var/lib/unifi/keystore /var/lib/unifi/keystore.backup

# Install certificate on UniFi Controller
sudo keytool -importkeystore -deststorepass aircontrolenterprise -destkeypass aircontrolenterprise -destkeystore /var/lib/unifi/keystore -srckeystore /usr/local/bin/lecert.p12 -srcstoretype PKCS12 -srcstorepass PASSWORD -alias ubnt -noprompt
# Cleanup
sudo rm /usr/local/bin/lecert.p12
# Restart UniFi Service to enable the new certificate
sudo /etc/init.d/unifi restart
 
# Automated Cron Job for Renewal
# Create renew_lets_encrypt_cert.sh script
sudo touch /usr/local/bin/renew_lets_encrypt_cert.sh
sudo echo -e '#!/bin/bash\n# Get the certificate from LetsEncrypt\n/usr/local/bin/certbot-auto renew --quiet --no-self-upgrade\n# Convert cert to PKCS #12 format\n/usr/bin/openssl pkcs12 -export -inkey /etc/letsencrypt/live/UNIFI.CONTROLLER.NAME/privkey.pem -in /etc/letsencrypt/live/UNIFI.CONTROLLER.NAME/fullchain.pem -out /usr/local/bin/lecert.p12 -name ubnt -password pass:PASSWORD\n# Load it into the java keystore that UBNT understands\n/usr/bin/keytool -importkeystore -deststorepass aircontrolenterprise -destkeypass aircontrolenterprise -destkeystore /var/lib/unifi/keystore -srckeystore /usr/local/bin/lecert.p12 -srcstoretype PKCS12 -srcstorepass PASSWORD -alias ubnt -noprompt\n# Clean up and use new cert\nrm /usr/local/bin/lecert.p12\n/etc/init.d/unifi restart' | sudo tee -a /usr/local/bin/renew_lets_encrypt_cert.sh
# Make renew_lets_encrypt_cert.sh script executable
sudo chmod +x /usr/local/bin/renew_lets_encrypt_cert.sh
# Schedule Cron Job to run renew_lets_encrypt_cert.sh every Monday
sudo crontab -l | { cat; echo "1 1 * * 1 /usr/local/bin/renew_lets_encrypt_cert.sh"; } | sudo crontab -

Gotchas

A common error is that the HTTPS port is not open on the Linux side of things, as per usual there are many ways to solve this problem, the easiest is probably: sudo ufw allow 443/tcp.
Another error has to do with variable swaps. Make sure all references to UNIFI.CONTROLLER.NAME and PASSWORD has been changed to fit your environment.
Keep the certbot user guide handy in case of any errors.

Software used
Certbot
Let’s Encrypt
Ubuntu Server 16.04.2 LTS