How to SSL/TLS on UniFi Controller with bashscript + acme.sh & matrix.sh notifications

How to SSL/TLS on UniFi Controller with bashscript + acme.sh & matrix.sh notifications

Setup acme.sh Cert

Open Port 80 on your NAT firewall first to forward to 2280 on your mashine:

acme.sh --issue --standalone -d my.domain.com --httpport 2280 --reloadcmd "/usr/bin/unifi-contr-ssl.sh" --force --log


# You can find the cmdreload command at: $ssldir/your.domain.com.*.conf
# decode B64 with:
echo "L3Vzci9iaW4vdW5pZmktY29udHItc3NsLnNo" | base64 -d -i -w0; echo
# with Bash
base64 -d <(echo -e "L3Vzci9iaW4vdW5pZmktY29udHItc3NsLnNo");echo

Create, setup and run => unifi-contr-ssl.sh

#!/bin/bash

# (c) 2022 - suuhmer
# setup script unifi controller ssl
#
# just setup first with acme.sh / etc. new cert(s):
# setup Destination NAT 80 > 2280
# acme.sh --issue --standalone -d my.domain.com --httpport 2288 --reloadcmd "/usr/bin/unifi-contr-ssl.sh" --force --log
#

# maybe helpful for cron install:
# ------------------------------------------------------------------------------------------------
#  --cert-file <file>                Path to copy the cert file to after issue/renew..
#  --key-file <file>                 Path to copy the key file to after issue/renew.
#  --ca-file <file>                  Path to copy the intermediate cert file to after issue/renew.
#  --fullchain-file <file>           Path to copy the fullchain cert file to after issue/renew.
#  --reloadcmd <command>             Command to execute after issue/renew to reload the server.

MYDOMAIN="my.domain.com"
NEWCERTS=/etc/ssl/private
PKEY="$NEWCERTS/$MYDOMAIN.key"

echo "Copy certs and rename new Unifi private key"
cp /root/.acme.sh/$MYDOMAIN/* $NEWCERTS
mv $PKEY $NEWCERTS/Unifi.key

echo "Stopping now UniFi Controllers"
service unifi stop

sleep 2; echo "backup original keystore"
mv /var/lib/unifi/keystore /var/lib/unifi/keystore.`date "+%Y%m%d-%H%m%S"`

echo "convert chain to appropriate format"
openssl pkcs12 -export -inkey $NEWCERTS/Unifi.key -in $NEWCERTS/fullchain.cer -out $NEWCERTS/cert.p12 -name unifi -password pass:temppass

echo "import cert chain => to keystore"
keytool -importkeystore -deststorepass aircontrolenterprise -destkeypass aircontrolenterprise -destkeystore /var/lib/unifi/keystore -srckeystore $NEWCERTS/cert.p12 -srcstoretype PKCS12 -srcstorepass temppass -alias unifi -noprompt

echo "cleanup"
#rm $NEWCERTS/root.crt $NEWCERTS/Unifi.crt $NEWCERTS/Unifi.key $NEWCERTS/fullchain.crt $NEWCERTS/cert.p12

echo "Start UniFi Controller"
service unifi start

#echo "del old jks
#rm unifi.keystore.jks

#echo "convert => pkcs12
#openssl pkcs12 -export -in cloudkey.crt -inkey cloudkey.key -out unifi.p12 -name unifi -password pass:aircontrolenterprise

#echo "JKS erstellen
#keytool -importkeystore -srckeystore unifi.p12 -srcstoretype PKCS12 -srcstorepass aircontrolenterprise -destkeystore unifi.keystore.jks -storepass aircontrolenterprise

#echo "del old p12"
#rm  unifi.p12

exit 0

Setup matrix.sh

git clone https://github.com/fabianonline/matrix.sh.git /opt/matrix.sh
ln -s /opt/matrix.sh/matrix.sh /usr/bin/matrix-send.sh

# Fix for insecure fail: 
sed 's/curl -s -H "$AUTHORIZATION"/curl -s -k -H "$AUTHORIZATION"'/g -i /opt/matrix.sh/matrix.sh
Install and bypass secure TLS/SSL fix for my wildcard SAN domain
Config matrix.sh
cat << EOF > ~/.matrix.sh
MATRIX_TOKEN="tok_XYZ"
MATRIX_HOMESERVER="https://matrix.server.com"
MATRIX_USER="@USERNAME:matrix.server.com"
MATRIX_ROOM_ID="XYZROOOMID:matrix.server.com"

EOF
If you know your credentials and tokens
Or the easy way via:
# User you want to use as your matrix send bot/user:
matrix-send.sh --login

# Enter the room you're ivited or you want to join:
matrix-send.sh --select-default-room

Setup acme => matrix notifications:

Create: .acme.sh/account.conf

cat << EOF > .acme.sh/account.conf

LOG_FILE='/root/.acme.sh/acme.sh.log'
#LOG_LEVEL=1
#AUTO_UPGRADE="1"
#NO_TIMESTAMP=1

ACCOUNT_EMAIL='info@mymail.com'
USER_PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
#UPGRADE_HASH='xxx'
NOTIFY_HOOK='customscript'

NOTIFY_LEVEL='2'
NOTIFY_MODE='0'
SAVED_CUSTOMSCRIPT_PATH='/usr/local/bin/acme-notification.sh'

EOF
--notify-level  0|1|2|3      
------------------------
0: disabled, no notification will be sent.
1: send notification only when there is an error. No news is good news.
2: send notification when a cert is successfully renewed, or there is an error
3: send notification when a cert is skipped, renewed, or error. You will receive notification every night with this level.

--notify-mode   0|1    
-------------------
0: Bulk mode. Send all the domain's notifications in one message(email)
1: Cert mode. Send a message for every single cert. You may receive a bulk of emails in one
https://github.com/acmesh-official/acme.sh/wiki/notify
Create: /usr/local/bin/acme-notification.sh
cat << EOF > /usr/local/bin/acme-notification.sh
#!/usr/bin/env bash

subject="$1"
message="$2"
status="$3"

#do-something "$subject ($status): $message"

#echo "$subject ($status): $message" | matrix-send.sh -

matrix-send.sh --html "Hello ACME friend ;)<h3>$subject - <u>Status: ($status):</u></h3><br/><strong>$message</strong><br/><i>bye :)</i>"

#echo -e "Hello ACME $subject \n($status): $message \nbye :)." | matrix-send.sh -pre -

EOF

Using my acme.sh notify script:

#!/usr/bin/env sh

#Support matrix via matrix.sh

#MATRIX_BIN="/usr/bin/matrix-send.sh"
#MATRIX_BIN_ARGS="--html -"
#MATRIX_TO="zzzz@example.com"

matrix_send() {
  _subject="$1"
  _content="$2"
  _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
  _debug "_subject" "$_subject"
  _debug "_content" "$_content"
  _debug "_statusCode" "$_statusCode"

  MATRIX_BIN="${MATRIX_BIN:-$(_readaccountconf_mutable MATRIX_BIN)}"
  if [ -n "$MATRIX_BIN" ] && ! _exists "$MATRIX_BIN"; then
    _err "It seems that the command $MATRIX_BIN is not in path."
    return 1
  fi
  _MATRIX_BIN=$(_matrix_bin)
  if [ -n "$MATRIX_BIN" ]; then
    _saveaccountconf_mutable MATRIX_BIN "$MATRIX_BIN"
  else
    _clearaccountconf "MATRIX_BIN"
  fi

  MATRIX_BIN_ARGS="${MATRIX_BIN_ARGS:-$(_readaccountconf_mutable MATRIX_BIN_ARGS)}"
  if [ -n "$MATRIX_BIN_ARGS" ]; then
    _saveaccountconf_mutable MATRIX_BIN_ARGS "$MATRIX_BIN_ARGS"
  else
    _clearaccountconf "MATRIX_BIN_ARGS"
  fi

  result=$({ _matrix_message | eval "$(_matrix_cmnd)"; } 2>&1)

  if [ $? -ne 0 ]; then
    _debug "matrix send error."
    _err "$result"
    return 1
  fi

  _debug "matrix send success."
  return 0
}

_matrix_bin() {
  if [ -n "$MATRIX_BIN" ]; then
    _MATRIX_BIN="$MATRIX_BIN"
  elif _exists "matrix-send.sh"; then
    _MATRIX_BIN="matrix-send.sh"
  else
    _err "Please install matrix-send.sh first."
    return 1
  fi

  echo "$_MATRIX_BIN"
}

_matrix_cmnd() {
  case $(basename "$_MATRIX_BIN") in
  matrix-send.sh)
    echo "'$_MATRIX_BIN' $MATRIX_BIN_ARGS"
    ;;
  *)
    _err "Command $MATRIX_BIN is not supported, use matrix.sh."
    return 1
    ;;
  esac
}

_matrix_message() {
  echo "<b>[ACME.SH] $_subject</b><br/><br/>$_contains<br/>Status: ($_statusCode)"
}

_matrix_valid() {
  _contains "$1" "@"
}
# Installing matrix hook
acme.sh --set-notify --notify-hook matrix