raspiblitz/home.admin/config.scripts/internet.letsencrypt.sh
/rootzoll abe57c9693
#4568 LNbits install fix for AMD & VMs + update lnbits to v0.12.11 (#4768)
* #4568 add missing dependencies for AMD64
* Update: LNbits 0.12.11
* #4765 add check postgres installed check
* #3264 add nginx header adjustments
* Fix postgres bats test (#4769)
* #4204 move .env to data disc
* letsencrypt takes care of selfsigned certs
2024-10-04 23:15:19 +02:00

447 lines
14 KiB
Bash
Executable File

#!/bin/bash
# command info
if [ $# -eq 0 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ] || [ "$1" = "-help" ]; then
echo "config script to install or remove the Let's Encrypt Client (ACME.SH)"
echo "internet.letsencrypt.sh [on|off]"
echo "internet.letsencrypt.sh issue-cert DNSSERVICE FULLDOMAINNAME APITOKEN ip|tor|ip&tor"
echo "internet.letsencrypt.sh remove-cert FULLDOMAINNAME ip|tor|ip&tor"
echo "internet.letsencrypt.sh refresh-nginx-certs"
exit 1
fi
source /mnt/hdd/raspiblitz.conf
# make sure the HDD is mounted
mountpoint -q /mnt/hdd || { echo "# internet.letsencrypt.sh - /mnt/hdd is not mounted. Exiting."; exit 1; }
# https://github.com/acmesh-official/acme.sh/releases
ACME_LOAD_BASE_URL="https://github.com/acmesh-official/acme.sh/archive/refs/tags/3.0.7.tar.gz"
ACME_VERSION="3.0.7"
ACME_INSTALL_HOME="/home/admin/.acme.sh"
ACME_CONFIG_HOME="/mnt/hdd/app-data/letsencrypt"
ACME_CERT_HOME="${ACME_CONFIG_HOME}/certs"
ACME_IS_INSTALLED=0
###################
# FUNCTIONS
###################
function menu_enter_email() {
HEIGHT=18
WIDTH=56
BACKTITLE="Manage TLS certificates"
TITLE="Let's Encrypt - eMail"
INPUTBOX="\n
You can *optionally* enter an eMail address.\n
\n
The address will not be included in the generated certificates.\n
\n
It will be used to e.g. notify you about certificate expiration and changes
to the Terms of Service of Let's Encrypt.\n
\n
Feel free to leave empty."
ADDRESS=$(dialog --clear \
--backtitle "${BACKTITLE}" \
--title "${TITLE}" \
--inputbox "${INPUTBOX}" ${HEIGHT} ${WIDTH} 2>&1 >/dev/tty)
echo "${ADDRESS}"
}
function acme_status() {
# check if acme is installed (either directory or cronjob)
cron_count=$(crontab -l | grep "acme.sh" -c)
if [ -f "${ACME_INSTALL_HOME}/acme.sh" ] || [ "${cron_count}" = "1" ]; then
ACME_IS_INSTALLED=1
else
ACME_IS_INSTALLED=0
fi
}
function acme_install() {
email="${1}"
# create a dummy email if none is provided
if [ -z "${email}" ]; then
random_number=$(shuf -i 100-999 -n 1)
random_word=$(shuf -n 1 /usr/share/dict/words)
ending="x.com"
email="${random_word}${random_number}@gm${ending}"
fi
# ensure socat
if ! command -v socat >/dev/null; then
echo "# installing socat..."
sudo apt-get update >/dev/null 2>&1
sudo apt-get install -y socat >/dev/null 2>&1
fi
# make sure config directory exists
if ! [ -d $ACME_CONFIG_HOME ]; then
sudo mkdir -p $ACME_CONFIG_HOME
fi
sudo chown admin:admin $ACME_CONFIG_HOME
# download and install acme.sh
echo "# download acme.sh release ${ACME_VERSION} from ${ACME_LOAD_BASE_URL}"
rm -r /tmp/acme.sh* 2>/dev/null
if ! curl -L --silent --fail -o "/tmp/acme.sh.tar.gz" "${ACME_LOAD_BASE_URL}" 2>&1; then
echo "Error ($?): Download failed from: ${ACME_LOAD_BASE_URL}"
rm -r /tmp/acme.sh*
exit 1
fi
if tar xzf "/tmp/acme.sh.tar.gz" -C /tmp/; then
cd "/tmp/acme.sh-${ACME_VERSION}" || exit
echo "# installing acme.sh with email(${email})"
./acme.sh --install \
--noprofile \
--home "${ACME_INSTALL_HOME}" \
--config-home "${ACME_CONFIG_HOME}" \
--cert-home "${ACME_CERT_HOME}" \
--accountemail "${email}"
else
echo "# Error ($?): Extracting failed"
exit 1
fi
rm -r /tmp/acme.sh*
}
function refresh_certs_with_nginx() {
# FIRST: SET ALL TO DEFAULT SELF SIGNED
# make a hashs of certs before
certHashBefore1=$(sudo md5sum /mnt/hdd/app-data/nginx/tls.cert | head -n1 | cut -d " " -f1)
certHashBefore2=$(sudo md5sum /mnt/hdd/app-data/nginx/tor_tls.cert | head -n1 | cut -d " " -f1)
# make sure self-signed cert exists & is valid
/home/admin/config.scripts/internet.selfsignedcert.sh create
echo "# default IP certs"
sudo rm /mnt/hdd/app-data/nginx/tls.cert
sudo rm /mnt/hdd/app-data/nginx/tls.key
sudo ln -sf /mnt/hdd/app-data/selfsignedcert/selfsigned.cert /mnt/hdd/app-data/nginx/tls.cert
sudo ln -sf /mnt/hdd/app-data/selfsignedcert/selfsigned.key /mnt/hdd/app-data/nginx/tls.key
echo "# default TOR certs"
sudo rm /mnt/hdd/app-data/nginx/tor_tls.cert
sudo rm /mnt/hdd/app-data/nginx/tor_tls.key
sudo ln -sf /mnt/hdd/app-data/selfsignedcert/selfsigned.cert /mnt/hdd/app-data/nginx/tor_tls.cert
sudo ln -sf /mnt/hdd/app-data/selfsignedcert/selfsigned.key /mnt/hdd/app-data/nginx/tor_tls.key
# SECOND: SET LETSENCRPYT CERTS FOR SUBSCRIPTIONS (ONLY IF VALID)
if [ "${letsencrypt}" != "on" ]; then
echo "# lets encrypt is off - so no certs replacements"
return
fi
certsDirectories=$(sudo ls ${ACME_CERT_HOME})
echo "# certsDirectories(${certsDirectories})"
directoryArray=(`echo "${certsDirectories}" | tr ' ' ' '`)
for i in "${directoryArray[@]}"; do
FQDN=$(echo "${i}" | cut -d "_" -f1)
echo "# i(${i})"
echo "# FQDN(${FQDN})"
# check if cert exists and is valid
CERT_FILE="${ACME_CERT_HOME}/${FQDN}_ecc/fullchain.cer"
echo "CERT_FILE(${CERT_FILE})"
if openssl x509 -checkend 86400 -noout -in "${CERT_FILE}"; then
echo "# The certificate is valid for more than one day. OK use them nginx."
else
echo "# The certificate is non-exist, invalid, expired or will expire within a day. DONT use them the nginx."
continue
fi
# check if there is a LetsEncrypt Subscription for this domain
details=$(/home/admin/config.scripts/blitz.subscriptions.letsencrypt.py subscription-detail $FQDN)
if [ $(echo "${details}" | grep -c "error=") -eq 0 ] && [ ${#details} -gt 10 ]; then
echo "# details(${details})"
# check if subscription is active
active=$(echo "${details}" | jq -r ".active")
if [ "${active}" != "true" ]; then
echo "# not active(${active}) ... skipping"
continue
fi
# get target for that domain
options=$(echo "${details}" | jq -r ".target")
# replace certs for clearnet
if [ "${options}" == "ip" ] || [ "${options}" == "ip&tor" ]; then
echo "# replacing IP certs for ${FQDN}"
sudo rm /mnt/hdd/app-data/nginx/tls.cert
sudo rm /mnt/hdd/app-data/nginx/tls.key
sudo ln -s ${ACME_CERT_HOME}/${FQDN}_ecc/fullchain.cer /mnt/hdd/app-data/nginx/tls.cert
sudo ln -s ${ACME_CERT_HOME}/${FQDN}_ecc/${FQDN}.key /mnt/hdd/app-data/nginx/tls.key
fi
# replace certs for tor
if [ "${options}" == "tor" ] || [ "${options}" == "ip&tor" ]; then
echo "# replacing TOR certs for ${FQDN}"
sudo rm /mnt/hdd/app-data/nginx/tor_tls.cert
sudo rm /mnt/hdd/app-data/nginx/tor_tls.key
sudo ln -s ${ACME_CERT_HOME}/${FQDN}_ecc/fullchain.cer /mnt/hdd/app-data/nginx/tor_tls.cert
sudo ln -s ${ACME_CERT_HOME}/${FQDN}_ecc/${FQDN}.key /mnt/hdd/app-data/nginx/tor_tls.key
fi
# todo maybe allow certs for single services later (dont forget that these also need to be replaced in 'on' then)
if [ "${options}" != "tor" ] && [ "${options}" != "ip" ] && [ "${options}" != "ip&tor" ]; then
echo "# FAIL target '${options}' not supported yet'"
fi
fi
done
# set permissions
sudo chown -h admin:www-data /mnt/hdd/app-data/nginx/tls.cert
sudo chown -h admin:www-data /mnt/hdd/app-data/nginx/tls.key
sudo chown -h admin:www-data /mnt/hdd/app-data/nginx/tor_tls.cert
sudo chown -h admin:www-data /mnt/hdd/app-data/nginx/tor_tls.key
# make a hashs of certs after
certHashAfter1=$(sudo md5sum /mnt/hdd/app-data/nginx/tls.cert | head -n1 | cut -d " " -f1)
certHashAfter2=$(sudo md5sum /mnt/hdd/app-data/nginx/tor_tls.cert | head -n1 | cut -d " " -f1)
echo "# certHashBefore(${certHashBefore1})"
echo "# certHashAfter(${certHashAfter1})"
echo "# certHashBeforeTor(${certHashBefore2})"
echo "# certHashAfterTor(${certHashAfter2})"
# check if nginx needs to be reloaded
if [ "${certHashBefore1}" != "${certHashAfter1}" ] || [ "${certHashBefore2}" != "${certHashAfter2}" ]; then
echo "# nginx needs to be reloaded"
sudo systemctl restart nginx 2>&1
else
echo "# nginx certs are the same - no need to reload"
fi
}
###################
# running as admin
###################
adminUserId=$(id -u admin)
if [ "${EUID}" != "${adminUserId}" ]; then
echo "error='please run as admin user'"
exit 1
fi
###################
# update status
###################
acme_status
###################
# ON
###################
if [ "$1" = "1" ] || [ "$1" = "on" ]; then
if [ ${ACME_IS_INSTALLED} -eq 0 ]; then
echo "*** INSTALLING Let's Encrypt Client 'acme.sh' ***"
# setting value in RaspiBlitz config
/home/admin/config.scripts/blitz.conf.sh set letsencrypt "on"
address="$2"
if [ "$2" == "enter-email" ]; then
address=$(menu_enter_email)
echo ""
fi
# make sure storage directory exist
sudo mkdir -p $ACME_CERT_HOME 2>/dev/null
sudo chown -R admin:admin $ACME_CONFIG_HOME
sudo chmod -R 733 $ACME_CONFIG_HOME
# install the acme script
echo "# acme_install"
acme_install "${address}"
echo ""
# make sure already existing certs get refreshed in to nginx
refresh_certs_with_nginx
exit 0
else
echo "# *** Let's Encrypt Client 'acme.sh' appears to be installed already ***"
exit 1
fi
###################
# ISSUE-CERT
###################
elif [ "$1" = "issue-cert" ]; then
# check if letsencrypt is on
if [ "${letsencrypt}" != "on" ]; then
echo "error='letsencrypt is not on'"
exit 1
fi
# make sure storage directory exist
sudo mkdir -p $ACME_CERT_HOME 2>/dev/null
sudo chown -R admin:admin $ACME_CONFIG_HOME
sudo chmod -R 733 $ACME_CONFIG_HOME
# get and check parameters
dnsservice=$2
FQDN=$3
apitoken=$4
options=$5
if [ ${#dnsservice} -eq 0 ] || [ ${#FQDN} -eq 0 ] || [ ${#apitoken} -eq 0 ]; then
echo "error='invalid parameters'"
exit 1
fi
if [ ${#options} -eq 0 ]; then
options="ip&tor"
fi
# prepare values and exports based on dnsservice
if [ "${dnsservice}" == "duckdns" ]; then
echo "# preparing DUCKDNS"
dnsservice="dns_duckdns"
export DuckDNS_Token=${apitoken}
elif [ "${dnsservice}" == "dynu" ]; then
echo "# preparing DYNYU"
dnsservice="dns_dynu"
clientid=$(echo "${apitoken}" | cut -d ':' -f 1)
secret=$(echo "${apitoken}" | cut -d ':' -f 2)
export Dynu_ClientId="${clientid}"
export Dynu_Secret="${secret}"
else
echo "error='not supported dnsservice'"
exit 1
fi
# create certificates
echo "# creating certs for ${FQDN}"
$ACME_INSTALL_HOME/acme.sh --home "${ACME_INSTALL_HOME}" --config-home "${ACME_CONFIG_HOME}" --cert-home "${ACME_CERT_HOME}" --issue --dns ${dnsservice} -d ${FQDN} --keylength ec-256 2>&1
success1=$($ACME_INSTALL_HOME/acme.sh --list --home "${ACME_INSTALL_HOME}" --config-home "${ACME_CONFIG_HOME}" --cert-home "${ACME_CERT_HOME}" | grep -c "${FQDN}")
success2=$(sudo ls ${ACME_CERT_HOME}/${FQDN}_ecc//fullchain.cer | grep -c "/fullchain.cer")
if [ ${success1} -eq 0 ] || [ ${success2} -eq 0 ]; then
sleep 6
echo "error='acme failed'"
exit 1
fi
# test nginx config
refresh_certs_with_nginx
syntaxOK=$(sudo nginx -t 2>&1 | grep -c "syntax is ok")
testOK=$(sudo nginx -t 2>&1 | grep -c "test is successful")
if [ ${syntaxOK} -eq 0 ] || [ ${testOK} -eq 0 ]; then
echo "# to check details on nginx config use: sudo nginx -t"
echo "error='nginx config failed'"
exit 1
fi
# restart nginx
echo "# restarting nginx"
sudo systemctl restart nginx 2>&1
exit 0
###################
# REMOVE-CERT
###################
elif [ "$1" = "remove-cert" ]; then
# make sure storage directory exist
sudo mkdir -p $ACME_CERT_HOME 2>/dev/null
sudo chown -R admin:admin $ACME_CONFIG_HOME
sudo chmod -R 733 $ACME_CONFIG_HOME
# get and check parameters
FQDN=$2
options=$3
if [ ${#FQDN} -eq 0 ]; then
echo "error='invalid parameters'"
exit 1
fi
if [ ${#options} -eq 0 ]; then
options="ip&tor"
fi
echo "# internet.letsencrypt.sh remove-cert ${FQDN} ${options}"
# remove cert from renewal
$ACME_INSTALL_HOME/acme.sh --remove -d "${FQDN}" --ecc --home "${ACME_INSTALL_HOME}" --config-home "${ACME_CONFIG_HOME}" --cert-home "${ACME_CERT_HOME}" 2>&1
# delete cert files
sudo rm -r ${ACME_CERT_HOME}/${FQDN}_ecc
# test nginx config
refresh_certs_with_nginx
syntaxOK=$(sudo nginx -t 2>&1 | grep -c "syntax is ok")
testOK=$(sudo nginx -t 2>&1 | grep -c "test is successful")
if [ ${syntaxOK} -eq 0 ] || [ ${testOK} -eq 0 ]; then
echo "# to check details on nginx config use: sudo nginx -t"
echo "error='nginx config failed'"
exit 1
fi
exit 0
###################
# REFRESH NGINX CERTS
###################
elif [ "$1" = "refresh-nginx-certs" ]; then
# refresh nginx
refresh_certs_with_nginx
syntaxOK=$(sudo nginx -t 2>&1 | grep -c "syntax is ok")
testOK=$(sudo nginx -t 2>&1 | grep -c "test is successful")
if [ ${syntaxOK} -eq 0 ] || [ ${testOK} -eq 0 ]; then
echo "# to check details on nginx config use: sudo nginx -t"
echo "error='nginx config failed'"
exit 1
fi
###################
# OFF
###################
elif [ "$1" = "0" ] || [ "$1" = "off" ]; then
if [ ${ACME_IS_INSTALLED} -eq 1 ]; then
echo "*** UNINSTALLING Let's Encrypt Client 'acme.sh' ***"
# setting value in RaspiBlitz config
/home/admin/config.scripts/blitz.conf.sh set letsencrypt "off"
"${ACME_INSTALL_HOME}/acme.sh" --uninstall \
--home "${ACME_INSTALL_HOME}" \
--config-home "${ACME_CONFIG_HOME}" \
--cert-home "${ACME_CERT_HOME}"
# remove certs
sudo rm -r ${ACME_CERT_HOME}
# refresh nginx
refresh_certs_with_nginx
# remove old script install
sudo rm -r ${ACME_INSTALL_HOME}
sudo rm -r ${ACME_CONFIG_HOME}
exit 0
else
echo "# *** Let's Encrypt Client 'acme.sh' not installed ***"
exit 1
fi
else
echo "# FAIL: parameter not known - run with -h for help"
exit 1
fi