Skip to content

Instantly share code, notes, and snippets.

@Nepherte
Last active April 3, 2024 02:32
Show Gist options
  • Save Nepherte/eb043826b701dfcc887a87202f1907dd to your computer and use it in GitHub Desktop.
Save Nepherte/eb043826b701dfcc887a87202f1907dd to your computer and use it in GitHub Desktop.
Script to import an SSL certificate into a UniFi Controller.
#!/usr/bin/env bash
# unifi_ssl_import.sh
# UniFi Controller SSL Certificate Import Script for Unix/Linux Systems
# by Steve Jenkins <http://www.stevejenkins.com/>
# Part of https://github.com/stevejenkins/ubnt-linux-utils/
# Incorporates ideas from https://source.sosdg.org/brielle/lets-encrypt-scripts
# Version 2.8
# Last Updated Jan 13, 2017
# REQUIREMENTS
# 1) Assumes you have a UniFi Controller installed and running on your system.
# 2) Assumes you already have a valid 2048-bit private key, signed certificate, and certificate authority
# chain file. The Controller UI will not work with a 4096-bit certificate. See http://wp.me/p1iGgP-2wU
# for detailed instructions on how to generate those files and use them with this script.
# KEYSTORE BACKUP
# Even though this script attempts to be clever and careful in how it backs up your existing keystore,
# it's never a bad idea to manually back up your keystore (located at $UNIFI_DIR/data/keystore on RedHat
# systems or /$UNIFI_DIR/keystore on Debian/Ubunty systems) to a separate directory before running this
# script. If anything goes wrong, you can restore from your backup, restart the UniFi Controller service,
# and be back online immediately.
# CONFIGURATION OPTIONS
UNIFI_HOSTNAME=ui.nepherte.com
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=no
LE_LIVE_DIR=/etc/letsencrypt/live
# THE FOLLOWING OPTIONS NOT REQUIRED IF LE_MODE IS ENABLED
PRIV_KEY=/etc/letsencrypt/live/ui.nepherte.com/privkey.pem
SIGNED_CRT=/etc/letsencrypt/live/ui.nepherte.com/cert.pem
CHAIN_FILE=/etc/letsencrypt/live/ui.nepherte.com/fullchain.pem
# CONFIGURATION OPTIONS YOU PROBABLY SHOULDN'T CHANGE
ALIAS=unifi
PASSWORD=aircontrolenterprise
#### SHOULDN'T HAVE TO TOUCH ANYTHING PAST THIS POINT ####
printf "\nStarting UniFi Controller SSL Import...\n"
# Check to see whether Let's Encrypt Mode (LE_MODE) is enabled
if [[ ${LE_MODE} == "YES" || ${LE_MODE} == "yes" || ${LE_MODE} == "Y" || ${LE_MODE} == "y" || ${LE_MODE} == "TRUE" || ${LE_MODE} == "true" || ${LE_MODE} == "ENABLED" || ${LE_MODE} == "enabled" || ${LE_MODE} == 1 ]] ; then
LE_MODE=true
printf "\nRunning in Let's Encrypt Mode...\n"
PRIV_KEY=${LE_LIVE_DIR}/${UNIFI_HOSTNAME}/privkey.pem
CHAIN_FILE=${LE_LIVE_DIR}/${UNIFI_HOSTNAME}/fullchain.pem
else
LE_MODE=false
printf "\nRunning in Standard Mode...\n"
fi
if [[ ${LE_MODE} == "true" ]]; then
# Check to see whether LE certificate has changed
printf "\nInspecting current SSL certificate...\n"
if md5sum -c "${LE_LIVE_DIR}/${UNIFI_HOSTNAME}/privkey.pem.md5" &>/dev/null; then
# MD5 remains unchanged, exit the script
printf "\nCertificate is unchanged, no update is necessary.\n"
exit 0
else
# MD5 is different, so it's time to get busy!
printf "\nUpdated SSL certificate available. Proceeding with import...\n"
fi
fi
# Verify required files exist
if [[ ! -f ${PRIV_KEY} ]] || [[ ! -f ${CHAIN_FILE} ]]; then
printf "\nMissing one or more required files. Check your settings.\n"
exit 1
else
# Everything looks OK to proceed
printf "\nImporting the following files:\n"
printf "Private Key: %s\n" "$PRIV_KEY"
printf "CA File: %s\n" "$CHAIN_FILE"
fi
# Create temp files
P12_TEMP=$(mktemp)
# Stop the UniFi Controller
printf "\nStopping UniFi Controller...\n"
service "${UNIFI_SERVICE}" stop
if [[ ${LE_MODE} == "true" ]]; then
# Write a new MD5 checksum based on the updated certificate
printf "\nUpdating certificate MD5 checksum...\n"
md5sum "${PRIV_KEY}" > "${LE_LIVE_DIR}/${UNIFI_HOSTNAME}/privkey.pem.md5"
fi
# Create double-safe keystore backup
if [[ -s "${KEYSTORE}.orig" ]]; then
printf "\nBackup of original keystore exists!\n"
printf "\nCreating non-destructive backup as keystore.bak...\n"
cp "${KEYSTORE}" "${KEYSTORE}.bak"
else
cp "${KEYSTORE}" "${KEYSTORE}.orig"
printf "\nNo original keystore backup found.\n"
printf "\nCreating backup as keystore.orig...\n"
fi
# Export your existing SSL key, cert, and CA data to a PKCS12 file
printf "\nExporting SSL certificate and key data into temporary PKCS12 file...\n"
#If there is a signed crt we should include this in the export
if [[ -f ${SIGNED_CRT} ]]; then
openssl pkcs12 -export \
-in "${CHAIN_FILE}" \
-in "${SIGNED_CRT}" \
-inkey "${PRIV_KEY}" \
-out "${P12_TEMP}" -passout pass:"${PASSWORD}" \
-name "${ALIAS}"
else
openssl pkcs12 -export \
-in "${CHAIN_FILE}" \
-inkey "${PRIV_KEY}" \
-out "${P12_TEMP}" -passout pass:"${PASSWORD}" \
-name "${ALIAS}"
fi
# Delete the previous certificate data from keystore to avoid "already exists" message
printf "\nRemoving previous certificate data from UniFi keystore...\n"
keytool -delete -alias "${ALIAS}" -keystore "${KEYSTORE}" -deststorepass "${PASSWORD}"
# Import the temp PKCS12 file into the UniFi keystore
printf "\nImporting SSL certificate into UniFi keystore...\n"
keytool -importkeystore \
-srckeystore "${P12_TEMP}" -srcstoretype PKCS12 \
-srcstorepass "${PASSWORD}" \
-destkeystore "${KEYSTORE}" \
-deststorepass "${PASSWORD}" \
-destkeypass "${PASSWORD}" \
-alias "${ALIAS}" -trustcacerts
# Clean up temp files
printf "\nRemoving temporary files...\n"
rm -f "${P12_TEMP}"
# Restart the UniFi Controller to pick up the updated keystore
printf "\nRestarting UniFi Controller to apply new Let's Encrypt SSL certificate...\n"
service "${UNIFI_SERVICE}" start
# That's all, folks!
printf "\nDone!\n"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment