From 5744c868355e8c1bebee2f7900c57817d3e39529 Mon Sep 17 00:00:00 2001 From: Thomas Lange Date: Sat, 22 Oct 2022 16:39:43 +0200 Subject: Put script into package directory Put the script into the package directory which reflects the directory structure of /usr/local. This makes it easily possible to install the script to /usr/local/sbin with a tool like *GNU Stow*. --- package/sbin/painless-le | 122 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100755 package/sbin/painless-le (limited to 'package/sbin/painless-le') diff --git a/package/sbin/painless-le b/package/sbin/painless-le new file mode 100755 index 0000000..19743c0 --- /dev/null +++ b/package/sbin/painless-le @@ -0,0 +1,122 @@ +#!/bin/bash +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# Painless Let's Encrypt Certificate Issuing [Thomas Lange ] # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# +# # +# 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}"). # +# # +# OPTION [-i]: Full path to the install directory for the certificates. # +# OPTION [-h]: List of hostnames for the certificate: example.org[:...] # +# OPTION [-K]: Filename for the existing private key relative to [-i] # +# OPTION [-I]: Target filename for the intermediate cert relative to [-i] # +# OPTION [-C]: Target filename for the certificate only file relative to [-i] # +# OPTION [-F]: Target filename for the certificate full file relative to [-i] # +# # +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%# + +#=============================================================================== +# Parsing command-line arguments with the getopts shell builtin +#=============================================================================== +while getopts :i:h:K:I:C:F: option +do + case $option in + i) ARGUMENT_DIRECTORY="$OPTARG" ;; + h) ARGUMENT_HOSTNAMES="$OPTARG" ;; + K) ARGUMENT_CONFIDENTIAL="$OPTARG" ;; + I) ARGUMENT_INTERMEDIATE="$OPTARG" ;; + C) ARGUMENT_CERTIFICATE_ONLY="$OPTARG" ;; + F) ARGUMENT_CERTIFICATE_FULL="$OPTARG" ;; + esac +done; shift $((OPTIND-1)) + +#=============================================================================== +# Checking if all required command-line arguments are provided +#=============================================================================== +[ -z "${ARGUMENT_DIRECTORY}" ] && echo "$0: Missing argument: [-i directory]" >&2 +[ -z "${ARGUMENT_HOSTNAMES}" ] && echo "$0: Missing argument: [-h hostnames]" >&2 + +#=============================================================================== +# Abort execution if required command-line argument is missing +#=============================================================================== +[ -z "${ARGUMENT_DIRECTORY}" ] || [ -z "${ARGUMENT_HOSTNAMES}" ] && exit 1 + +#=============================================================================== +# Define the ACME endpoint address +#=============================================================================== +LETSENCRYPT_ENDPOINT="https://acme-v01.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 +#=============================================================================== + OPENSSLCONF="/etc/ssl/openssl.cnf" + REQUESTFILE=`mktemp /tmp/painless-le.XXXXXXXXXX.csr` + CONFIDENTIAL="${ARGUMENT_DIRECTORY%/}/${ARGUMENT_CONFIDENTIAL:-confidential.pem}" + INTERMEDIATE="${ARGUMENT_DIRECTORY%/}/${ARGUMENT_INTERMEDIATE:-intermediate.pem}" +CERTIFICATE_ONLY="${ARGUMENT_DIRECTORY%/}/${ARGUMENT_CERTIFICATE_ONLY:-certificate_only.pem}" +CERTIFICATE_FULL="${ARGUMENT_DIRECTORY%/}/${ARGUMENT_CERTIFICATE_FULL:-certificate_full.pem}" + +#=============================================================================== +# Delete Certificate-Signing-Request (CSR) file on exit +#=============================================================================== +trap 'rm ${REQUESTFILE}' EXIT + +#=============================================================================== +# Generate Certificate-Signing-Request (CSR) +#=============================================================================== +openssl req -config <(cat "${OPENSSLCONF}" <(printf "[SAN]\nsubjectAltName=DNS:`echo ${ARGUMENT_HOSTNAMES} | sed "s/:/,DNS:/g"`")) \ +-new -sha256 -key "${CONFIDENTIAL}" -out "${REQUESTFILE}" -outform der -reqexts SAN -subj "/" + +#=============================================================================== +# Checking if Certificate-Signing-Request (CSR) was successfully created +#=============================================================================== +if [ $? != 0 ]; then + echo "$0: Certificate-Signing-Request (CSR) could not be created!" >&2 + exit 1 +fi + +#=============================================================================== +# Execute defined command BEFORE the ACME challenge is started +#=============================================================================== +[ ! -z "${LETSENCRYPT_COMMAND_BEFORE}" ] && eval $LETSENCRYPT_COMMAND_BEFORE + +#=============================================================================== +# Execute Let's Encrypt and 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}.$$" + +#=============================================================================== +# Checking if Certbot has successfully accomplished the ACME challenge +#=============================================================================== +if [ $? != 0 ]; then + echo "$0: Certbot could not successfully accomplish the ACME challenge." >&2 + exit 1 +fi + +#=============================================================================== +# Replace previous certificates with the new obtained certificate files +#=============================================================================== +[ -f "${INTERMEDIATE}.$$" ] && mv "${INTERMEDIATE}.$$" "${INTERMEDIATE}" +[ -f "${CERTIFICATE_ONLY}.$$" ] && mv "${CERTIFICATE_ONLY}.$$" "${CERTIFICATE_ONLY}" +[ -f "${CERTIFICATE_FULL}.$$" ] && mv "${CERTIFICATE_FULL}.$$" "${CERTIFICATE_FULL}" + +#=============================================================================== +# Adjust the UNIX permissions with owner and group for the new created files +#=============================================================================== +chmod --reference "${CONFIDENTIAL}" "${INTERMEDIATE}" "${CERTIFICATE_ONLY}" "${CERTIFICATE_FULL}" +chown --reference "${CONFIDENTIAL}" "${INTERMEDIATE}" "${CERTIFICATE_ONLY}" "${CERTIFICATE_FULL}" + +#=============================================================================== +# Execute defined command AFTER the ACME challenge is completed +#=============================================================================== +[ ! -z "${LETSENCRYPT_COMMAND_AFTER}" ] && eval $LETSENCRYPT_COMMAND_AFTER -- cgit v1.2.3