From d4c9ce451059048792cad7b6add776924fde1d6a Mon Sep 17 00:00:00 2001 From: Dennis Koot Date: Fri, 29 Jan 2016 14:03:53 +0100 Subject: [PATCH 01/13] option to refetch certificate --- getssl | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/getssl b/getssl index 558c487..92b6679 100755 --- a/getssl +++ b/getssl @@ -13,7 +13,7 @@ # GNU General Public License at for # more details. -# Usage: getssl [-h|--help] [-d|--debug] [-c] [-a|--all] [-w working_dir] domain +# Usage: getssl [-h|--help] [-d|--debug] [-c] [-r|--refetch] [-a|--all] [-w working_dir] domain # Revision history: # 2016-01-08 Created (v0.1) @@ -32,6 +32,7 @@ # 2016-01-29 Fix ssh-reload-command, extra waiting for DNS-challenge, add some error_exit and cleanup help message (v0.14) # 2016-01-29 added -a|--all option to renew all configured certificates (v0.15) # 2016-01-29 added option for eliptic curve keys (v0.16) +# 2016-01-29 added -r|--refetch option to refetch certificate from site (v0.16) # --------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -50,6 +51,7 @@ RENEW_ALLOW="30" PRIVATE_KEY_ALG="rsa" _USE_DEBUG=0 _CREATE_CONFIG=0 +_REFETCH_CERT=0 _RENEW_ALL=0 clean_up() { # Perform pre-exit housekeeping @@ -83,7 +85,7 @@ signal_exit() { # Handle trapped signals } usage() { - echo -e "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c] [-a|--all] [-w working_dir] domain" + echo -e "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c] [-r|--refetch] [-a|--all] [-w working_dir] domain" } log() { @@ -130,7 +132,7 @@ write_getssl_template() { ACCOUNT_KEY_LENGTH=4096 ACCOUNT_KEY="$WORKING_DIR/account.key" PRIVATE_KEY_ALG="rsa" - + # The command needed to reload apache / nginx or whatever you use #RELOAD_CMD="" # The time period within which you want to allow renewal of a certificate - this prevents hitting some of the rate limits. @@ -163,7 +165,7 @@ write_domain_template() { #ACCOUNT_KEY_LENGTH=4096 #ACCOUNT_KEY="$WORKING_DIR/account.key" PRIVATE_KEY_ALG="rsa" - + # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS=${EX_SANS} @@ -294,7 +296,8 @@ $(usage) Options: -h, --help Display this help message and exit -d, --debug Outputs debug information - -c, Create default config files + -c, --create Create default config files + -r, --refetch Refetch current certificates from site -a, --all Renew all certificates -w working_dir Working directory @@ -315,10 +318,12 @@ while [[ -n $1 ]]; do _USE_DEBUG=1 ;; -c | --create) _CREATE_CONFIG=1 ;; + -r | --refetch) + _REFETCH_CERT=1 ;; -a | --all) _RENEW_ALL=1 ;; -w) - echo "working directory"; shift; WORKING_DIR="$1" ;; + shift; WORKING_DIR="$1" ;; -* | --*) usage error_exit "Unknown option $1" ;; @@ -357,6 +362,9 @@ if [ ${_RENEW_ALL} -eq 1 ]; then if [ ${_USE_DEBUG} -eq 1 ]; then cmd="$cmd -d" fi + if [ ${_REFETCH_CERT} -eq 1 ]; then + cmd="$cmd -r" + fi cmd="$cmd $dir" debug "CMD: $cmd" @@ -443,6 +451,13 @@ if [ -f "$DOMAIN_DIR/getssl.cfg" ]; then . "$DOMAIN_DIR/getssl.cfg" fi +# refetch the certificate from the server if option is set +if [ ${_REFETCH_CERT} -eq 1 ]; then + info "refetch certificate for $DOMAIN and save to $DOMAIN_DIR/${DOMAIN}.crt" + EX_CERT=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 2>/dev/null) + echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt +fi + if [ -f "$CERT_FILE" ]; then debug "certificate $CERT_FILE exists" enddate=$(openssl x509 -in $CERT_FILE -noout -enddate 2>/dev/null| cut -d= -f 2-) From fd7fd2a37bf3a5d206a76c491bdb6160f9c75888 Mon Sep 17 00:00:00 2001 From: Dennis Koot Date: Fri, 29 Jan 2016 14:47:10 +0100 Subject: [PATCH 02/13] basic domain check --- getssl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 92b6679..7b7e232 100755 --- a/getssl +++ b/getssl @@ -455,7 +455,17 @@ fi if [ ${_REFETCH_CERT} -eq 1 ]; then info "refetch certificate for $DOMAIN and save to $DOMAIN_DIR/${DOMAIN}.crt" EX_CERT=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 2>/dev/null) - echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt + if [ ! -z "${EX_CERT}" ]; then + # check if the certificate is for the right domain + EX_CERT_DOMAIN=$(echo "$EX_CERT" | openssl x509 -noout -subject | sed s/.*CN=//) + if [ "$EX_CERT_DOMAIN" == "$DOMAIN" ] + echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt + else + error_exit "fetched certificate domain-name ($EX_CERT_DOMAIN) does not match $DOMAIN" + fi + else + error_exit "failed to fetch certificate for $DOMAIN" + fi fi if [ -f "$CERT_FILE" ]; then From 295e9714ca9208df1c54ad5e834657c323f030d8 Mon Sep 17 00:00:00 2001 From: Dennis Koot Date: Fri, 29 Jan 2016 14:52:45 +0100 Subject: [PATCH 03/13] configuration variable for refetch --- getssl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/getssl b/getssl index 7b7e232..6428e95 100755 --- a/getssl +++ b/getssl @@ -49,6 +49,7 @@ VALIDATE_VIA_DNS="" RELOAD_CMD="" RENEW_ALLOW="30" PRIVATE_KEY_ALG="rsa" +ALWAYS_REFETCH_CERT="false" _USE_DEBUG=0 _CREATE_CONFIG=0 _REFETCH_CERT=0 @@ -137,6 +138,8 @@ write_getssl_template() { #RELOAD_CMD="" # The time period within which you want to allow renewal of a certificate - this prevents hitting some of the rate limits. RENEW_ALLOW="30" + # Always refetch the certificate from the server before checking expiry + #ALWAYS_REFETCH_CERT="true" # openssl config file. The default should work in most cases. SSLCONF="$SSLCONF" @@ -185,6 +188,8 @@ write_domain_template() { #RELOAD_CMD="" # The time period within which you want to allow renewal of a certificate - this prevents hitting some of the rate limits. #RENEW_ALLOW="30" + # Always refetch the certificate from the server before checking expiry + #ALWAYS_REFETCH_CERT="true" # Use the following 3 variables if you want to validate via DNS #VALIDATE_VIA_DNS="true" @@ -451,6 +456,10 @@ if [ -f "$DOMAIN_DIR/getssl.cfg" ]; then . "$DOMAIN_DIR/getssl.cfg" fi +if [ "$ALWAYS_REFETCH_CERT" == "true" ]; then + _REFETCH_CERT=1 +fi + # refetch the certificate from the server if option is set if [ ${_REFETCH_CERT} -eq 1 ]; then info "refetch certificate for $DOMAIN and save to $DOMAIN_DIR/${DOMAIN}.crt" From a259f51488f27ec8d9ea4b5bb10e21eb2f936bae Mon Sep 17 00:00:00 2001 From: Dennis Koot Date: Fri, 29 Jan 2016 15:15:10 +0100 Subject: [PATCH 04/13] rebased --- README.md | 13 ++++++++++--- getssl | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a915e5b..2bfc9bf 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,16 @@ get an SSL certificate via LetsEncrypt. Suitable for automating the process in This was written as an addition to checkssl for servers to automatically renew certifictes. In addition it allows the running of this script in standard bash ( on a desktop computer, or even virtualbox) and add the checks, and certificates to a remote server ( providing you have an ssh key on the remote server with access). Potentially I can include FTP as an option for uploading as well. ``` -getssl ver. 0.16 +getssl ver. 0.17 Obtain SSL certificates from the letsencrypt.org ACME server -Usage: getssl [-h|--help] [-d|--debug] [-c] [-a|--all] [-w working_dir] domain +Usage: getssl [-h|--help] [-d|--debug] [-c] [-r|--refetch] [-a|--all] [-w working_dir] domain Options: -h, --help Display this help message and exit -d, --debug Outputs debug information - -c, Create default config files + -c, --create Create default config files + -r, --refetch Refetch current certificates from site -a, --all Renew all certificates -w working_dir Working directory ``` @@ -36,11 +37,14 @@ AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf" #ACCOUNT_EMAIL="me@example.com" ACCOUNT_KEY_LENGTH=4096 ACCOUNT_KEY="/home/andy/.getssl/account.key" +PRIVATE_KEY_ALG="rsa" # The command needed to reload apache / nginx or whatever you use #RELOAD_CMD="" # The time period within which you want to allow renewal of a certificate - this prevents hitting some of the rate limits. RENEW_ALLOW="30" +# Always refetch the certificate from the server before checking expiry +#ALWAYS_REFETCH_CERT="true" # openssl config file. The default should work in most cases. SSLCONF="/usr/lib/ssl/openssl.cnf" @@ -69,6 +73,7 @@ then, within the **working directory** there will be a folder for each certifica #ACCOUNT_EMAIL="me@example.com" #ACCOUNT_KEY_LENGTH=4096 #ACCOUNT_KEY="/home/andy/.getssl/account.key" +PRIVATE_KEY_ALG="rsa" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS=www.example.org,example.edu,example.net,example.org,www.example.com,www.example.edu,www.example.net @@ -89,6 +94,8 @@ SANS=www.example.org,example.edu,example.net,example.org,www.example.com,www.exa #RELOAD_CMD="" # The time period within which you want to allow renewal of a certificate - this prevents hitting some of the rate limits. #RENEW_ALLOW="30" +# Always refetch the certificate from the server before checking expiry +#ALWAYS_REFETCH_CERT="true" # Use the following 3 variables if you want to validate via DNS #VALIDATE_VIA_DNS="true" diff --git a/getssl b/getssl index 6428e95..5b698ad 100755 --- a/getssl +++ b/getssl @@ -32,11 +32,11 @@ # 2016-01-29 Fix ssh-reload-command, extra waiting for DNS-challenge, add some error_exit and cleanup help message (v0.14) # 2016-01-29 added -a|--all option to renew all configured certificates (v0.15) # 2016-01-29 added option for eliptic curve keys (v0.16) -# 2016-01-29 added -r|--refetch option to refetch certificate from site (v0.16) +# 2016-01-29 added -r|--refetch option to refetch certificate from site (v0.17) # --------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="0.16" +VERSION="0.17" # defaults CA="https://acme-staging.api.letsencrypt.org" From 508b416383eb45a89c280d1368f88d294b79b2fc Mon Sep 17 00:00:00 2001 From: Dennis Koot Date: Fri, 29 Jan 2016 15:29:39 +0100 Subject: [PATCH 05/13] compare local vs remote after installing --- getssl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/getssl b/getssl index 5b698ad..dc22425 100755 --- a/getssl +++ b/getssl @@ -796,4 +796,13 @@ if [ ! -z "$RELOAD_CMD" ]; then fi fi +# Check if the certificate is installed correctly +CERT_REMOTE=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 -noout -fingerprint 2>/dev/null) +CERT_LOCAL=$(cat "$CERT_FILE" | openssl x509 -noout -fingerprint 2>/dev/null) +if [ "$CERT_LOCAL" == "$CERT_REMOTE" ]; then + info "certificate installed OK on server" +else + error_exit "certificate on server is different from local certificate" +fi + graceful_exit From e3e6ed2cbe604bfda35a4460e3c66af743350a67 Mon Sep 17 00:00:00 2001 From: srvrco Date: Fri, 29 Jan 2016 16:01:57 +0000 Subject: [PATCH 06/13] updating to use server-type --- getssl | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/getssl b/getssl index dc22425..afc19a8 100755 --- a/getssl +++ b/getssl @@ -13,7 +13,7 @@ # GNU General Public License at for # more details. -# Usage: getssl [-h|--help] [-d|--debug] [-c] [-r|--refetch] [-a|--all] [-w working_dir] domain +# Usage: getssl [-h|--help] [-d|--debug] [-c|--create] [-a|--all] [-w working_dir] domain # Revision history: # 2016-01-08 Created (v0.1) @@ -49,7 +49,7 @@ VALIDATE_VIA_DNS="" RELOAD_CMD="" RENEW_ALLOW="30" PRIVATE_KEY_ALG="rsa" -ALWAYS_REFETCH_CERT="false" +SERVER_TYPE="webserver" _USE_DEBUG=0 _CREATE_CONFIG=0 _REFETCH_CERT=0 @@ -86,7 +86,7 @@ signal_exit() { # Handle trapped signals } usage() { - echo -e "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c] [-r|--refetch] [-a|--all] [-w working_dir] domain" + echo -e "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c|--create] [-a|--all] [-w working_dir] domain" } log() { @@ -138,8 +138,9 @@ write_getssl_template() { #RELOAD_CMD="" # The time period within which you want to allow renewal of a certificate - this prevents hitting some of the rate limits. RENEW_ALLOW="30" - # Always refetch the certificate from the server before checking expiry - #ALWAYS_REFETCH_CERT="true" + # Define the server type. If it's a "webserver" then the main website will be checked for certificate expiry + # and also will be checked after an update to confirm correct certificate is running. + #SERVER_TYPE="webserver" # openssl config file. The default should work in most cases. SSLCONF="$SSLCONF" @@ -188,8 +189,9 @@ write_domain_template() { #RELOAD_CMD="" # The time period within which you want to allow renewal of a certificate - this prevents hitting some of the rate limits. #RENEW_ALLOW="30" - # Always refetch the certificate from the server before checking expiry - #ALWAYS_REFETCH_CERT="true" + # Define the server type. If it's a "webserver" then the main website will be checked for certificate expiry + # and also will be checked after an update to confirm correct certificate is running. + #SERVER_TYPE="webserver" # Use the following 3 variables if you want to validate via DNS #VALIDATE_VIA_DNS="true" @@ -302,7 +304,6 @@ Options: -h, --help Display this help message and exit -d, --debug Outputs debug information -c, --create Create default config files - -r, --refetch Refetch current certificates from site -a, --all Renew all certificates -w working_dir Working directory @@ -323,8 +324,6 @@ while [[ -n $1 ]]; do _USE_DEBUG=1 ;; -c | --create) _CREATE_CONFIG=1 ;; - -r | --refetch) - _REFETCH_CERT=1 ;; -a | --all) _RENEW_ALL=1 ;; -w) @@ -367,9 +366,6 @@ if [ ${_RENEW_ALL} -eq 1 ]; then if [ ${_USE_DEBUG} -eq 1 ]; then cmd="$cmd -d" fi - if [ ${_REFETCH_CERT} -eq 1 ]; then - cmd="$cmd -r" - fi cmd="$cmd $dir" debug "CMD: $cmd" @@ -456,24 +452,28 @@ if [ -f "$DOMAIN_DIR/getssl.cfg" ]; then . "$DOMAIN_DIR/getssl.cfg" fi -if [ "$ALWAYS_REFETCH_CERT" == "true" ]; then - _REFETCH_CERT=1 -fi - -# refetch the certificate from the server if option is set -if [ ${_REFETCH_CERT} -eq 1 ]; then - info "refetch certificate for $DOMAIN and save to $DOMAIN_DIR/${DOMAIN}.crt" +# if it's a webserver, connect and obtain the certificate +if [[ ${SERVER_TYPE} == "webserver" ]]; then + info "getting certificate for $DOMAIN" EX_CERT=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 2>/dev/null) - if [ ! -z "${EX_CERT}" ]; then + CERT_REMOTE=$(cat "$EX_CERT" | openssl x509 -noout -fingerprint 2>/dev/null) + CERT_LOCAL=$(cat "$CERT_FILE" | openssl x509 -noout -fingerprint 2>/dev/null) + if [ "$CERT_LOCAL" == "$CERT_REMOTE" ]; then + debug "certificate on server is same as the local cert" + else # check if the certificate is for the right domain - EX_CERT_DOMAIN=$(echo "$EX_CERT" | openssl x509 -noout -subject | sed s/.*CN=//) + EX_CERT_DOMAIN=$(echo "$EX_CERT" | openssl x509 -noout -subject | sed s/.*CN=//) if [ "$EX_CERT_DOMAIN" == "$DOMAIN" ] - echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt + # check renew-date on ex_cert and compare to local ( if local exists) + # if remote has longer to expiry date then + # archive local copy with dates + # copy remote to local echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt + # endif ( if not true, then we want to use the existing local one or renew local depending on dates. ) else + # we probably don't want to exit here .... we probably just want to ignore it and use the local copy + # for example it may be the first time, and we haven't got a valid cert on it yet .... error_exit "fetched certificate domain-name ($EX_CERT_DOMAIN) does not match $DOMAIN" fi - else - error_exit "failed to fetch certificate for $DOMAIN" fi fi From 5070bb696ba8506f5039a758a081f02459cde8c9 Mon Sep 17 00:00:00 2001 From: srvrco Date: Fri, 29 Jan 2016 16:06:43 +0000 Subject: [PATCH 07/13] updated README to relect change to server_type --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2bfc9bf..cee09f6 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ Options: -h, --help Display this help message and exit -d, --debug Outputs debug information -c, --create Create default config files - -r, --refetch Refetch current certificates from site -a, --all Renew all certificates -w working_dir Working directory ``` @@ -43,8 +42,9 @@ PRIVATE_KEY_ALG="rsa" #RELOAD_CMD="" # The time period within which you want to allow renewal of a certificate - this prevents hitting some of the rate limits. RENEW_ALLOW="30" -# Always refetch the certificate from the server before checking expiry -#ALWAYS_REFETCH_CERT="true" +# Define the server type. If it's a "webserver" then the main website will be checked for certificate expiry +# and also will be checked after an update to confirm correct certificate is running. +#SERVER_TYPE="webserver" # openssl config file. The default should work in most cases. SSLCONF="/usr/lib/ssl/openssl.cnf" @@ -94,8 +94,9 @@ SANS=www.example.org,example.edu,example.net,example.org,www.example.com,www.exa #RELOAD_CMD="" # The time period within which you want to allow renewal of a certificate - this prevents hitting some of the rate limits. #RENEW_ALLOW="30" -# Always refetch the certificate from the server before checking expiry -#ALWAYS_REFETCH_CERT="true" +# Define the server type. If it's a "webserver" then the main website will be checked for certificate expiry +# and also will be checked after an update to confirm correct certificate is running. +#SERVER_TYPE="webserver" # Use the following 3 variables if you want to validate via DNS #VALIDATE_VIA_DNS="true" From f1c50416afc4ff055d24fde0c9778c9498dc5837 Mon Sep 17 00:00:00 2001 From: srvrco Date: Fri, 29 Jan 2016 18:10:07 +0000 Subject: [PATCH 08/13] updated logic for checking remote cert .... not yet tested all routes through code --- getssl | 89 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/getssl b/getssl index afc19a8..ad23f9e 100755 --- a/getssl +++ b/getssl @@ -32,7 +32,7 @@ # 2016-01-29 Fix ssh-reload-command, extra waiting for DNS-challenge, add some error_exit and cleanup help message (v0.14) # 2016-01-29 added -a|--all option to renew all configured certificates (v0.15) # 2016-01-29 added option for eliptic curve keys (v0.16) -# 2016-01-29 added -r|--refetch option to refetch certificate from site (v0.17) +# 2016-01-29 added server-type option to use and check cert validity from website (v0.17) # --------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -52,7 +52,6 @@ PRIVATE_KEY_ALG="rsa" SERVER_TYPE="webserver" _USE_DEBUG=0 _CREATE_CONFIG=0 -_REFETCH_CERT=0 _RENEW_ALL=0 clean_up() { # Perform pre-exit housekeeping @@ -136,10 +135,13 @@ write_getssl_template() { # The command needed to reload apache / nginx or whatever you use #RELOAD_CMD="" - # The time period within which you want to allow renewal of a certificate - this prevents hitting some of the rate limits. + # The time period within which you want to allow renewal of a certificate + # this prevents hitting some of the rate limits. RENEW_ALLOW="30" - # Define the server type. If it's a "webserver" then the main website will be checked for certificate expiry - # and also will be checked after an update to confirm correct certificate is running. + + # Define the server type. If it's a "webserver" then the main website + # will be checked for certificate expiry and also will be checked after + # an update to confirm correct certificate is running. #SERVER_TYPE="webserver" # openssl config file. The default should work in most cases. @@ -187,10 +189,13 @@ write_domain_template() { # The command needed to reload apache / nginx or whatever you use #RELOAD_CMD="" - # The time period within which you want to allow renewal of a certificate - this prevents hitting some of the rate limits. - #RENEW_ALLOW="30" - # Define the server type. If it's a "webserver" then the main website will be checked for certificate expiry - # and also will be checked after an update to confirm correct certificate is running. + # The time period within which you want to allow renewal of a certificate + # this prevents hitting some of the rate limits. + RENEW_ALLOW="30" + + # Define the server type. If it's a "webserver" then the main website + # will be checked for certificate expiry and also will be checked after + # an update to confirm correct certificate is running. #SERVER_TYPE="webserver" # Use the following 3 variables if you want to validate via DNS @@ -293,6 +298,16 @@ _requires() { fi } +cert_archive() { + certfile=$1 + enddate=$(openssl x509 -in $certfile -noout -enddate 2>/dev/null| cut -d= -f 2-) + formatted_enddate=$(date -d "${enddate}" +%F) + startdate=$(openssl x509 -in $xertfile -noout -startdate 2>/dev/null| cut -d= -f 2-) + formatted_startdate=$(date -d "${startdate}" +%F) + mv "${certfile}" "${certfile}_${formatted_startdate}_${formatted_enddate}" + debug "backing up old certificate file to ${certfile}_${formatted_startdate}_${formatted_enddate}" +} + help_message() { cat <<- _EOF_ $PROGNAME ver. $VERSION @@ -456,24 +471,40 @@ fi if [[ ${SERVER_TYPE} == "webserver" ]]; then info "getting certificate for $DOMAIN" EX_CERT=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 2>/dev/null) - CERT_REMOTE=$(cat "$EX_CERT" | openssl x509 -noout -fingerprint 2>/dev/null) - CERT_LOCAL=$(cat "$CERT_FILE" | openssl x509 -noout -fingerprint 2>/dev/null) - if [ "$CERT_LOCAL" == "$CERT_REMOTE" ]; then - debug "certificate on server is same as the local cert" - else - # check if the certificate is for the right domain - EX_CERT_DOMAIN=$(echo "$EX_CERT" | openssl x509 -noout -subject | sed s/.*CN=//) - if [ "$EX_CERT_DOMAIN" == "$DOMAIN" ] - # check renew-date on ex_cert and compare to local ( if local exists) - # if remote has longer to expiry date then - # archive local copy with dates - # copy remote to local echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt - # endif ( if not true, then we want to use the existing local one or renew local depending on dates. ) - else - # we probably don't want to exit here .... we probably just want to ignore it and use the local copy - # for example it may be the first time, and we haven't got a valid cert on it yet .... - error_exit "fetched certificate domain-name ($EX_CERT_DOMAIN) does not match $DOMAIN" + if [ ! -z "$EX_CERT" ]; then + if [ -f "$CERT_FILE" ]; then #if local exists + CERT_REMOTE=$(echo "$EX_CERT" | openssl x509 -noout -fingerprint 2>/dev/null) + CERT_LOCAL=$(cat "$CERT_FILE" | openssl x509 -noout -fingerprint 2>/dev/null) + if [ "$CERT_LOCAL" == "$CERT_REMOTE" ]; then + debug "certificate on server is same as the local cert" + else + # check if the certificate is for the right domain + EX_CERT_DOMAIN=$(echo "$EX_CERT" | openssl x509 -noout -subject | sed s/.*CN=//) + if [ "$EX_CERT_DOMAIN" == "$DOMAIN" ]; then + # check renew-date on ex_cert and compare to local ( if local exists) + enddate_ex=$(echo "$EX_CERT" | openssl x509 -noout -enddate 2>/dev/null| cut -d= -f 2-) + enddate_lc=$(cat "$CERT_FILE" | openssl x509 -noout -enddate 2>/dev/null| cut -d= -f 2-) + if [ $(date -d "$enddate_ex" +%s) -gt $(date -d "$enddate_lc" +%s) ]; then + #remote has longer to expiry date then + debug "backing up old certificate file to ${CERT_FILE}_${formatted_startdate}_${formatted_enddate}" + cert_archive "$CERT_FILE" + debug "copying remote cert to local" + echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt + else + info "remote expires sooner than local ..... " + # remote expires sooner than local + # somehow need to tell it to potentially just upload the local ..... + fi + else + info "Certificate on remote domain does not match domain, ignoring current remote certificate" + fi + fi + else + info "local cert doesn't exist, saving copy from remote" + echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt fi + else + info "no certificate obtained from host" fi fi @@ -484,11 +515,7 @@ if [ -f "$CERT_FILE" ]; then if [[ $(date -d "${RENEW_ALLOW} days" +%s) -lt $(date -d "$enddate" +%s) ]]; then error_exit "existing certificate ( $CERT_FILE ) is still valid for more than $RENEW_ALLOW days - aborting" else - formatted_enddate=$(date -d "${enddate}" +%F) - startdate=$(openssl x509 -in $CERT_FILE -noout -startdate 2>/dev/null| cut -d= -f 2-) - formatted_startdate=$(date -d "${startdate}" +%F) - mv "${CERT_FILE}" "${CERT_FILE}_${formatted_startdate}_${formatted_enddate}" - debug "backing up old certificate file to ${CERT_FILE}_${formatted_startdate}_${formatted_enddate}" + cert_archive "${CERT_FILE}" fi fi fi From a629223a9a7a9e34b77b2edf17330bba56d7e197 Mon Sep 17 00:00:00 2001 From: srvrco Date: Fri, 29 Jan 2016 18:50:51 +0000 Subject: [PATCH 09/13] corrected type and removed surpless line --- getssl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/getssl b/getssl index ad23f9e..86edd8f 100755 --- a/getssl +++ b/getssl @@ -302,7 +302,7 @@ cert_archive() { certfile=$1 enddate=$(openssl x509 -in $certfile -noout -enddate 2>/dev/null| cut -d= -f 2-) formatted_enddate=$(date -d "${enddate}" +%F) - startdate=$(openssl x509 -in $xertfile -noout -startdate 2>/dev/null| cut -d= -f 2-) + startdate=$(openssl x509 -in $certfile -noout -startdate 2>/dev/null| cut -d= -f 2-) formatted_startdate=$(date -d "${startdate}" +%F) mv "${certfile}" "${certfile}_${formatted_startdate}_${formatted_enddate}" debug "backing up old certificate file to ${certfile}_${formatted_startdate}_${formatted_enddate}" @@ -486,7 +486,6 @@ if [[ ${SERVER_TYPE} == "webserver" ]]; then enddate_lc=$(cat "$CERT_FILE" | openssl x509 -noout -enddate 2>/dev/null| cut -d= -f 2-) if [ $(date -d "$enddate_ex" +%s) -gt $(date -d "$enddate_lc" +%s) ]; then #remote has longer to expiry date then - debug "backing up old certificate file to ${CERT_FILE}_${formatted_startdate}_${formatted_enddate}" cert_archive "$CERT_FILE" debug "copying remote cert to local" echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt From ac9f549ccd3ad337e579194d25717226edc5d21b Mon Sep 17 00:00:00 2001 From: srvrco Date: Sat, 30 Jan 2016 08:59:29 +0000 Subject: [PATCH 10/13] incuded --force option for when you want to add an additional SAN to a cert --- getssl | 153 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 83 insertions(+), 70 deletions(-) diff --git a/getssl b/getssl index 86edd8f..bfd5a4e 100755 --- a/getssl +++ b/getssl @@ -13,7 +13,7 @@ # GNU General Public License at for # more details. -# Usage: getssl [-h|--help] [-d|--debug] [-c|--create] [-a|--all] [-w working_dir] domain +# Usage: getssl [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-w working_dir] domain # Revision history: # 2016-01-08 Created (v0.1) @@ -53,6 +53,7 @@ SERVER_TYPE="webserver" _USE_DEBUG=0 _CREATE_CONFIG=0 _RENEW_ALL=0 +_FORCE_RENEW=0 clean_up() { # Perform pre-exit housekeeping if [ ! -z "$DOMAIN_DIR" ]; then @@ -85,7 +86,7 @@ signal_exit() { # Handle trapped signals } usage() { - echo -e "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c|--create] [-a|--all] [-w working_dir] domain" + echo -e "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-w working_dir] domain" } log() { @@ -257,9 +258,11 @@ send_signed_request() { } copy_file_to_location() { - from=$1 - to=$2 + cert=$1 + from=$2 + to=$3 if [ ! -z "$to" ]; then + info "copying $cert to $to" debug "copying from $from to $to" if [[ "${to:0:4}" == "ssh:" ]] ; then debug "using scp scp -q $from ${to:4}" @@ -305,24 +308,41 @@ cert_archive() { startdate=$(openssl x509 -in $certfile -noout -startdate 2>/dev/null| cut -d= -f 2-) formatted_startdate=$(date -d "${startdate}" +%F) mv "${certfile}" "${certfile}_${formatted_startdate}_${formatted_enddate}" - debug "backing up old certificate file to ${certfile}_${formatted_startdate}_${formatted_enddate}" + info "archiving old certificate file to ${certfile}_${formatted_startdate}_${formatted_enddate}" } -help_message() { - cat <<- _EOF_ -$PROGNAME ver. $VERSION -Obtain SSL certificates from the letsencrypt.org ACME server - -$(usage) - -Options: - -h, --help Display this help message and exit - -d, --debug Outputs debug information - -c, --create Create default config files - -a, --all Renew all certificates - -w working_dir Working directory +reload_service() { + if [ ! -z "$RELOAD_CMD" ]; then + info "reloading SSL services" + if [[ "${RELOAD_CMD:0:4}" == "ssh:" ]] ; then + sshhost=$(echo "$RELOAD_CMD"| awk -F: '{print $2}') + command=${RELOAD_CMD:(( ${#sshhost} + 5))} + debug "running following comand to reload cert" + debug "ssh $sshhost ${command}" + ssh $sshhost "${command}" 1>/dev/null 2>&1 + else + debug "running reload command $RELOAD_CMD" + $RELOAD_CMD + fi + fi +} -_EOF_ +help_message() { + cat <<- _EOF_ + $PROGNAME ver. $VERSION + Obtain SSL certificates from the letsencrypt.org ACME server + + $(usage) + + Options: + -h, --help Display this help message and exit + -d, --debug Outputs debug information + -c, --create Create default config files + -f, --force Fore renewal of cert (overrides expiry checks) + -a, --all Renew all certificates + -w working_dir Working directory + + _EOF_ return } @@ -339,6 +359,8 @@ while [[ -n $1 ]]; do _USE_DEBUG=1 ;; -c | --create) _CREATE_CONFIG=1 ;; + -f | --force) + _FORCE_RENEW=1 ;; -a | --all) _RENEW_ALL=1 ;; -w) @@ -369,6 +391,10 @@ if [ ${_RENEW_ALL} -eq 1 ]; then error_exit "cannot combine -c|--create with -a|--all" fi + if [ ${_FORCE_RENEW} -eq 1 ]; then + error_exit "cannot combine -f|--force with -a|--all because of rate limits" + fi + if [ ! -d "$WORKING_DIR" ]; then error_exit "working dir not found or not set - $WORKING_DIR" fi @@ -468,10 +494,10 @@ if [ -f "$DOMAIN_DIR/getssl.cfg" ]; then fi # if it's a webserver, connect and obtain the certificate -if [[ ${SERVER_TYPE} == "webserver" ]]; then - info "getting certificate for $DOMAIN" +if [[ "${SERVER_TYPE}" == "webserver" ]] && [ $_FORCE_RENEW -eq 0 ]; then + info "getting certificate for $DOMAIN from webserver" EX_CERT=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 2>/dev/null) - if [ ! -z "$EX_CERT" ]; then + if [ ! -z "$EX_CERT" ]; then # if obtained a cert if [ -f "$CERT_FILE" ]; then #if local exists CERT_REMOTE=$(echo "$EX_CERT" | openssl x509 -noout -fingerprint 2>/dev/null) CERT_LOCAL=$(cat "$CERT_FILE" | openssl x509 -noout -fingerprint 2>/dev/null) @@ -485,28 +511,40 @@ if [[ ${SERVER_TYPE} == "webserver" ]]; then enddate_ex=$(echo "$EX_CERT" | openssl x509 -noout -enddate 2>/dev/null| cut -d= -f 2-) enddate_lc=$(cat "$CERT_FILE" | openssl x509 -noout -enddate 2>/dev/null| cut -d= -f 2-) if [ $(date -d "$enddate_ex" +%s) -gt $(date -d "$enddate_lc" +%s) ]; then - #remote has longer to expiry date then + # remote has longer to expiry date than local copy. + # archive local copy and save remote to local cert_archive "$CERT_FILE" - debug "copying remote cert to local" - echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt + debug "copying remote certificate to local" + echo "$EX_CERT" > "$DOMAIN_DIR/${DOMAIN}.crt" else - info "remote expires sooner than local ..... " - # remote expires sooner than local - # somehow need to tell it to potentially just upload the local ..... + info "remote expires sooner than local ..... will attempt to upload from local" + echo "$EX_CERT" > "$DOMAIN_DIR/${DOMAIN}.crt.remote" + cert_archive "$DOMAIN_DIR/${DOMAIN}.crt.remote" + copy_file_to_location "domain certificate" "$CERT_FILE" "$DOMAIN_CERT_LOCATION" + copy_file_to_location "private key" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LOCATION" + copy_file_to_location "CA certificate" "$CA_CERT" "$CA_CERT_LOCATION" + cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}.pem" + copy_file_to_location "full pem" "$TEMP_DIR/${DOMAIN}.pem" "$DOMAIN_PEM_LOCATION" + reload_service fi else info "Certificate on remote domain does not match domain, ignoring current remote certificate" fi fi - else - info "local cert doesn't exist, saving copy from remote" - echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt + else # local cert doesn't exist" + info "local certificate doesn't exist, saving a copy from remote" + echo "$EX_CERT" > "$DOMAIN_DIR/${DOMAIN}.crt" fi else info "no certificate obtained from host" fi fi +# if force renew is set, set the date validity checks to 100000 days +if [ $_FORCE_RENEW -eq 1 ]; then + RENEW_ALLOW=100000 +fi + if [ -f "$CERT_FILE" ]; then debug "certificate $CERT_FILE exists" enddate=$(openssl x509 -in $CERT_FILE -noout -enddate 2>/dev/null| cut -d= -f 2-) @@ -785,50 +823,25 @@ fi # copy certs to the correct location -if [ ! -z "$DOMAIN_CERT_LOCATION" ]; then - info "copying domain certificate to $DOMAIN_CERT_LOCATION" - copy_file_to_location "$CERT_FILE" "$DOMAIN_CERT_LOCATION" -fi - -if [ ! -z "$DOMAIN_KEY_LOCATION" ]; then - info "copying private key to $DOMAIN_KEY_LOCATION" - copy_file_to_location "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LOCATION" -fi - -if [ ! -z "$CA_CERT_LOCATION" ]; then - info "copying CA certificate to $CA_CERT_LOCATION" - copy_file_to_location "$CA_CERT" "$CA_CERT_LOCATION" -fi - -if [ ! -z "$DOMAIN_PEM_LOCATION" ]; then - # Create full pem - cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" "$CA_CERT" > "$DOMAIN_DIR/${DOMAIN}.pem" - copy_file_to_location "$DOMAIN_DIR/${DOMAIN}.pem" "$DOMAIN_PEM_LOCATION" -fi +copy_file_to_location "domain certificate" "$CERT_FILE" "$DOMAIN_CERT_LOCATION" +copy_file_to_location "private key" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LOCATION" +copy_file_to_location "CA certificate" "$CA_CERT" "$CA_CERT_LOCATION" +cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}.pem" +copy_file_to_location "full pem" "$TEMP_DIR/${DOMAIN}.pem" "$DOMAIN_PEM_LOCATION" # Run reload command to restart apache / nginx or whatever system -if [ ! -z "$RELOAD_CMD" ]; then - info "reloading SSL services" - if [[ "${RELOAD_CMD:0:4}" == "ssh:" ]] ; then - sshhost=$(echo "$RELOAD_CMD"| awk -F: '{print $2}') - command=${RELOAD_CMD:(( ${#sshhost} + 5))} - debug "running following comand to reload cert" - debug "ssh $sshhost ${command}" - ssh $sshhost "${command}" 1>/dev/null 2>&1 - else - debug "running reload command $RELOAD_CMD" - $RELOAD_CMD - fi -fi +reload_service # Check if the certificate is installed correctly -CERT_REMOTE=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 -noout -fingerprint 2>/dev/null) -CERT_LOCAL=$(cat "$CERT_FILE" | openssl x509 -noout -fingerprint 2>/dev/null) -if [ "$CERT_LOCAL" == "$CERT_REMOTE" ]; then - info "certificate installed OK on server" -else - error_exit "certificate on server is different from local certificate" +if [[ ${SERVER_TYPE} == "webserver" ]]; then + CERT_REMOTE=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 -noout -fingerprint 2>/dev/null) + CERT_LOCAL=$(cat "$CERT_FILE" | openssl x509 -noout -fingerprint 2>/dev/null) + if [ "$CERT_LOCAL" == "$CERT_REMOTE" ]; then + info "certificate installed OK on server" + else + error_exit "certificate on server is different from local certificate" + fi fi graceful_exit From 8ffc089c3e3ddf3c9ff21f9613e241c64844ddaa Mon Sep 17 00:00:00 2001 From: srvrco Date: Sat, 30 Jan 2016 09:46:31 +0000 Subject: [PATCH 11/13] removed ls dependency, and changed renew-all to check-all --- getssl | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/getssl b/getssl index bfd5a4e..e364f51 100755 --- a/getssl +++ b/getssl @@ -52,7 +52,7 @@ PRIVATE_KEY_ALG="rsa" SERVER_TYPE="webserver" _USE_DEBUG=0 _CREATE_CONFIG=0 -_RENEW_ALL=0 +_CHECK_ALL=0 _FORCE_RENEW=0 clean_up() { # Perform pre-exit housekeeping @@ -338,8 +338,8 @@ help_message() { -h, --help Display this help message and exit -d, --debug Outputs debug information -c, --create Create default config files - -f, --force Fore renewal of cert (overrides expiry checks) - -a, --all Renew all certificates + -f, --force Force renewal of cert (overrides expiry checks) + -a, --all Check all certificates -w working_dir Working directory _EOF_ @@ -362,7 +362,7 @@ while [[ -n $1 ]]; do -f | --force) _FORCE_RENEW=1 ;; -a | --all) - _RENEW_ALL=1 ;; + _CHECK_ALL=1 ;; -w) shift; WORKING_DIR="$1" ;; -* | --*) @@ -384,8 +384,8 @@ _requires xxd _requires base64 _requires nslookup -if [ ${_RENEW_ALL} -eq 1 ]; then - info "Renew all certificates" +if [ ${_CHECK_ALL} -eq 1 ]; then + info "Check all certificates" if [ ${_CREATE_CONFIG} -eq 1 ]; then error_exit "cannot combine -c|--create with -a|--all" @@ -398,16 +398,15 @@ if [ ${_RENEW_ALL} -eq 1 ]; then if [ ! -d "$WORKING_DIR" ]; then error_exit "working dir not found or not set - $WORKING_DIR" fi - - for dir in $(ls "$WORKING_DIR"); do - if [ -d "$WORKING_DIR/$dir" ]; then - info "Renewing $dir" + for dir in ${WORKING_DIR}/*; do + if [ -d "$dir" ]; then + debug "Checking $dir" cmd="$0 -w '$WORKING_DIR'" if [ ${_USE_DEBUG} -eq 1 ]; then cmd="$cmd -d" fi - cmd="$cmd $dir" + cmd="$cmd $(basename $dir)" debug "CMD: $cmd" eval "$cmd" @@ -495,7 +494,7 @@ fi # if it's a webserver, connect and obtain the certificate if [[ "${SERVER_TYPE}" == "webserver" ]] && [ $_FORCE_RENEW -eq 0 ]; then - info "getting certificate for $DOMAIN from webserver" + debug "getting certificate for $DOMAIN from webserver" EX_CERT=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 2>/dev/null) if [ ! -z "$EX_CERT" ]; then # if obtained a cert if [ -f "$CERT_FILE" ]; then #if local exists @@ -532,7 +531,7 @@ if [[ "${SERVER_TYPE}" == "webserver" ]] && [ $_FORCE_RENEW -eq 0 ]; then fi fi else # local cert doesn't exist" - info "local certificate doesn't exist, saving a copy from remote" + debug "local certificate doesn't exist, saving a copy from remote" echo "$EX_CERT" > "$DOMAIN_DIR/${DOMAIN}.crt" fi else @@ -548,10 +547,12 @@ fi if [ -f "$CERT_FILE" ]; then debug "certificate $CERT_FILE exists" enddate=$(openssl x509 -in $CERT_FILE -noout -enddate 2>/dev/null| cut -d= -f 2-) + debug "enddate is $enddate" if [[ "$enddate" != "-" ]]; then if [[ $(date -d "${RENEW_ALLOW} days" +%s) -lt $(date -d "$enddate" +%s) ]]; then - error_exit "existing certificate ( $CERT_FILE ) is still valid for more than $RENEW_ALLOW days - aborting" + error_exit "certificate for $DOMAIN is still valid for more than $RENEW_ALLOW days" else + debug "certificate for $DOMAIN needs renewal" cert_archive "${CERT_FILE}" fi fi From 9777f1cd46b71872e80dd9c8ba1018c081f3e34c Mon Sep 17 00:00:00 2001 From: srvrco Date: Sat, 30 Jan 2016 09:55:33 +0000 Subject: [PATCH 12/13] updated README to add --force option and change renew-all to check-all --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cee09f6..31a2418 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ Options: -h, --help Display this help message and exit -d, --debug Outputs debug information -c, --create Create default config files - -a, --all Renew all certificates + -f, --force Force renewal of cert (overrides expiry checks) + -a, --all Check all certificates -w working_dir Working directory ``` From 97c8ac41b9ca56f4a0449db37e864a127051ca6d Mon Sep 17 00:00:00 2001 From: srvrco Date: Sat, 30 Jan 2016 15:17:12 +0000 Subject: [PATCH 13/13] updated after using shellcheck standard check --- getssl | 117 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 62 insertions(+), 55 deletions(-) diff --git a/getssl b/getssl index e364f51..cf0ffad 100755 --- a/getssl +++ b/getssl @@ -90,7 +90,7 @@ usage() { } log() { - echo "[$(date +%Y-%m-%d\ %H:%M:%S)] $*" >> ${PROGNAME}.log + echo "[$(date +%Y-%m-%d\ %H:%M:%S)] $*" >> "${PROGNAME}.log" } debug() { @@ -105,7 +105,7 @@ info() { _b64() { __n=$(cat) - echo $__n | tr '/+' '_-' | tr -d '= ' + echo "$__n" | tr '/+' '_-' | tr -d '= ' } write_openssl_conf() { @@ -222,7 +222,7 @@ send_signed_request() { if [ ${_USE_DEBUG} -eq 1 ]; then CURL="$CURL --trace-ascii $dp " fi - payload64=$(echo -n $payload | base64 -w 0 | _b64) + payload64=$(echo -n "$payload" | base64 -w 0 | _b64) debug payload64 "$payload64" nonceurl="$CA/directory" @@ -233,7 +233,7 @@ send_signed_request() { protected=$(echo -n "$HEADERPLACE" | sed "s/NONCE/$nonce/" ) debug protected "$protected" - protected64=$( echo -n $protected | base64 -w 0 | _b64) + protected64=$( echo -n "$protected" | base64 -w 0 | _b64) debug protected64 "$protected64" sig=$(echo -n "$protected64.$payload64" | openssl dgst -sha256 -sign "$ACCOUNT_KEY" | base64 -w 0 | _b64) @@ -243,16 +243,16 @@ send_signed_request() { debug body "$body" if [ "$needbase64" ] ; then - response="$($CURL -X POST --data "$body" $url | base64 -w 0)" + response=$($CURL -X POST --data "$body" "$url" | base64 -w 0) else - response="$($CURL -X POST --data "$body" $url)" + response=$($CURL -X POST --data "$body" "$url") fi - responseHeaders="$(sed 's/\r//g' $CURL_HEADER)" + responseHeaders=$(sed 's/\r//g' "$CURL_HEADER") debug responseHeaders "$responseHeaders" debug response "$response" - code="$(grep ^HTTP $CURL_HEADER | tail -1 | cut -d " " -f 2)" + code=$(grep ^HTTP "$CURL_HEADER" | tail -1 | cut -d " " -f 2) debug code "$code" } @@ -266,15 +266,15 @@ copy_file_to_location() { debug "copying from $from to $to" if [[ "${to:0:4}" == "ssh:" ]] ; then debug "using scp scp -q $from ${to:4}" - scp -q $from ${to:4} >/dev/null 2>&1 + scp -q "$from" "${to:4}" >/dev/null 2>&1 if [ $? -gt 0 ]; then error_exit "problem copying file to the server using scp. scp $from ${to:4}" fi else - mkdir -p "$(dirname $to)" + mkdir -p "$(dirname "$to")" if [ $? -gt 0 ]; then - error_exit "cannot create ACL directory $(basename $to)" + error_exit "cannot create ACL directory $(basename "$to")" fi cp "$from" "$to" fi @@ -285,16 +285,16 @@ copy_file_to_location() { getcr() { url="$1" debug url "$url" - response="$(curl --silent $url)" + response=$(curl --silent "$url") ret=$? debug response "$response" - code="$(echo $response | grep -o '"status":[0-9]\+' | cut -d : -f 2)" + code=$(echo "$response" | grep -o '"status":[0-9]\+' | cut -d : -f 2) debug code "$code" return $ret } _requires() { - result=$(which $1 2>/dev/null) + result=$(which "$1" 2>/dev/null) debug "checking for required $1 ... $result" if [ -z "$result" ]; then error_exit "This script requires $1 installed" @@ -303,9 +303,9 @@ _requires() { cert_archive() { certfile=$1 - enddate=$(openssl x509 -in $certfile -noout -enddate 2>/dev/null| cut -d= -f 2-) + enddate=$(openssl x509 -in "$certfile" -noout -enddate 2>/dev/null| cut -d= -f 2-) formatted_enddate=$(date -d "${enddate}" +%F) - startdate=$(openssl x509 -in $certfile -noout -startdate 2>/dev/null| cut -d= -f 2-) + startdate=$(openssl x509 -in "$certfile" -noout -startdate 2>/dev/null| cut -d= -f 2-) formatted_startdate=$(date -d "${startdate}" +%F) mv "${certfile}" "${certfile}_${formatted_startdate}_${formatted_enddate}" info "archiving old certificate file to ${certfile}_${formatted_startdate}_${formatted_enddate}" @@ -319,7 +319,8 @@ reload_service() { command=${RELOAD_CMD:(( ${#sshhost} + 5))} debug "running following comand to reload cert" debug "ssh $sshhost ${command}" - ssh $sshhost "${command}" 1>/dev/null 2>&1 + # shellcheck disable=SC2029 + ssh "$sshhost" "${command}" 1>/dev/null 2>&1 else debug "running reload command $RELOAD_CMD" $RELOAD_CMD @@ -406,7 +407,7 @@ if [ ${_CHECK_ALL} -eq 1 ]; then if [ ${_USE_DEBUG} -eq 1 ]; then cmd="$cmd -d" fi - cmd="$cmd $(basename $dir)" + cmd="$cmd $(basename "$dir")" debug "CMD: $cmd" eval "$cmd" @@ -452,11 +453,11 @@ if [ ${_CREATE_CONFIG} -eq 1 ]; then info "domain config already exists $DOMAIN_DIR/getssl.cfg" else info "creating domain config file in $DOMAIN_DIR/getssl.cfg" - EX_CERT=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 2>/dev/null) + EX_CERT=$(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:443" 2>/dev/null | openssl x509 2>/dev/null) EX_SANS="www.${DOMAIN}" if [ ! -z "${EX_CERT}" ]; then if [ ! -f "$DOMAIN_DIR/${DOMAIN}.crt" ]; then - echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt + echo "$EX_CERT" > "$DOMAIN_DIR/${DOMAIN}.crt" fi EX_SANS=$(echo "$EX_CERT" | openssl x509 -noout -text 2>/dev/null| grep "Subject Alternative Name" -A2 \ | grep -Eo "DNS:[a-zA-Z 0-9.-]*" | sed "s@DNS:$DOMAIN@@g" | grep -v '^$' | cut -c 5-) @@ -495,11 +496,11 @@ fi # if it's a webserver, connect and obtain the certificate if [[ "${SERVER_TYPE}" == "webserver" ]] && [ $_FORCE_RENEW -eq 0 ]; then debug "getting certificate for $DOMAIN from webserver" - EX_CERT=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 2>/dev/null) + EX_CERT=$(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:443" 2>/dev/null | openssl x509 2>/dev/null) if [ ! -z "$EX_CERT" ]; then # if obtained a cert if [ -f "$CERT_FILE" ]; then #if local exists CERT_REMOTE=$(echo "$EX_CERT" | openssl x509 -noout -fingerprint 2>/dev/null) - CERT_LOCAL=$(cat "$CERT_FILE" | openssl x509 -noout -fingerprint 2>/dev/null) + CERT_LOCAL=$(openssl x509 -noout -fingerprint < "$CERT_FILE" 2>/dev/null) if [ "$CERT_LOCAL" == "$CERT_REMOTE" ]; then debug "certificate on server is same as the local cert" else @@ -508,8 +509,8 @@ if [[ "${SERVER_TYPE}" == "webserver" ]] && [ $_FORCE_RENEW -eq 0 ]; then if [ "$EX_CERT_DOMAIN" == "$DOMAIN" ]; then # check renew-date on ex_cert and compare to local ( if local exists) enddate_ex=$(echo "$EX_CERT" | openssl x509 -noout -enddate 2>/dev/null| cut -d= -f 2-) - enddate_lc=$(cat "$CERT_FILE" | openssl x509 -noout -enddate 2>/dev/null| cut -d= -f 2-) - if [ $(date -d "$enddate_ex" +%s) -gt $(date -d "$enddate_lc" +%s) ]; then + enddate_lc=$(openssl x509 -noout -enddate < "$CERT_FILE" 2>/dev/null| cut -d= -f 2-) + if [ "$(date -d "$enddate_ex" +%s)" -gt "$(date -d "$enddate_lc" +%s)" ]; then # remote has longer to expiry date than local copy. # archive local copy and save remote to local cert_archive "$CERT_FILE" @@ -546,7 +547,7 @@ fi if [ -f "$CERT_FILE" ]; then debug "certificate $CERT_FILE exists" - enddate=$(openssl x509 -in $CERT_FILE -noout -enddate 2>/dev/null| cut -d= -f 2-) + enddate=$(openssl x509 -in "$CERT_FILE" -noout -enddate 2>/dev/null| cut -d= -f 2-) debug "enddate is $enddate" if [[ "$enddate" != "-" ]]; then if [[ $(date -d "${RENEW_ALLOW} days" +%s) -lt $(date -d "$enddate" +%s) ]]; then @@ -574,19 +575,22 @@ fi if [ -f "$DOMAIN_DIR/${DOMAIN}.key" ]; then debug "domain key exists at $DOMAIN_DIR/${DOMAIN}.key - skipping generation" # check validity of domain key - if [ "$(openssl rsa -noout -text -in $DOMAIN_DIR/${DOMAIN}.key|head -1)" != "Private-Key: ($DOMAIN_KEY_LENGTH bit)" ]; then + cert_key_len=$(openssl rsa -noout -text -in "$DOMAIN_DIR/${DOMAIN}.key"|head -1) + debug "existing certificate key has header $cert_key_len" + cert_key_req="Private-Key: ($DOMAIN_KEY_LENGTH bit)" + if [ "$cert_key_len" != "$cert_key_req" ]; then error_exit "$DOMAIN_DIR/${DOMAIN}.key does not appear to be an appropriate private key - aborting" fi else info "creating domain key - $DOMAIN_DIR/${DOMAIN}.key" - openssl genrsa $DOMAIN_KEY_LENGTH > $DOMAIN_DIR/${DOMAIN}.key + openssl genrsa "$DOMAIN_KEY_LENGTH" > "$DOMAIN_DIR/${DOMAIN}.key" fi #create SAN if [ -z "$SANS" ]; then - SANLIST="[SAN]\nsubjectAltName=DNS:${DOMAIN}" + SANLIST="ubjectAltName=DNS:${DOMAIN}" else - SANLIST="[SAN]\nsubjectAltName=DNS:${DOMAIN},DNS:${SANS//,/,DNS:}" + SANLIST="subjectAltName=DNS:${DOMAIN},DNS:${SANS//,/,DNS:}" fi debug "created SAN list = $SANLIST" @@ -594,28 +598,29 @@ debug "created SAN list = $SANLIST" if [ -f "$DOMAIN_DIR/${DOMAIN}.csr" ]; then debug "domain csr exists at - $DOMAIN_DIR/${DOMAIN}.csr - skipping generation" #check csr is valid for domain - if [ "$(openssl req -noout -text -in $DOMAIN_DIR/${DOMAIN}.csr| grep -o DNS:${DOMAIN})" != "DNS:${DOMAIN}" ]; then + domains_in_csr=$(openssl req -noout -text -in "$DOMAIN_DIR/${DOMAIN}.csr"| grep -o "DNS:${DOMAIN}") + if [ "$domains_in_csr" != "DNS:${DOMAIN}" ]; then error_exit "existing csr at $DOMAIN_DIR/${DOMAIN}.csr does not appear to be valid for ${DOMAIN} - aborting" fi else debug "creating domain csr - $DOMAIN_DIR/${DOMAIN}.csr" - openssl req -new -sha256 -key $DOMAIN_DIR/${DOMAIN}.key -subj "/" -reqexts SAN -config \ - <(cat $SSLCONF <(printf "$SANLIST")) > $DOMAIN_DIR/${DOMAIN}.csr + openssl req -new -sha256 -key "$DOMAIN_DIR/${DOMAIN}.key" -subj "/" -reqexts SAN -config \ + <(cat "$SSLCONF" <(printf "[SAN]\n%s" "$SANLIST")) > "$DOMAIN_DIR/${DOMAIN}.csr" fi # use account key to register with CA -pub_exp=$(openssl rsa -in $ACCOUNT_KEY -noout -text | grep "^publicExponent:"| cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1) +pub_exp=$(openssl rsa -in "$ACCOUNT_KEY" -noout -text | grep "^publicExponent:"| cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1) if [ "${#pub_exp}" == "5" ] ; then pub_exp=0$pub_exp fi debug pub_exp "$pub_exp" -e=$(echo $pub_exp | xxd -r -p | base64) +e=$(echo "$pub_exp" | xxd -r -p | base64) debug e "$e" -modulus=$(openssl rsa -in $ACCOUNT_KEY -modulus -noout | cut -d '=' -f 2 ) -n=$(echo $modulus| xxd -r -p | base64 -w 0 | _b64 ) +modulus=$(openssl rsa -in "$ACCOUNT_KEY" -modulus -noout | cut -d '=' -f 2 ) +n=$(echo "$modulus"| xxd -r -p | base64 -w 0 | _b64 ) jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}' @@ -635,7 +640,7 @@ send_signed_request "$CA/acme/new-reg" "$regjson" if [ "$code" == "" ] || [ "$code" == '201' ] ; then info "Registered" - echo $response > $TEMP_DIR/account.json + echo "$response" > "$TEMP_DIR/account.json" elif [ "$code" == '409' ] ; then debug "Already registered" else @@ -669,7 +674,7 @@ for d in $alldomains; do fi if [[ $VALIDATE_VIA_DNS == "true" ]]; then # set up the correct DNS token for verification - dns01=$(echo $response | egrep -o '{[^{]*"type":"dns-01"[^}]*') + dns01=$(echo "$response" | egrep -o '{[^{]*"type":"dns-01"[^}]*') debug dns01 "$dns01" token=$(echo "$dns01" | sed 's/,/\n'/g| grep '"token":'| cut -d : -f 2|sed 's/"//g') @@ -687,13 +692,13 @@ for d in $alldomains; do debug "adding dns via command: $DNS_ADD_COMMAND $d $auth_key" $DNS_ADD_COMMAND "$d" "$auth_key" - primary_ns=$(nslookup -type=soa ${d} | grep origin | awk '{print $3}') + primary_ns=$(nslookup -type=soa "${d}" | grep origin | awk '{print $3}') debug primary_ns "$primary_ns" ntries=0 check_dns="fail" while [ "$check_dns" == "fail" ]; do - check_result=$(nslookup -type=txt _acme-challenge.${d} ${primary_ns} | grep ^_acme|awk -F'"' '{ print $2}') + check_result=$(nslookup -type=txt "_acme-challenge.${d}" "${primary_ns}" | grep ^_acme|awk -F'"' '{ print $2}') debug result "$check_result" if [[ "$check_result" == "$auth_key" ]]; then @@ -701,11 +706,11 @@ for d in $alldomains; do debug "checking DNS ... _acme-challenge.$d gave $check_result" if [ "$DNS_EXTRA_WAIT" != "" ]; then info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME-server to check the dns" - sleep $DNS_EXTRA_WAIT + sleep "$DNS_EXTRA_WAIT" fi else if [[ $ntries -lt 100 ]]; then - ntries=$(( $ntries + 1 )) + ntries=$(( ntries + 1 )) info "testing DNS. Attempt $ntries/100 completed. waiting 10 secs before testing verify again" sleep 10 else @@ -716,7 +721,7 @@ for d in $alldomains; do fi done else # set up the correct http token for verification - http01=$(echo $response | egrep -o '{[^{]*"type":"http-01"[^}]*') + http01=$(echo "$response" | egrep -o '{[^{]*"type":"http-01"[^}]*') debug http01 "$http01" token=$(echo "$http01" | sed 's/,/\n'/g| grep '"token":'| cut -d : -f 2|sed 's/"//g') @@ -737,32 +742,33 @@ for d in $alldomains; do wellknown_url="http://$d/.well-known/acme-challenge/$token" debug wellknown_url "$wellknown_url" - if [ ! "$(curl --silent --location $wellknown_url)" == "$keyauthorization" ]; then + if [ ! "$(curl --silent --location "$wellknown_url")" == "$keyauthorization" ]; then error_exit "for some reason could not reach $wellknown_url - please check it manually" fi fi debug challenge - send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" + send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then error_exit "$d:Challenge error: $code" fi + # shellcheck disable=SC2078 while [ "1" ] ; do debug "checking" - if ! getcr $uri ; then + if ! getcr "$uri" ; then error_exit "$d:Verify error:$code" fi - status=$(echo $response | egrep -o '"status":"[^"]+"' | cut -d : -f 2 | sed 's/"//g') + status=$(echo "$response" | egrep -o '"status":"[^"]+"' | cut -d : -f 2 | sed 's/"//g') if [ "$status" == "valid" ] ; then info "Verified $d" break; fi if [ "$status" == "invalid" ] ; then - error=$(echo $response | egrep -o '"error":{[^}]*}' | grep -o '"detail":"[^"]*"' | cut -d '"' -f 4) + error=$(echo "$response" | egrep -o '"error":{[^}]*}' | grep -o '"detail":"[^"]*"' | cut -d '"' -f 4) error_exit "$d:Verify error:$error" fi @@ -777,7 +783,7 @@ for d in $alldomains; do if [[ $VALIDATE_VIA_DNS == "true" ]]; then debug "remove DNS entry" - $DNS_DEL_COMMAND $DOMAIN + $DNS_DEL_COMMAND "$DOMAIN" else debug "remove token from ${ACL[$dn]}" if [[ "${ACL[$dn]:0:4}" == "ssh:" ]] ; then @@ -785,7 +791,8 @@ for d in $alldomains; do command="rm -f ${ACL[$dn]:(( ${#sshhost} + 5))}/$token" debug "running following comand to remove token" debug "ssh $sshhost ${command}" - ssh $sshhost "${command}" 1>/dev/null 2>&1 + # shellcheck disable=SC2029 + ssh "$sshhost" "${command}" 1>/dev/null 2>&1 rm -f "$TEMP_DIR/$token" else rm -f "${ACL[$dn]}/$token" @@ -796,10 +803,10 @@ for d in $alldomains; do done info "Verification completed, obtaining certificate." -der="$(openssl req -in $DOMAIN_DIR/${DOMAIN}.csr -outform DER | base64 -w 0 | _b64)" +der=$(openssl req -in "$DOMAIN_DIR/${DOMAIN}.csr" -outform DER | base64 -w 0 | _b64) send_signed_request "$CA/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64" -CertData="$(grep -i -o '^Location.*' $CURL_HEADER |sed 's/\r//g'| cut -d " " -f 2)" +CertData=$(grep -i -o '^Location.*' "$CURL_HEADER" |sed 's/\r//g'| cut -d " " -f 2) if [ "$CertData" ] ; then echo -----BEGIN CERTIFICATE----- > "$CERT_FILE" @@ -809,7 +816,7 @@ if [ "$CertData" ] ; then fi if [ -z "$CertData" ] ; then - response="$(echo $response | base64 -d)" + response=$(echo "$response" | base64 -d) error_exit "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')" fi @@ -836,8 +843,8 @@ reload_service # Check if the certificate is installed correctly if [[ ${SERVER_TYPE} == "webserver" ]]; then - CERT_REMOTE=$(echo | openssl s_client -servername ${DOMAIN} -connect ${DOMAIN}:443 2>/dev/null | openssl x509 -noout -fingerprint 2>/dev/null) - CERT_LOCAL=$(cat "$CERT_FILE" | openssl x509 -noout -fingerprint 2>/dev/null) + CERT_REMOTE=$(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:443" 2>/dev/null | openssl x509 -noout -fingerprint 2>/dev/null) + CERT_LOCAL=$(openssl x509 -noout -fingerprint < "$CERT_FILE" 2>/dev/null) if [ "$CERT_LOCAL" == "$CERT_REMOTE" ]; then info "certificate installed OK on server" else