From a4baf04d0c0345310579c63a51ff1af58ebb7b4e Mon Sep 17 00:00:00 2001 From: Thomas Lange Date: Mon, 24 Oct 2022 20:48:16 +0200 Subject: Do some code cleanup/optimizing (including bugfix) Do some code cleanup and optimizing, and fix a bug where the script will always return exit code 1 if LETSENCRYPT_COMMAND_AFTER wasn't defined. The bug was caused by this last line in the script: [ ! -z "${LETSENCRYPT_COMMAND_AFTER}" ] \ && eval $LETSENCRYPT_COMMAND_AFTER ... and has been fixed by putting the "eval $LETSENCRYPT_COMMAND_AFTER" line into a real "if" container so that it doesn't affect the exit code of the script when LETSENCRYPT_COMMAND_AFTER is undefined: if [ ! -z "${LETSENCRYPT_COMMAND_AFTER}" ]; then eval $LETSENCRYPT_COMMAND_AFTER fi --- package/sbin/painless-le | 97 ++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/package/sbin/painless-le b/package/sbin/painless-le index 82886ee..124bbe8 100755 --- a/package/sbin/painless-le +++ b/package/sbin/painless-le @@ -4,9 +4,9 @@ #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# # # # Easily get an X.509 certificate from the Let's Encrypt Certificate Authority # -# for a bunch of hostnames without having an HTTP server installed. The script # -# assumes that you have an existing private key stored within your desired # -# install directory (with the filename which is defined in "${CONFIDENTIAL}"). # +# without the need of having a dedicated web server installed. With PainlessLE # +# you'll manage your certificate and private key files by yourself. Instead of # +# Certbot, you're responsible for the periodic renewal of the certificates. # # # # USAGE: # # painless-le [OPTIONS] TARGET_DIR DNS_DOMAIN [DNS_DOMAIN ...] # @@ -21,6 +21,15 @@ # # #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +#=============================================================================== +# Define ACME endpoint address and BEFORE/AFTER commands +#=============================================================================== +LETSENCRYPT_ENDPOINT="https://acme-v02.api.letsencrypt.org/directory" +#LETSENCRYPT_ENDPOINT="https://acme-staging-v02.api.letsencrypt.org/directory" + +#LETSENCRYPT_COMMAND_BEFORE="systemctl stop apache2" +#LETSENCRYPT_COMMAND_AFTER="systemctl start apache2" + #=============================================================================== # Normalize command-line arguments with GNU getopt #=============================================================================== @@ -32,10 +41,10 @@ set -- $(getopt -uo K:I:C:F: -- "$@") while getopts :K:I:C:F: option do case $option in - K) ARGUMENT_CONFIDENTIAL="$OPTARG" ;; - I) ARGUMENT_INTERMEDIATE="$OPTARG" ;; - C) ARGUMENT_CERTIFICATE_ONLY="$OPTARG" ;; - F) ARGUMENT_CERTIFICATE_FULL="$OPTARG" ;; + K) OPT_CONFIDENTIAL="$OPTARG" ;; + I) OPT_INTERMEDIATE="$OPTARG" ;; + C) OPT_CERTIFICATE_ONLY="$OPTARG" ;; + F) OPT_CERTIFICATE_FULL="$OPTARG" ;; esac done; shift $((OPTIND-1)) @@ -46,51 +55,35 @@ TARGET_DIR="$1" DNS_DOMAIN="${@:2}" #=============================================================================== -# Check if required positional arguments are given +# Check if required positional arguments are missing #=============================================================================== [ -z "${TARGET_DIR}" ] && echo "$0: Missing argument: TARGET_DIR" >&2 [ -z "${DNS_DOMAIN}" ] && echo "$0: Missing argument: DNS_DOMAIN" >&2 - -#=============================================================================== -# Exit script if required positional argument is missing -#=============================================================================== [ -z "${TARGET_DIR}" ] || [ -z "${DNS_DOMAIN}" ] && exit 1 #=============================================================================== -# Define the ACME endpoint address -#=============================================================================== -LETSENCRYPT_ENDPOINT="https://acme-v02.api.letsencrypt.org/directory" -#LETSENCRYPT_ENDPOINT="https://acme-staging-v02.api.letsencrypt.org/directory" - -#=============================================================================== -# Define commands who are executed BEFORE and AFTER the ACME challenge -#=============================================================================== -#LETSENCRYPT_COMMAND_BEFORE="systemctl stop apache2" -#LETSENCRYPT_COMMAND_AFTER="systemctl start apache2" - -#=============================================================================== -# Define required paths +# Define filename variables #=============================================================================== OPENSSLCONF="/etc/ssl/openssl.cnf" - REQUESTFILE=`mktemp /tmp/painless-le.XXXXXXXXXX.csr` - CONFIDENTIAL="${TARGET_DIR%/}/${ARGUMENT_CONFIDENTIAL:-confidential.pem}" - INTERMEDIATE="${TARGET_DIR%/}/${ARGUMENT_INTERMEDIATE:-intermediate.pem}" -CERTIFICATE_ONLY="${TARGET_DIR%/}/${ARGUMENT_CERTIFICATE_ONLY:-certificate_only.pem}" -CERTIFICATE_FULL="${TARGET_DIR%/}/${ARGUMENT_CERTIFICATE_FULL:-certificate_full.pem}" + REQUESTFILE="$(mktemp /tmp/painless-le.XXXXXX.csr)" + CONFIDENTIAL="${TARGET_DIR%/}/${OPT_CONFIDENTIAL:-confidential.pem}" + INTERMEDIATE="${TARGET_DIR%/}/${OPT_INTERMEDIATE:-intermediate.pem}" +CERTIFICATE_ONLY="${TARGET_DIR%/}/${OPT_CERTIFICATE_ONLY:-certificate_only.pem}" +CERTIFICATE_FULL="${TARGET_DIR%/}/${OPT_CERTIFICATE_FULL:-certificate_full.pem}" #=============================================================================== -# Delete Certificate-Signing-Request (CSR) file on exit +# Ensure the Certificate-Signing-Request file is deleted on exit #=============================================================================== trap 'rm ${REQUESTFILE}' EXIT #=============================================================================== -# Generate Certificate-Signing-Request (CSR) +# Create Certificate-Signing-Request #=============================================================================== openssl req -config <(cat "${OPENSSLCONF}" <(printf "[SAN]\nsubjectAltName=DNS:`echo ${DNS_DOMAIN} | sed "s/ /,DNS:/g"`")) \ --new -sha256 -key "${CONFIDENTIAL}" -out "${REQUESTFILE}" -outform der -reqexts SAN -subj "/" + -new -sha256 -key "${CONFIDENTIAL}" -out "${REQUESTFILE}" -outform der -reqexts SAN -subj "/" #=============================================================================== -# Checking if Certificate-Signing-Request (CSR) was successfully created +# Check if Certificate-Signing-Request creation failed #=============================================================================== if [ $? != 0 ]; then echo "$0: Certificate-Signing-Request (CSR) could not be created!" >&2 @@ -98,18 +91,20 @@ if [ $? != 0 ]; then fi #=============================================================================== -# Execute defined command BEFORE the ACME challenge is started +# Run defined "BEFORE" command #=============================================================================== -[ ! -z "${LETSENCRYPT_COMMAND_BEFORE}" ] && eval $LETSENCRYPT_COMMAND_BEFORE +if [ ! -z "${LETSENCRYPT_COMMAND_BEFORE}" ]; then + eval $LETSENCRYPT_COMMAND_BEFORE +fi #=============================================================================== -# Execute Let's Encrypt and accomplish the ACME challenge to get the certificate +# Run Certbot to accomplish the ACME challenge to get the certificate #=============================================================================== certbot certonly --authenticator standalone --server "${LETSENCRYPT_ENDPOINT}" --csr "${REQUESTFILE}" \ ---cert-path "${CERTIFICATE_ONLY}.$$" --fullchain-path "${CERTIFICATE_FULL}.$$" --chain-path "${INTERMEDIATE}.$$" + --cert-path "${CERTIFICATE_ONLY}.$$" --fullchain-path "${CERTIFICATE_FULL}.$$" --chain-path "${INTERMEDIATE}.$$" #=============================================================================== -# Checking if Certbot has successfully accomplished the ACME challenge +# Check if Certbot failed to obtain a certificate #=============================================================================== if [ $? != 0 ]; then echo "$0: Certbot could not successfully accomplish the ACME challenge." >&2 @@ -117,19 +112,25 @@ if [ $? != 0 ]; then fi #=============================================================================== -# Replace previous certificates with the new obtained certificate files +# Replace old certificate files (if any) with the newly obtained ones #=============================================================================== -[ -f "${INTERMEDIATE}.$$" ] && mv "${INTERMEDIATE}.$$" "${INTERMEDIATE}" -[ -f "${CERTIFICATE_ONLY}.$$" ] && mv "${CERTIFICATE_ONLY}.$$" "${CERTIFICATE_ONLY}" -[ -f "${CERTIFICATE_FULL}.$$" ] && mv "${CERTIFICATE_FULL}.$$" "${CERTIFICATE_FULL}" +for file in "${INTERMEDIATE}" "${CERTIFICATE_ONLY}" "${CERTIFICATE_FULL}"; do + if [ -f "${file}.$$" ]; then + mv "${file}.$$" "${file}" + fi +done #=============================================================================== -# Adjust the UNIX permissions with owner and group for the new created files +# Inherit permissions of private key to new certificate files #=============================================================================== -chmod --reference "${CONFIDENTIAL}" "${INTERMEDIATE}" "${CERTIFICATE_ONLY}" "${CERTIFICATE_FULL}" -chown --reference "${CONFIDENTIAL}" "${INTERMEDIATE}" "${CERTIFICATE_ONLY}" "${CERTIFICATE_FULL}" +for command in "chmod" "chown"; do + $command --reference "${CONFIDENTIAL}" \ + "${INTERMEDIATE}" "${CERTIFICATE_ONLY}" "${CERTIFICATE_FULL}" +done #=============================================================================== -# Execute defined command AFTER the ACME challenge is completed +# Run defined "AFTER" command #=============================================================================== -[ ! -z "${LETSENCRYPT_COMMAND_AFTER}" ] && eval $LETSENCRYPT_COMMAND_AFTER +if [ ! -z "${LETSENCRYPT_COMMAND_AFTER}" ]; then + eval $LETSENCRYPT_COMMAND_AFTER +fi -- cgit v1.2.3