From 670a07d37e7183c80ba9ebba314168480167789e Mon Sep 17 00:00:00 2001 From: srvrco Date: Wed, 1 Jun 2016 09:15:42 +0100 Subject: [PATCH] Reorder functions alphabetically as part of code tidy. --- README.md | 2 +- getssl | 646 +++++++++++++++++++++++++++--------------------------- 2 files changed, 323 insertions(+), 325 deletions(-) diff --git a/README.md b/README.md index ae39f06..f4b0f24 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Obtain SSL certificates from the letsencrypt.org ACME server. Suitable for auto This was written in standard bash ( so can be run on a server, 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). ``` -getssl ver. 0.42 +getssl ver. 1.00 Obtain SSL certificates from the letsencrypt.org ACME server Usage: getssl [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-q|--quiet] [-u|--upgrade] [-w working_dir] domain diff --git a/getssl b/getssl index 48161b9..1819e9f 100755 --- a/getssl +++ b/getssl @@ -62,12 +62,11 @@ # 2016-05-30 Improvements to auto-upgrade (0.44) # 2016-05-31 Improved comments - no structural changes # 2016-05-31 After running for nearly 6 months, final testing prior to a 1.00 stable version. (0.90) +# 2016-06-01 Reorder functions alphabetically as part of code tidy. (0.91) # --------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="0.90" - -ORIGCMD="$0 $*" +VERSION="0.91" # defaults CODE_LOCATION="https://raw.githubusercontent.com/srvrco/getssl/master/getssl" @@ -94,59 +93,64 @@ _FORCE_RENEW=0 _QUIET=0 _UPGRADE=0 -clean_up() { # Perform pre-exit housekeeping - umask "$ORIG_UMASK" - if [ ! -z "$DOMAIN_DIR" ]; then - rm -rf "${TEMP_DIR:?}" - fi - if [[ $VALIDATE_VIA_DNS == "true" ]]; then - if [[ ! -z "$DNS_DEL_COMMAND" ]]; then - $DNS_DEL_COMMAND "$d" - fi - fi -} +# store copy of original command in case of upgrading script and re-running +ORIGCMD="$0 $*" -error_exit() { # give error message on error exit - echo -e "${PROGNAME}: ${1:-"Unknown Error"}" >&2 - clean_up - exit 1 +cert_archive() { # Archive certificate file by copying with dates at end. + 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 "$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}" } -graceful_exit() { # normal exit function. - clean_up - exit -} +check_challenge_completion() { # checks with the ACME server if our challenge is OK + uri=$1 + domain=$2 + keyauthorization=$3 -signal_exit() { # Handle trapped signals - case $1 in - INT) - error_exit "Program interrupted by user" ;; - TERM) - echo -e "\n$PROGNAME: Program terminated" >&2 - graceful_exit ;; - *) - error_exit "$PROGNAME: Terminating on unknown signal" ;; - esac -} + debug "sending request to ACME server saying we're ready for challenge" + send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" -usage() { # program usage - echo "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-q|--quiet] [-u|--upgrade] [-w working_dir] domain" -} + # check respose from our request to perform challenge + if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then + error_exit "$domain:Challenge error: $code" + fi -log() { # write info to log file with date / time stamp - echo "[$(date +%Y-%m-%d\ %H:%M:%S)] $*" >> "${PROGNAME}.log" -} + # loop "forever" to keep checking for a response from the ACME server. + # shellcheck disable=SC2078 + while [ "1" ] ; do + debug "checking" + if ! getcr "$uri" ; then + error_exit "$domain:Verify error:$code" + fi -debug() { # write out debug info if the debug flag has been set - if [ ${_USE_DEBUG} -eq 1 ]; then - echo "$@" - fi -} + # shellcheck disable=SC2086 + status=$(echo $response | grep -Po '"status":[ ]*"[^"]+"' | cut -d '"' -f 4) -info() { # write out info as long as the quiet flag has not been set. - if [ ${_QUIET} -eq 0 ]; then - echo "$@" - fi + # If ACME respose is valid, then break out of loop + if [ "$status" == "valid" ] ; then + info "Verified $domain" + break; + fi + + # if ACME response is that their check gave an invalid response, error exit + if [ "$status" == "invalid" ] ; then + error=$(echo "$response" | grep -Po '"error":[ ]*{[^}]*}' | grep -o '"detail":"[^"]*"' | cut -d '"' -f 4) + error_exit "$domain:Verify error:$error" + fi + + # if ACME response is pending ( they haven't completed checks yet) then wait and try again. + if [ "$status" == "pending" ] ; then + info "Pending" + else + error_exit "$domain:Verify error:$response" + fi + debug "sleep 5 secs before testing verify again" + sleep 5 + done } check_upgrade() { # check if a more recent version of code is available available @@ -176,14 +180,145 @@ check_upgrade() { # check if a more recent version of code is available availabl fi } -urlbase64() { # urlbase64: base64 encoded string with '+' replaced with '-' and '/' replaced with '_' - openssl base64 -e | tr -d '\n\r' | os_sed -e 's:=*$::g' -e 'y:+/:-_:' +clean_up() { # Perform pre-exit housekeeping + umask "$ORIG_UMASK" + if [ ! -z "$DOMAIN_DIR" ]; then + rm -rf "${TEMP_DIR:?}" + fi + if [[ $VALIDATE_VIA_DNS == "true" ]]; then + if [[ ! -z "$DNS_DEL_COMMAND" ]]; then + $DNS_DEL_COMMAND "$d" + fi + fi +} + +copy_file_to_location() { # copies a file, using scp if required. + cert=$1 # descriptive name, just used for display + from=$2 # current file location + to=$3 # location to move file to. + 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}" + 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 + elif [[ "${to:0:4}" == "ftp:" ]] ; then + if [[ "$cert" != "challenge token" ]] ; then + error_exit "ftp is not a sercure method for copying certificates or keys" + fi + debug "using ftp to copy the file from $from" + ftpuser=$(echo "$to"| awk -F: '{print $2}') + ftppass=$(echo "$to"| awk -F: '{print $3}') + ftphost=$(echo "$to"| awk -F: '{print $4}') + ftplocn=$(echo "$to"| awk -F: '{print $5}') + ftpdirn=$(dirname "$ftplocn") + ftpfile=$(basename "$ftplocn") + fromdir=$(dirname "$from") + fromfile=$(basename "$from") + debug "ftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile" + debug "from dir=$fromdir file=$fromfile" + ftp -n <<- _EOF + open $ftphost + user $ftpuser $ftppass + cd $ftpdirn + lcd $fromdir + put $fromfile + _EOF + elif [[ "${to:0:5}" == "sftp:" ]] ; then + debug "using sftp to copy the file from $from" + ftpuser=$(echo "$to"| awk -F: '{print $2}') + ftppass=$(echo "$to"| awk -F: '{print $3}') + ftphost=$(echo "$to"| awk -F: '{print $4}') + ftplocn=$(echo "$to"| awk -F: '{print $5}') + ftpdirn=$(dirname "$ftplocn") + ftpfile=$(basename "$ftplocn") + fromdir=$(dirname "$from") + fromfile=$(basename "$from") + debug "sftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile" + debug "from dir=$fromdir file=$fromfile" + sshpass -p "$ftppass" sftp "$ftpuser@$ftphost" <<- _EOF + cd $ftpdirn + lcd $fromdir + put $fromfile + _EOF + else + mkdir -p "$(dirname "$to")" + if [ $? -gt 0 ]; then + error_exit "cannot create ACL directory $(basename "$to")" + fi + cp "$from" "$to" + if [ $? -ne 0 ]; then + error_exit "cannot copy $from to $to" + fi + fi + debug "copied $from to $to" + fi +} + +debug() { # write out debug info if the debug flag has been set + if [ ${_USE_DEBUG} -eq 1 ]; then + echo "$@" + fi +} + +error_exit() { # give error message on error exit + echo -e "${PROGNAME}: ${1:-"Unknown Error"}" >&2 + clean_up + exit 1 +} + +getcr() { # get curl response + url="$1" + debug url "$url" + response=$(curl --silent "$url") + ret=$? + debug response "$response" + # shellcheck disable=SC2086 + code=$(echo $response | grep -Eo '"status":[ ]*[0-9]*' | cut -d : -f 2) + debug code "$code" + debug getcr return code $ret + return $ret +} + +graceful_exit() { # normal exit function. + clean_up + exit +} + +help_message() { # print out the 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 Force renewal of cert (overrides expiry checks) + -a, --all Check all certificates + -q, --quiet Quiet mode (only outputs on error) + -u, --upgrade Upgrade getssl if a more recent version is available + -w working_dir Working directory + + _EOF_ } hex2bin() { # Remove spaces, add leading zero, escape as hex string and parse with printf printf -- "$(cat | os_sed -e 's/[[:space:]]//g' -e 's/^(.(.{2})*)$/0\1/' -e 's/(.{2})/\\x\1/g')" } +info() { # write out info as long as the quiet flag has not been set. + if [ ${_QUIET} -eq 0 ]; then + echo "$@" + fi +} + os_sed() { # Use different sed version for different os types... if [[ "$OSTYPE" == "linux-gnu" ]]; then sed -r "${@}" @@ -192,54 +327,104 @@ os_sed() { # Use different sed version for different os types... fi } -write_openssl_conf() { # write out a minimal openssl conf - cat > "$1" <<- _EOF_openssl_conf_ - # minimal openssl.cnf file - distinguished_name = req_distinguished_name - [ req_distinguished_name ] - [v3_req] - [v3_ca] - _EOF_openssl_conf_ +reload_service() { # Runs a command to reload services ( via ssh if needed) + 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}" + # shellcheck disable=SC2029 + ssh "$sshhost" "${command}" 1>/dev/null 2>&1 + # allow 2 seconds for services to restart + sleep 2 + else + debug "running reload command $RELOAD_CMD" + $RELOAD_CMD + fi + fi } -write_getssl_template() { # write out the main template file - cat > "$1" <<- _EOF_getssl_ - # Uncomment and modify any variables you need - # The staging server is best for testing (hence set as default) - CA="https://acme-staging.api.letsencrypt.org" - # This server issues full certificates, however has rate limits - #CA="https://acme-v01.api.letsencrypt.org" +requires() { # check if required function is available + result=$(which "$1" 2>/dev/null) + debug "checking for required $1 ... $result" + if [ -z "$result" ]; then + error_exit "This script requires $1 installed" + fi +} - AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf" +send_signed_request() { # Sends a request to the ACME server, signed with your private key. + url=$1 + payload=$2 + needbase64=$3 - # Set an email address associated with your account - generally set at account level rather than domain. - #ACCOUNT_EMAIL="me@example.com" - ACCOUNT_KEY_LENGTH=4096 - ACCOUNT_KEY="$WORKING_DIR/account.key" - PRIVATE_KEY_ALG="rsa" + debug url "$url" + debug payload "$payload" - # 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" + CURL_HEADER="$TEMP_DIR/curl.header" + dp="$TEMP_DIR/curl.dump" + CURL="curl --silent --dump-header $CURL_HEADER " + if [ ${_USE_DEBUG} -eq 1 ]; then + CURL="$CURL --trace-ascii $dp " + fi - # Define the server type. This can either be a webserver, ldaps or a port number which - # will be checked for certificate expiry and also will be checked after - # an update to confirm correct certificate is running (if CHECK_REMOTE) is set to true - SERVER_TYPE="webserver" - CHECK_REMOTE="true" + # convert payload to url base 64 + payload64="$(printf '%s' "${payload}" | urlbase64)" + debug payload64 "$payload64" - # openssl config file. The default should work in most cases. - SSLCONF="$SSLCONF" + # get nonce from ACME server + nonceurl="$CA/directory" + nonce=$($CURL -I $nonceurl | grep "^Replay-Nonce:" | sed s/\\r//|sed s/\\n//| cut -d ' ' -f 2) - # Use the following 3 variables if you want to validate via DNS - #VALIDATE_VIA_DNS="true" - #DNS_ADD_COMMAND= - #DNS_DEL_COMMAND= - # If your DNS-server needs extra time to make sure your DNS changes are readable by the ACME-server (time in seconds) - #DNS_EXTRA_WAIT=60 - _EOF_getssl_ + debug nonce "$nonce" + + # Build header with just our public key and algorithm information + header='{"alg": "RS256", "jwk": {"e": "'"${pub_exp64}"'", "kty": "RSA", "n": "'"${pub_mod64}"'"}}' + + # Build another header which also contains the previously received nonce and encode it as urlbase64 + protected='{"alg": "RS256", "jwk": {"e": "'"${pub_exp64}"'", "kty": "RSA", "n": "'"${pub_mod64}"'"}, "nonce": "'"${nonce}"'"}' + protected64="$(printf '%s' "${protected}" | urlbase64)" + debug protected "$protected" + + # Sign header with nonce and our payload with our private key and encode signature as urlbase64 + signed64="$(printf '%s' "${protected64}.${payload64}" | openssl dgst -sha256 -sign "${ACCOUNT_KEY}" | urlbase64)" + + # Send header + extended header + payload + signature to the acme-server + body='{"header": '"${header}"', "protected": "'"${protected64}"'", "payload": "'"${payload64}"'", "signature": "'"${signed64}"'"}' + debug "data for account registration = $body" + + if [ "$needbase64" ] ; then + response=$($CURL -X POST --data "$body" "$url" | urlbase64) + else + response=$($CURL -X POST --data "$body" "$url") + fi + + responseHeaders=$(sed 's/\r//g' "$CURL_HEADER") + debug responseHeaders "$responseHeaders" + debug response "$response" + code=$(grep ^HTTP "$CURL_HEADER" | tail -1 | cut -d " " -f 2) + debug code "$code" +} + +signal_exit() { # Handle trapped signals + case $1 in + INT) + error_exit "Program interrupted by user" ;; + TERM) + echo -e "\n$PROGNAME: Program terminated" >&2 + graceful_exit ;; + *) + error_exit "$PROGNAME: Terminating on unknown signal" ;; + esac +} + +urlbase64() { # urlbase64: base64 encoded string with '+' replaced with '-' and '/' replaced with '_' + openssl base64 -e | tr -d '\n\r' | os_sed -e 's:=*$::g' -e 'y:+/:-_:' +} + +usage() { # program usage + echo "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-q|--quiet] [-u|--upgrade] [-w working_dir] domain" } write_domain_template() { # write out a template file for a domain. @@ -299,241 +484,54 @@ write_domain_template() { # write out a template file for a domain. _EOF_domain_ } -send_signed_request() { # Sends a request to the ACME server, signed with your private key. - url=$1 - payload=$2 - needbase64=$3 - - debug url "$url" - debug payload "$payload" - - CURL_HEADER="$TEMP_DIR/curl.header" - dp="$TEMP_DIR/curl.dump" - CURL="curl --silent --dump-header $CURL_HEADER " - if [ ${_USE_DEBUG} -eq 1 ]; then - CURL="$CURL --trace-ascii $dp " - fi - - # convert payload to url base 64 - payload64="$(printf '%s' "${payload}" | urlbase64)" - debug payload64 "$payload64" - - # get nonce from ACME server - nonceurl="$CA/directory" - nonce=$($CURL -I $nonceurl | grep "^Replay-Nonce:" | sed s/\\r//|sed s/\\n//| cut -d ' ' -f 2) - - debug nonce "$nonce" - - # Build header with just our public key and algorithm information - header='{"alg": "RS256", "jwk": {"e": "'"${pub_exp64}"'", "kty": "RSA", "n": "'"${pub_mod64}"'"}}' - - # Build another header which also contains the previously received nonce and encode it as urlbase64 - protected='{"alg": "RS256", "jwk": {"e": "'"${pub_exp64}"'", "kty": "RSA", "n": "'"${pub_mod64}"'"}, "nonce": "'"${nonce}"'"}' - protected64="$(printf '%s' "${protected}" | urlbase64)" - debug protected "$protected" - - # Sign header with nonce and our payload with our private key and encode signature as urlbase64 - signed64="$(printf '%s' "${protected64}.${payload64}" | openssl dgst -sha256 -sign "${ACCOUNT_KEY}" | urlbase64)" - - # Send header + extended header + payload + signature to the acme-server - body='{"header": '"${header}"', "protected": "'"${protected64}"'", "payload": "'"${payload64}"'", "signature": "'"${signed64}"'"}' - debug "data for account registration = $body" - - if [ "$needbase64" ] ; then - response=$($CURL -X POST --data "$body" "$url" | urlbase64) - else - response=$($CURL -X POST --data "$body" "$url") - fi - - responseHeaders=$(sed 's/\r//g' "$CURL_HEADER") - debug responseHeaders "$responseHeaders" - debug response "$response" - code=$(grep ^HTTP "$CURL_HEADER" | tail -1 | cut -d " " -f 2) - debug code "$code" -} - -check_challenge_completion() { # checks with the ACME server if our challenge is OK - uri=$1 - domain=$2 - keyauthorization=$3 - - debug "sending request to ACME server saying we're ready for challenge" - send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" - - # check respose from our request to perform challenge - if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then - error_exit "$domain:Challenge error: $code" - fi - - # loop "forever" to keep checking for a response from the ACME server. - # shellcheck disable=SC2078 - while [ "1" ] ; do - debug "checking" - if ! getcr "$uri" ; then - error_exit "$domain:Verify error:$code" - fi - - # shellcheck disable=SC2086 - status=$(echo $response | grep -Po '"status":[ ]*"[^"]+"' | cut -d '"' -f 4) - - # If ACME respose is valid, then break out of loop - if [ "$status" == "valid" ] ; then - info "Verified $domain" - break; - fi - - # if ACME response is that their check gave an invalid response, error exit - if [ "$status" == "invalid" ] ; then - error=$(echo "$response" | grep -Po '"error":[ ]*{[^}]*}' | grep -o '"detail":"[^"]*"' | cut -d '"' -f 4) - error_exit "$domain:Verify error:$error" - fi +write_getssl_template() { # write out the main template file + cat > "$1" <<- _EOF_getssl_ + # Uncomment and modify any variables you need + # The staging server is best for testing (hence set as default) + CA="https://acme-staging.api.letsencrypt.org" + # This server issues full certificates, however has rate limits + #CA="https://acme-v01.api.letsencrypt.org" - # if ACME response is pending ( they haven't completed checks yet) then wait and try again. - if [ "$status" == "pending" ] ; then - info "Pending" - else - error_exit "$domain:Verify error:$response" - fi - debug "sleep 5 secs before testing verify again" - sleep 5 - done -} + AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf" -copy_file_to_location() { # copies a file, using scp if required. - cert=$1 # descriptive name, just used for display - from=$2 # current file location - to=$3 # location to move file to. - 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}" - 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 - elif [[ "${to:0:4}" == "ftp:" ]] ; then - if [[ "$cert" != "challenge token" ]] ; then - error_exit "ftp is not a sercure method for copying certificates or keys" - fi - debug "using ftp to copy the file from $from" - ftpuser=$(echo "$to"| awk -F: '{print $2}') - ftppass=$(echo "$to"| awk -F: '{print $3}') - ftphost=$(echo "$to"| awk -F: '{print $4}') - ftplocn=$(echo "$to"| awk -F: '{print $5}') - ftpdirn=$(dirname "$ftplocn") - ftpfile=$(basename "$ftplocn") - fromdir=$(dirname "$from") - fromfile=$(basename "$from") - debug "ftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile" - debug "from dir=$fromdir file=$fromfile" - ftp -n <<- _EOF - open $ftphost - user $ftpuser $ftppass - cd $ftpdirn - lcd $fromdir - put $fromfile - _EOF - elif [[ "${to:0:5}" == "sftp:" ]] ; then - debug "using sftp to copy the file from $from" - ftpuser=$(echo "$to"| awk -F: '{print $2}') - ftppass=$(echo "$to"| awk -F: '{print $3}') - ftphost=$(echo "$to"| awk -F: '{print $4}') - ftplocn=$(echo "$to"| awk -F: '{print $5}') - ftpdirn=$(dirname "$ftplocn") - ftpfile=$(basename "$ftplocn") - fromdir=$(dirname "$from") - fromfile=$(basename "$from") - debug "sftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile" - debug "from dir=$fromdir file=$fromfile" - sshpass -p "$ftppass" sftp "$ftpuser@$ftphost" <<- _EOF - cd $ftpdirn - lcd $fromdir - put $fromfile - _EOF - else - mkdir -p "$(dirname "$to")" - if [ $? -gt 0 ]; then - error_exit "cannot create ACL directory $(basename "$to")" - fi - cp "$from" "$to" - if [ $? -ne 0 ]; then - error_exit "cannot copy $from to $to" - fi - fi - debug "copied $from to $to" - fi -} + # Set an email address associated with your account - generally set at account level rather than domain. + #ACCOUNT_EMAIL="me@example.com" + ACCOUNT_KEY_LENGTH=4096 + ACCOUNT_KEY="$WORKING_DIR/account.key" + PRIVATE_KEY_ALG="rsa" -getcr() { # get curl response - url="$1" - debug url "$url" - response=$(curl --silent "$url") - ret=$? - debug response "$response" - # shellcheck disable=SC2086 - code=$(echo $response | grep -Eo '"status":[ ]*[0-9]*' | cut -d : -f 2) - debug code "$code" - debug getcr return code $ret - return $ret -} + # 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" -_requires() { # check if required function is available - result=$(which "$1" 2>/dev/null) - debug "checking for required $1 ... $result" - if [ -z "$result" ]; then - error_exit "This script requires $1 installed" - fi -} + # Define the server type. This can either be a webserver, ldaps or a port number which + # will be checked for certificate expiry and also will be checked after + # an update to confirm correct certificate is running (if CHECK_REMOTE) is set to true + SERVER_TYPE="webserver" + CHECK_REMOTE="true" -cert_archive() { # Archive certificate file by copying with dates at end. - 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 "$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}" -} + # openssl config file. The default should work in most cases. + SSLCONF="$SSLCONF" -reload_service() { # Runs a command to reload services ( via ssh if needed) - 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}" - # shellcheck disable=SC2029 - ssh "$sshhost" "${command}" 1>/dev/null 2>&1 - # allow 2 seconds for services to restart - sleep 2 - else - debug "running reload command $RELOAD_CMD" - $RELOAD_CMD - fi - fi + # Use the following 3 variables if you want to validate via DNS + #VALIDATE_VIA_DNS="true" + #DNS_ADD_COMMAND= + #DNS_DEL_COMMAND= + # If your DNS-server needs extra time to make sure your DNS changes are readable by the ACME-server (time in seconds) + #DNS_EXTRA_WAIT=60 + _EOF_getssl_ } -help_message() { # print out the 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 Force renewal of cert (overrides expiry checks) - -a, --all Check all certificates - -q, --quiet Quiet mode (only outputs on error) - -u, --upgrade Upgrade getssl if a more recent version is available - -w working_dir Working directory - - _EOF_ +write_openssl_conf() { # write out a minimal openssl conf + cat > "$1" <<- _EOF_openssl_conf_ + # minimal openssl.cnf file + distinguished_name = req_distinguished_name + [ req_distinguished_name ] + [v3_req] + [v3_ca] + _EOF_openssl_conf_ } # Trap signals @@ -572,13 +570,13 @@ done #check if required applications are included -_requires openssl -_requires curl -_requires nslookup -_requires sed -_requires grep -_requires awk -_requires tr +requires openssl +requires curl +requires nslookup +requires sed +requires grep +requires awk +requires tr # Check if upgrades are available check_upgrade