Browse Source

Merge pull request #14 from koter84/refetch-certificate

Added --all and --force options, as well as ability to check certificate status on web-server (rather than local)
pull/15/head
serverco 10 years ago
parent
commit
d74316242a
2 changed files with 217 additions and 118 deletions
  1. +13
    -4
      README.md
  2. +204
    -114
      getssl

+ 13
- 4
README.md View File

@ -4,16 +4,17 @@ 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. 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 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: Options:
-h, --help Display this help message and exit -h, --help Display this help message and exit
-d, --debug Outputs debug information -d, --debug Outputs debug information
-c, Create default config files
-a, --all Renew all certificates
-c, --create Create default config files
-f, --force Force renewal of cert (overrides expiry checks)
-a, --all Check all certificates
-w working_dir Working directory -w working_dir Working directory
``` ```
@ -36,11 +37,15 @@ AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
#ACCOUNT_EMAIL="me@example.com" #ACCOUNT_EMAIL="me@example.com"
ACCOUNT_KEY_LENGTH=4096 ACCOUNT_KEY_LENGTH=4096
ACCOUNT_KEY="/home/andy/.getssl/account.key" ACCOUNT_KEY="/home/andy/.getssl/account.key"
PRIVATE_KEY_ALG="rsa"
# The command needed to reload apache / nginx or whatever you use # The command needed to reload apache / nginx or whatever you use
#RELOAD_CMD="" #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" 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"
# openssl config file. The default should work in most cases. # openssl config file. The default should work in most cases.
SSLCONF="/usr/lib/ssl/openssl.cnf" SSLCONF="/usr/lib/ssl/openssl.cnf"
@ -69,6 +74,7 @@ then, within the **working directory** there will be a folder for each certifica
#ACCOUNT_EMAIL="me@example.com" #ACCOUNT_EMAIL="me@example.com"
#ACCOUNT_KEY_LENGTH=4096 #ACCOUNT_KEY_LENGTH=4096
#ACCOUNT_KEY="/home/andy/.getssl/account.key" #ACCOUNT_KEY="/home/andy/.getssl/account.key"
PRIVATE_KEY_ALG="rsa"
# Additional domains - this could be multiple domains / subdomains in a comma separated list # 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 SANS=www.example.org,example.edu,example.net,example.org,www.example.com,www.example.edu,www.example.net
@ -89,6 +95,9 @@ SANS=www.example.org,example.edu,example.net,example.org,www.example.com,www.exa
#RELOAD_CMD="" #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" #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 # Use the following 3 variables if you want to validate via DNS
#VALIDATE_VIA_DNS="true" #VALIDATE_VIA_DNS="true"


+ 204
- 114
getssl View File

@ -13,7 +13,7 @@
# GNU General Public License at <http://www.gnu.org/licenses/> for # GNU General Public License at <http://www.gnu.org/licenses/> for
# more details. # more details.
# Usage: getssl [-h|--help] [-d|--debug] [-c] [-a|--all] [-w working_dir] domain
# Usage: getssl [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-w working_dir] domain
# Revision history: # Revision history:
# 2016-01-08 Created (v0.1) # 2016-01-08 Created (v0.1)
@ -32,10 +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 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 -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 option for eliptic curve keys (v0.16)
# 2016-01-29 added server-type option to use and check cert validity from website (v0.17)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
PROGNAME=${0##*/} PROGNAME=${0##*/}
VERSION="0.16"
VERSION="0.17"
# defaults # defaults
CA="https://acme-staging.api.letsencrypt.org" CA="https://acme-staging.api.letsencrypt.org"
@ -48,9 +49,11 @@ VALIDATE_VIA_DNS=""
RELOAD_CMD="" RELOAD_CMD=""
RENEW_ALLOW="30" RENEW_ALLOW="30"
PRIVATE_KEY_ALG="rsa" PRIVATE_KEY_ALG="rsa"
SERVER_TYPE="webserver"
_USE_DEBUG=0 _USE_DEBUG=0
_CREATE_CONFIG=0 _CREATE_CONFIG=0
_RENEW_ALL=0
_CHECK_ALL=0
_FORCE_RENEW=0
clean_up() { # Perform pre-exit housekeeping clean_up() { # Perform pre-exit housekeeping
if [ ! -z "$DOMAIN_DIR" ]; then if [ ! -z "$DOMAIN_DIR" ]; then
@ -83,11 +86,11 @@ signal_exit() { # Handle trapped signals
} }
usage() { 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|--create] [-f|--force] [-a|--all] [-w working_dir] domain"
} }
log() { log() {
echo "[$(date +%Y-%m-%d\ %H:%M:%S)] $*" >> ${PROGNAME}.log
echo "[$(date +%Y-%m-%d\ %H:%M:%S)] $*" >> "${PROGNAME}.log"
} }
debug() { debug() {
@ -102,7 +105,7 @@ info() {
_b64() { _b64() {
__n=$(cat) __n=$(cat)
echo $__n | tr '/+' '_-' | tr -d '= '
echo "$__n" | tr '/+' '_-' | tr -d '= '
} }
write_openssl_conf() { write_openssl_conf() {
@ -130,11 +133,17 @@ write_getssl_template() {
ACCOUNT_KEY_LENGTH=4096 ACCOUNT_KEY_LENGTH=4096
ACCOUNT_KEY="$WORKING_DIR/account.key" ACCOUNT_KEY="$WORKING_DIR/account.key"
PRIVATE_KEY_ALG="rsa" PRIVATE_KEY_ALG="rsa"
# The command needed to reload apache / nginx or whatever you use # The command needed to reload apache / nginx or whatever you use
#RELOAD_CMD="" #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" 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"
# openssl config file. The default should work in most cases. # openssl config file. The default should work in most cases.
SSLCONF="$SSLCONF" SSLCONF="$SSLCONF"
@ -163,7 +172,7 @@ write_domain_template() {
#ACCOUNT_KEY_LENGTH=4096 #ACCOUNT_KEY_LENGTH=4096
#ACCOUNT_KEY="$WORKING_DIR/account.key" #ACCOUNT_KEY="$WORKING_DIR/account.key"
PRIVATE_KEY_ALG="rsa" PRIVATE_KEY_ALG="rsa"
# Additional domains - this could be multiple domains / subdomains in a comma separated list # Additional domains - this could be multiple domains / subdomains in a comma separated list
SANS=${EX_SANS} SANS=${EX_SANS}
@ -181,8 +190,14 @@ write_domain_template() {
# The command needed to reload apache / nginx or whatever you use # The command needed to reload apache / nginx or whatever you use
#RELOAD_CMD="" #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"
# 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 # Use the following 3 variables if you want to validate via DNS
#VALIDATE_VIA_DNS="true" #VALIDATE_VIA_DNS="true"
@ -207,7 +222,7 @@ send_signed_request() {
if [ ${_USE_DEBUG} -eq 1 ]; then if [ ${_USE_DEBUG} -eq 1 ]; then
CURL="$CURL --trace-ascii $dp " CURL="$CURL --trace-ascii $dp "
fi fi
payload64=$(echo -n $payload | base64 -w 0 | _b64)
payload64=$(echo -n "$payload" | base64 -w 0 | _b64)
debug payload64 "$payload64" debug payload64 "$payload64"
nonceurl="$CA/directory" nonceurl="$CA/directory"
@ -218,7 +233,7 @@ send_signed_request() {
protected=$(echo -n "$HEADERPLACE" | sed "s/NONCE/$nonce/" ) protected=$(echo -n "$HEADERPLACE" | sed "s/NONCE/$nonce/" )
debug protected "$protected" debug protected "$protected"
protected64=$( echo -n $protected | base64 -w 0 | _b64)
protected64=$( echo -n "$protected" | base64 -w 0 | _b64)
debug protected64 "$protected64" debug protected64 "$protected64"
sig=$(echo -n "$protected64.$payload64" | openssl dgst -sha256 -sign "$ACCOUNT_KEY" | base64 -w 0 | _b64) sig=$(echo -n "$protected64.$payload64" | openssl dgst -sha256 -sign "$ACCOUNT_KEY" | base64 -w 0 | _b64)
@ -228,36 +243,38 @@ send_signed_request() {
debug body "$body" debug body "$body"
if [ "$needbase64" ] ; then if [ "$needbase64" ] ; then
response="$($CURL -X POST --data "$body" $url | base64 -w 0)"
response=$($CURL -X POST --data "$body" "$url" | base64 -w 0)
else else
response="$($CURL -X POST --data "$body" $url)"
response=$($CURL -X POST --data "$body" "$url")
fi fi
responseHeaders="$(sed 's/\r//g' $CURL_HEADER)"
responseHeaders=$(sed 's/\r//g' "$CURL_HEADER")
debug responseHeaders "$responseHeaders" debug responseHeaders "$responseHeaders"
debug response "$response" 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" debug code "$code"
} }
copy_file_to_location() { copy_file_to_location() {
from=$1
to=$2
cert=$1
from=$2
to=$3
if [ ! -z "$to" ]; then if [ ! -z "$to" ]; then
info "copying $cert to $to"
debug "copying from $from to $to" debug "copying from $from to $to"
if [[ "${to:0:4}" == "ssh:" ]] ; then if [[ "${to:0:4}" == "ssh:" ]] ; then
debug "using scp scp -q $from ${to:4}" 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 if [ $? -gt 0 ]; then
error_exit "problem copying file to the server using scp. error_exit "problem copying file to the server using scp.
scp $from ${to:4}" scp $from ${to:4}"
fi fi
else else
mkdir -p "$(dirname $to)"
mkdir -p "$(dirname "$to")"
if [ $? -gt 0 ]; then if [ $? -gt 0 ]; then
error_exit "cannot create ACL directory $(basename $to)"
error_exit "cannot create ACL directory $(basename "$to")"
fi fi
cp "$from" "$to" cp "$from" "$to"
fi fi
@ -268,37 +285,65 @@ copy_file_to_location() {
getcr() { getcr() {
url="$1" url="$1"
debug url "$url" debug url "$url"
response="$(curl --silent $url)"
response=$(curl --silent "$url")
ret=$? ret=$?
debug response "$response" 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" debug code "$code"
return $ret return $ret
} }
_requires() { _requires() {
result=$(which $1 2>/dev/null)
result=$(which "$1" 2>/dev/null)
debug "checking for required $1 ... $result" debug "checking for required $1 ... $result"
if [ -z "$result" ]; then if [ -z "$result" ]; then
error_exit "This script requires $1 installed" error_exit "This script requires $1 installed"
fi fi
} }
help_message() {
cat <<- _EOF_
$PROGNAME ver. $VERSION
Obtain SSL certificates from the letsencrypt.org ACME server
$(usage)
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 "$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}"
}
Options:
-h, --help Display this help message and exit
-d, --debug Outputs debug information
-c, 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}"
# shellcheck disable=SC2029
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 Force renewal of cert (overrides expiry checks)
-a, --all Check all certificates
-w working_dir Working directory
_EOF_
return return
} }
@ -315,10 +360,12 @@ while [[ -n $1 ]]; do
_USE_DEBUG=1 ;; _USE_DEBUG=1 ;;
-c | --create) -c | --create)
_CREATE_CONFIG=1 ;; _CREATE_CONFIG=1 ;;
-f | --force)
_FORCE_RENEW=1 ;;
-a | --all) -a | --all)
_RENEW_ALL=1 ;;
_CHECK_ALL=1 ;;
-w) -w)
echo "working directory"; shift; WORKING_DIR="$1" ;;
shift; WORKING_DIR="$1" ;;
-* | --*) -* | --*)
usage usage
error_exit "Unknown option $1" ;; error_exit "Unknown option $1" ;;
@ -338,26 +385,29 @@ _requires xxd
_requires base64 _requires base64
_requires nslookup _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 if [ ${_CREATE_CONFIG} -eq 1 ]; then
error_exit "cannot combine -c|--create with -a|--all" error_exit "cannot combine -c|--create with -a|--all"
fi 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 if [ ! -d "$WORKING_DIR" ]; then
error_exit "working dir not found or not set - $WORKING_DIR" error_exit "working dir not found or not set - $WORKING_DIR"
fi 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'" cmd="$0 -w '$WORKING_DIR'"
if [ ${_USE_DEBUG} -eq 1 ]; then if [ ${_USE_DEBUG} -eq 1 ]; then
cmd="$cmd -d" cmd="$cmd -d"
fi fi
cmd="$cmd $dir"
cmd="$cmd $(basename "$dir")"
debug "CMD: $cmd" debug "CMD: $cmd"
eval "$cmd" eval "$cmd"
@ -403,11 +453,11 @@ if [ ${_CREATE_CONFIG} -eq 1 ]; then
info "domain config already exists $DOMAIN_DIR/getssl.cfg" info "domain config already exists $DOMAIN_DIR/getssl.cfg"
else else
info "creating domain config file in $DOMAIN_DIR/getssl.cfg" 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}" EX_SANS="www.${DOMAIN}"
if [ ! -z "${EX_CERT}" ]; then if [ ! -z "${EX_CERT}" ]; then
if [ ! -f "$DOMAIN_DIR/${DOMAIN}.crt" ]; then if [ ! -f "$DOMAIN_DIR/${DOMAIN}.crt" ]; then
echo "$EX_CERT" > $DOMAIN_DIR/${DOMAIN}.crt
echo "$EX_CERT" > "$DOMAIN_DIR/${DOMAIN}.crt"
fi fi
EX_SANS=$(echo "$EX_CERT" | openssl x509 -noout -text 2>/dev/null| grep "Subject Alternative Name" -A2 \ 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-) | grep -Eo "DNS:[a-zA-Z 0-9.-]*" | sed "s@DNS:$DOMAIN@@g" | grep -v '^$' | cut -c 5-)
@ -443,18 +493,68 @@ if [ -f "$DOMAIN_DIR/getssl.cfg" ]; then
. "$DOMAIN_DIR/getssl.cfg" . "$DOMAIN_DIR/getssl.cfg"
fi 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)
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=$(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
# 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=$(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"
debug "copying remote certificate to local"
echo "$EX_CERT" > "$DOMAIN_DIR/${DOMAIN}.crt"
else
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 # local cert doesn't exist"
debug "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 if [ -f "$CERT_FILE" ]; then
debug "certificate $CERT_FILE exists" 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 [[ "$enddate" != "-" ]]; then
if [[ $(date -d "${RENEW_ALLOW} days" +%s) -lt $(date -d "$enddate" +%s) ]]; 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 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}"
debug "certificate for $DOMAIN needs renewal"
cert_archive "${CERT_FILE}"
fi fi
fi fi
fi fi
@ -475,19 +575,22 @@ fi
if [ -f "$DOMAIN_DIR/${DOMAIN}.key" ]; then if [ -f "$DOMAIN_DIR/${DOMAIN}.key" ]; then
debug "domain key exists at $DOMAIN_DIR/${DOMAIN}.key - skipping generation" debug "domain key exists at $DOMAIN_DIR/${DOMAIN}.key - skipping generation"
# check validity of domain key # 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" error_exit "$DOMAIN_DIR/${DOMAIN}.key does not appear to be an appropriate private key - aborting"
fi fi
else else
info "creating domain key - $DOMAIN_DIR/${DOMAIN}.key" 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 fi
#create SAN #create SAN
if [ -z "$SANS" ]; then if [ -z "$SANS" ]; then
SANLIST="[SAN]\nsubjectAltName=DNS:${DOMAIN}"
SANLIST="ubjectAltName=DNS:${DOMAIN}"
else else
SANLIST="[SAN]\nsubjectAltName=DNS:${DOMAIN},DNS:${SANS//,/,DNS:}"
SANLIST="subjectAltName=DNS:${DOMAIN},DNS:${SANS//,/,DNS:}"
fi fi
debug "created SAN list = $SANLIST" debug "created SAN list = $SANLIST"
@ -495,28 +598,29 @@ debug "created SAN list = $SANLIST"
if [ -f "$DOMAIN_DIR/${DOMAIN}.csr" ]; then if [ -f "$DOMAIN_DIR/${DOMAIN}.csr" ]; then
debug "domain csr exists at - $DOMAIN_DIR/${DOMAIN}.csr - skipping generation" debug "domain csr exists at - $DOMAIN_DIR/${DOMAIN}.csr - skipping generation"
#check csr is valid for domain #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" error_exit "existing csr at $DOMAIN_DIR/${DOMAIN}.csr does not appear to be valid for ${DOMAIN} - aborting"
fi fi
else else
debug "creating domain csr - $DOMAIN_DIR/${DOMAIN}.csr" 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 fi
# use account key to register with CA # 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 if [ "${#pub_exp}" == "5" ] ; then
pub_exp=0$pub_exp pub_exp=0$pub_exp
fi fi
debug pub_exp "$pub_exp" debug pub_exp "$pub_exp"
e=$(echo $pub_exp | xxd -r -p | base64)
e=$(echo "$pub_exp" | xxd -r -p | base64)
debug e "$e" 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'"}' jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
@ -536,7 +640,7 @@ send_signed_request "$CA/acme/new-reg" "$regjson"
if [ "$code" == "" ] || [ "$code" == '201' ] ; then if [ "$code" == "" ] || [ "$code" == '201' ] ; then
info "Registered" info "Registered"
echo $response > $TEMP_DIR/account.json
echo "$response" > "$TEMP_DIR/account.json"
elif [ "$code" == '409' ] ; then elif [ "$code" == '409' ] ; then
debug "Already registered" debug "Already registered"
else else
@ -570,7 +674,7 @@ for d in $alldomains; do
fi fi
if [[ $VALIDATE_VIA_DNS == "true" ]]; then # set up the correct DNS token for verification 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" debug dns01 "$dns01"
token=$(echo "$dns01" | sed 's/,/\n'/g| grep '"token":'| cut -d : -f 2|sed 's/"//g') token=$(echo "$dns01" | sed 's/,/\n'/g| grep '"token":'| cut -d : -f 2|sed 's/"//g')
@ -588,13 +692,13 @@ for d in $alldomains; do
debug "adding dns via command: $DNS_ADD_COMMAND $d $auth_key" debug "adding dns via command: $DNS_ADD_COMMAND $d $auth_key"
$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" debug primary_ns "$primary_ns"
ntries=0 ntries=0
check_dns="fail" check_dns="fail"
while [ "$check_dns" == "fail" ]; do 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" debug result "$check_result"
if [[ "$check_result" == "$auth_key" ]]; then if [[ "$check_result" == "$auth_key" ]]; then
@ -602,11 +706,11 @@ for d in $alldomains; do
debug "checking DNS ... _acme-challenge.$d gave $check_result" debug "checking DNS ... _acme-challenge.$d gave $check_result"
if [ "$DNS_EXTRA_WAIT" != "" ]; then if [ "$DNS_EXTRA_WAIT" != "" ]; then
info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME-server to check the dns" info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME-server to check the dns"
sleep $DNS_EXTRA_WAIT
sleep "$DNS_EXTRA_WAIT"
fi fi
else else
if [[ $ntries -lt 100 ]]; then 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" info "testing DNS. Attempt $ntries/100 completed. waiting 10 secs before testing verify again"
sleep 10 sleep 10
else else
@ -617,7 +721,7 @@ for d in $alldomains; do
fi fi
done done
else # set up the correct http token for verification 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" debug http01 "$http01"
token=$(echo "$http01" | sed 's/,/\n'/g| grep '"token":'| cut -d : -f 2|sed 's/"//g') token=$(echo "$http01" | sed 's/,/\n'/g| grep '"token":'| cut -d : -f 2|sed 's/"//g')
@ -638,32 +742,33 @@ for d in $alldomains; do
wellknown_url="http://$d/.well-known/acme-challenge/$token" wellknown_url="http://$d/.well-known/acme-challenge/$token"
debug wellknown_url "$wellknown_url" 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" error_exit "for some reason could not reach $wellknown_url - please check it manually"
fi fi
fi fi
debug challenge debug challenge
send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then
error_exit "$d:Challenge error: $code" error_exit "$d:Challenge error: $code"
fi fi
# shellcheck disable=SC2078
while [ "1" ] ; do while [ "1" ] ; do
debug "checking" debug "checking"
if ! getcr $uri ; then
if ! getcr "$uri" ; then
error_exit "$d:Verify error:$code" error_exit "$d:Verify error:$code"
fi 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 if [ "$status" == "valid" ] ; then
info "Verified $d" info "Verified $d"
break; break;
fi fi
if [ "$status" == "invalid" ] ; then 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" error_exit "$d:Verify error:$error"
fi fi
@ -678,7 +783,7 @@ for d in $alldomains; do
if [[ $VALIDATE_VIA_DNS == "true" ]]; then if [[ $VALIDATE_VIA_DNS == "true" ]]; then
debug "remove DNS entry" debug "remove DNS entry"
$DNS_DEL_COMMAND $DOMAIN
$DNS_DEL_COMMAND "$DOMAIN"
else else
debug "remove token from ${ACL[$dn]}" debug "remove token from ${ACL[$dn]}"
if [[ "${ACL[$dn]:0:4}" == "ssh:" ]] ; then if [[ "${ACL[$dn]:0:4}" == "ssh:" ]] ; then
@ -686,7 +791,8 @@ for d in $alldomains; do
command="rm -f ${ACL[$dn]:(( ${#sshhost} + 5))}/$token" command="rm -f ${ACL[$dn]:(( ${#sshhost} + 5))}/$token"
debug "running following comand to remove token" debug "running following comand to remove token"
debug "ssh $sshhost ${command}" 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" rm -f "$TEMP_DIR/$token"
else else
rm -f "${ACL[$dn]}/$token" rm -f "${ACL[$dn]}/$token"
@ -697,10 +803,10 @@ for d in $alldomains; do
done done
info "Verification completed, obtaining certificate." 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" 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 if [ "$CertData" ] ; then
echo -----BEGIN CERTIFICATE----- > "$CERT_FILE" echo -----BEGIN CERTIFICATE----- > "$CERT_FILE"
@ -710,7 +816,7 @@ if [ "$CertData" ] ; then
fi fi
if [ -z "$CertData" ] ; then if [ -z "$CertData" ] ; then
response="$(echo $response | base64 -d)"
response=$(echo "$response" | base64 -d)
error_exit "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')" error_exit "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')"
fi fi
@ -725,40 +831,24 @@ fi
# copy certs to the correct location # 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 # 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
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=$(openssl x509 -noout -fingerprint < "$CERT_FILE" 2>/dev/null)
if [ "$CERT_LOCAL" == "$CERT_REMOTE" ]; then
info "certificate installed OK on server"
else else
debug "running reload command $RELOAD_CMD"
$RELOAD_CMD
error_exit "certificate on server is different from local certificate"
fi fi
fi fi


Loading…
Cancel
Save