Skip to content

Instantly share code, notes, and snippets.

@ljm42
Last active March 18, 2022 03:10
Show Gist options
  • Save ljm42/fbf73bf4d3a9a7322f2466ae48c0e75b to your computer and use it in GitHub Desktop.
Save ljm42/fbf73bf4d3a9a7322f2466ae48c0e75b to your computer and use it in GitHub Desktop.
#!/bin/bash
# Copyright 2017, ljm42
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 2,
# as published by the Free Software Foundation.
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# the latest version of this script can be found here:
# https://gist.github.com/ljm42/fbf73bf4d3a9a7322f2466ae48c0e75b
VERSION=0.7
######################################
# REQUIREMENTS BEFORE INSTALLING
######################################
#
# This script will watch for changes to your LetsEncrypt certificates and
# copy them to the unRAID certificate directory, automatically keeping unRAID current.
#
# This script will attempt to validate the certs before restarting nginx. If there is a problem,
# you will need to delete/replace the files in /boot/config/ssl/certs and reboot.
#
# Requirements (yeah, there are a lot):
# - You must be running unRAID 6.4.0-rc3+ with SSL enabled in the unRAID GUI
# - You should install the User Scripts plugin
# - You must have the LSIO LetsEncrypt docker installed and working for your domain
# (referred to as "mydomain.com" here)
# - Note: to prevent a conflict with port 443, the LetsEncrypt docker either needs to be
# on its own IP or unRAID should be on a custom port
# - The url you want to use to access your server must be added as a subdomain
# in LetsEncrypt (i.e. add "tower" as a subdomain of "mydomain.com" to get "tower.mydomain.com")
# - External to your network, that url ("tower.mydomain.com") should resolve to your router via DDNS
# and port 443 should be forwarded from the router to port 443 on your LetsEncrypt IP
# (LetsEncrypt will not issue the cert unless it can reach the IP address)
# - Internal to your network, that url should resolve to your unRAID server (i.e. 192.168.1.50)
# You might do this by adding "tower.mydomain.com -> 192.168.1.50" to your router and using
# your router for DNS on your network.
# Or you could distribute hosts files that contain this line (don't include the pound sign):
# 192.168.1.50 tower.mydomain.com
######################################
# CONFIGURATION
######################################
# Set the path to your LSIO Let's Encrypt key folder.
# This folder should contain your privkey.pem and fullchain.pem files
LE_CERT_PATH="/mnt/user/appdata/letsencrypt/keys/letsencrypt"
# In the User Scripts plugin, add a new script named "copy_le_certs" and paste in the
# contents of this script. Set the schedule to disabled.
# Then add another new script named "copy_le_certs_launcher" and paste the following
# into the script area (remove the first pound sign and leading spaces from each line)
# #!/bin/bash
# echo "/boot/config/plugins/user.scripts/scripts/copy_le_certs/script" | at NOW -M > /dev/null 2>&1
# Schedule the launcher script to run at array start
######################################
# END CONFIGURATION
######################################
######################################
# Begin script
######################################
######################################
# Setup environment
######################################
# Setup variables for logger and echo. Thanks WeeboTech!
# https://forums.lime-technology.com/topic/37535-how-to-have-script-log-messages-to-unraidss-syslog/#comment-361182
#P=${0##*/} # basename of program
#R=${0%%/$P} # dirname of program
#P=${P%.*} # strip off after last . character
# The above variables don't work when executed by the User Scripts plugin. Just set it manually:
P="copy_le_certs"
# logger -t${P}[$$] "message"
# echo "${P}[$$] message"
UN_SERVER_NAME=${HOSTNAME}
UN_CERT_PATH="/boot/config/ssl/certs"
KEY_SOURCE="${LE_CERT_PATH}/privkey.pem"
KEY_DEST="${UN_CERT_PATH}/${UN_SERVER_NAME}_unraid_key.pem"
CERT_SOURCE="${LE_CERT_PATH}/fullchain.pem"
CERT_DEST="${UN_CERT_PATH}/${UN_SERVER_NAME}_unraid_cert.pem"
CHANGED=0
######################################
# Ensure multiple copies of the script do not run at the same time
######################################
# http://mywiki.wooledge.org/BashFAQ/045
exec 9>/tmp/${P}.lck
if ! flock -n 9 ; then
echo "${P}[$$] another instance is already running. If you are sure this is wrong, delete '/tmp/${P}.lck'";
logger -t${P}[$$] "another instance is already running. If you are sure this is wrong, delete '/tmp/${P}.lck'";
ps -ef | grep "${P}"
exit 1
fi
# this now runs under the lock until 9 is closed (it will be closed automatically when the script ends)
######################################
# Ensure variables set properly and files exist
######################################
# Make sure source directory and files exist
if [[ ! -d "${LE_CERT_PATH}" && ! -x "${LE_CERT_PATH}" ]]; then
echo "${P}[$$] error: ${LE_CERT_PATH} does not exist. exiting"
logger -t${P}[$$] "ERROR: source directory does not exist, unable to start monitoring"
exit 1
fi
if [[ ! -e "${KEY_SOURCE}" ]]; then
echo "${P}[$$] error: ${KEY_SOURCE} does not exist. exiting"
logger -t${P}[$$] "ERROR: source key does not exist, unable to start monitoring"
exit 1
fi
if [[ ! -e "${CERT_SOURCE}" ]]; then
echo "${P}[$$] error: ${CERT_SOURCE} does not exist. exiting"
logger -t${P}[$$] "ERROR: source cert does not exist, unable to start monitoring"
exit 1
fi
# Make sure destination directory exists
if [[ ! -d "${UN_CERT_PATH}" && ! -x "${UN_CERT_PATH}" ]]; then
mkdir -p ${UN_CERT_PATH}
if [[ ! -d "${UN_CERT_PATH}" && ! -x "${UN_CERT_PATH}" ]]; then
echo "${P}[$$] error: unable to create dest path ${UN_CERT_PATH}. exiting"
logger -t${P}[$$] "ERROR: unable to create dest path, unable to start monitoring"
exit 1
fi
fi
# destination files have to exist or diff will fail
if [[ ! -e "${KEY_DEST}" ]]; then
cp ${KEY_SOURCE} ${KEY_DEST}
CHANGED=1
fi
if [[ ! -e "${CERT_DEST}" ]]; then
cp ${CERT_SOURCE} ${CERT_DEST}
CHANGED=1
fi
# compare source to destination without waiting for inotifywait, copy if needed
if ! diff ${KEY_SOURCE} ${KEY_DEST} >/dev/null ; then
cp ${KEY_SOURCE} ${KEY_DEST}
CHANGED=1
fi
if ! diff ${CERT_SOURCE} ${CERT_DEST} >/dev/null ; then
cp ${CERT_SOURCE} ${CERT_DEST}
CHANGED=1
fi
if [ "${CHANGED}" = "1" ]; then
# do not run nginx commands if they don't exist (i.e. running script on unRAID < 6.4 in prep for upgrade)
if [ -e "/etc/rc.d/rc.nginx" ]; then
/etc/rc.d/rc.nginx check
if [ $? -eq 0 ]; then
/etc/rc.d/rc.nginx reload
logger -t${P}[$$] "New LetsEncrypt certificates installed in unRAID gui"
/usr/local/emhttp/webGui/scripts/notify -e "unRAID Server Notice" -s "LetsEncrypt" -d "New LetsEncrypt certificates installed in unRAID gui" -i "normal"
else
logger -t${P}[$$] "New LetsEncrypt certificates are invalid. Resolve before rebooting."
/usr/local/emhttp/webGui/scripts/notify -e "unRAID Server Notice" -s "LetsEncrypt" -d "New LetsEncrypt certificates are invalid. Resolve before rebooting." -i "alert"
fi
else
logger -t${P}[$$] "New LetsEncrypt certificates installed in unRAID gui"
fi
CHANGED=0
fi
######################################
# Watch for changes using inotifywait
######################################
logger -t${P}[$$] "now watching for changes to certs"
# in order to break out of inotifywait loop when array unmounts, need this while loop syntax:
# https://unix.stackexchange.com/questions/166546/bash-cannot-break-out-of-piped-while-read-loop-process-substitution-works
while read EVENT; do
# logger -t${P}[$$] "event - '${EVENT}'"
# if array is stopped, exit
if [ "${EVENT}" = "UNMOUNT" ]; then
logger -t${P}[$$] "Array unmounted, exiting"
break
fi
# just to be safe, ensure the source files still exist before copying
if [[ ! -e "${KEY_SOURCE}" || ! -e "${CERT_SOURCE}" ]]; then
logger -t${P}[$$] "LE certificates are missing, won't install"
continue
fi
# compare source to destination, copy if needed
if ! diff ${KEY_SOURCE} ${KEY_DEST} >/dev/null ; then
cp ${KEY_SOURCE} ${KEY_DEST}
CHANGED=1
fi
if ! diff ${CERT_SOURCE} ${CERT_DEST} >/dev/null ; then
cp ${CERT_SOURCE} ${CERT_DEST}
CHANGED=1
fi
if [ "${CHANGED}" = "1" ]; then
# do not run nginx commands if they don't exist (i.e. running script on unRAID < 6.4 in prep for upgrade)
if [ -e "/etc/rc.d/rc.nginx" ]; then
/etc/rc.d/rc.nginx check
if [ $? -eq 0 ]; then
/etc/rc.d/rc.nginx reload
logger -t${P}[$$] "New LetsEncrypt certificates installed in unRAID gui"
/usr/local/emhttp/webGui/scripts/notify -e "unRAID Server Notice" -s "LetsEncrypt" -d "New LetsEncrypt certificates installed in unRAID gui" -i "normal"
else
logger -t${P}[$$] "New LetsEncrypt certificates are invalid. Resolve before rebooting."
/usr/local/emhttp/webGui/scripts/notify -e "unRAID Server Notice" -s "LetsEncrypt" -d "New LetsEncrypt certificates are invalid. Resolve before rebooting." -i "alert"
fi
else
logger -t${P}[$$] "New LetsEncrypt certificates installed in unRAID gui"
fi
CHANGED=0
#else
# logger -t${P}[$$] "there was an ${EVENT} event, but we don't care"
fi
done < <(inotifywait -q --monitor --event modify --event move --event create --event delete --event unmount --format "%e" ${KEY_SOURCE} ${CERT_SOURCE})
# depending on how the script is exited, this message may not be logged
logger -t${P}[$$] "exiting script, no longer monitoring for changes"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment