From b15c2e898e2355d788c17ccfa2135a56b3d1309a Mon Sep 17 00:00:00 2001 From: srvrco Date: Mon, 11 Jan 2016 22:19:11 +0000 Subject: [PATCH] includes existing cert on -c option --- README.md | 2 +- getssl | 92 ++++++++++++++++++++++++++++++------------------------- 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 172f9bc..7d27c12 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ get an SSL certificate via LetsEncryot. 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.1 + getssl ver. 0.2 To obtain a letsencrypt SSL cert Usage: getssl [-h|--help] [-d|--debug] [-c] [-w working_dir] domain diff --git a/getssl b/getssl index 7ba6d25..cd67bce 100755 --- a/getssl +++ b/getssl @@ -18,10 +18,11 @@ # Revision history: # 2016-01-08 Created (v0.1) # 2016-01-11 type correction and upload to github (v0.2) +# 2016-01-11 added import of any existing cert on -c option (v0.3) # --------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="0.2" +VERSION="0.3" # defaults #umask 077 # paranoid umask, as we're creating private keys @@ -119,10 +120,10 @@ send_signed_request() { sig=$(echo -n "$protected64.$payload64" | openssl dgst -sha256 -sign $ACCOUNT_KEY | base64 -w 0 | _b64) debug sig "$sig" - + body="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" debug body "$body" - + if [ "$needbase64" ] ; then response="$($CURL -X POST --data "$body" $url | base64 -w 0)" else @@ -130,7 +131,7 @@ send_signed_request() { 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)" @@ -154,7 +155,7 @@ copy_file_to_location() { cp $from $to fi debug "copied $from to $to" - fi + fi } getcr() { @@ -169,7 +170,7 @@ getcr() { } _requires() { - result=$(which $1) + result=$(which $1 2>/dev/null) debug checking for required $1 ... $result if [ -z "$result" ]; then echo "This script requires $1 installed" @@ -257,16 +258,16 @@ CA=\"https://acme-staging.api.letsencrypt.org\" AGREEMENT=\"https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf\" -#set an email address associated with your account +#set an email address associated with your account #ACCOUNT_EMAIL=\"me@example.com\" ACCOUNT_KEY_LENGTH=4096 -#The default directory for all your certs to be stored within ( in subdirectories by domain name ) +#The default directory for all your certs to be stored within ( in subdirectories by domain name ) WORKING_DIR=~/.getssl # the command needed to reload apache / gninx 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\" " >> $WORKING_DIR/getssl.cfg fi @@ -278,6 +279,15 @@ RENEW_ALLOW=\"30\" 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_SANS="www.${DOMAIN}" + if [ ! -z "${EX_CERT}" ]; then + if [ ! -f $DOMAIN_DIR/domain.crt ]; then + 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"| cut -c 5-) + fi echo "# uncomment and modify any variables you need # The staging server is best for testing #CA=\"https://acme-staging.api.letsencrypt.org\" @@ -286,12 +296,12 @@ RENEW_ALLOW=\"30\" #AGREEMENT=\"https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf\" -#set an email address associated with your account - generally set at account level rather than domain. +#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 # additional domains - this could be multiple domains / subdomains in a comma separated list -SANS=www.${DOMAIN} +SANS=${EX_SANS} #Acme Challenge Location. The first line for the domain, the following ones for each additional domain #if these start with ssh: then the next variable is assumed to be the hostname and the rest the location. @@ -305,10 +315,10 @@ SANS=www.${DOMAIN} #CA_CERT_LOCATION=\"/etc/ssl/chain.crt\" # the command needed to reload apache / gninx 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\" " >> $DOMAIN_DIR/getssl.cfg - + fi graceful_exit fi @@ -391,34 +401,34 @@ else <(cat $SSLCONF <(printf "$SANLIST")) > $DOMAIN_DIR/${DOMAIN}.csr 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) if [ "${#pub_exp}" == "5" ] ; then pub_exp=0$pub_exp fi debug pub_exp "$pub_exp" - + 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 ) jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}' - + HEADER='{"alg": "RS256", "jwk": '$jwk'}' HEADERPLACE='{"nonce": "NONCE", "alg": "RS256", "jwk": '$jwk'}' debug HEADER "$HEADER" - + accountkey_json=$(echo -n "$jwk" | sed "s/ //g") thumbprint=$(echo -n "$accountkey_json" | sha256sum | xxd -r -p | base64 -w 0 | _b64) - + info "Registering account" regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}' if [ "$ACCOUNT_EMAIL" ] ; then regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}' -fi +fi send_signed_request "$CA/acme/new-reg" "$regjson" if [ "$code" == "" ] || [ "$code" == '201' ] ; then @@ -436,7 +446,7 @@ info "Verify each domain" alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g") dn=0 -for d in $alldomains; do +for d in $alldomains; do info "Verifing $d" debug "domain $d has location ${ACL[$dn]}" if [ -z "${ACL[$dn]}" ]; then @@ -452,16 +462,16 @@ for d in $alldomains; do 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') debug token $token - + uri=$(echo "$http01" | sed 's/,/\n'/g| grep '"uri":'| cut -d : -f 2,3|sed 's/"//g') debug uri $uri - + keyauthorization="$token.$thumbprint" debug keyauthorization "$keyauthorization" - + echo -n "$keyauthorization" > $DOMAIN_DIR/tmp/$token chmod 755 $DOMAIN_DIR/tmp/$token @@ -470,14 +480,14 @@ for d in $alldomains; do wellknown_url="http://$d/.well-known/acme-challenge/$token" debug wellknown_url "$wellknown_url" - + if [ ! "$(curl --silent $wellknown_url)" == "$keyauthorization" ]; then error_exit "for some reason could not reach $wellknown_url - please check it manually" fi debug challenge "$challenge" send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" - + if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then error_exit "$d:Challenge error: $resource" fi @@ -487,27 +497,27 @@ for d in $alldomains; do if ! getcr $uri ; then error_exit "$d:Verify error:$resource" fi - + 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_exit "$d:Verify error:$error" fi - + if [ "$status" == "pending" ] ; then info "Pending" else - error_exit "$d:Verify error:$response" - fi + error_exit "$d:Verify error:$response" + fi debug "sleep 5 secs berfore testiing verify again" sleep 5 done - + debug "remove token from ${ACL[$dn]}" if [[ "${ACL[$dn]:0:4}" == "ssh:" ]] ; then sshhost=$(echo "${ACL[$dn]}"| awk -F: '{print $2}') @@ -519,29 +529,29 @@ for d in $alldomains; do else rm -f ${ACL[$dn]} fi - -done + +done info "Verification completed, obtaining certificate." 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)" - + if [ "$CertData" ] ; then echo -----BEGIN CERTIFICATE----- > "$CERT_FILE" curl --silent "$CertData" | base64 >> "$CERT_FILE" echo -----END CERTIFICATE----- >> "$CERT_FILE" info "Certificate saved in $CERT_FILE" fi - + if [ -z "$CertData" ] ; then response="$(echo $response | base64 -d)" error_exit "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')" fi - + IssuerData=$(grep -i '^Link' $CURL_HEADER | cut -d " " -f 2| cut -d ';' -f 1 | sed 's///g') - + if [ "$IssuerData" ] ; then echo -----BEGIN CERTIFICATE----- > "$CA_CERT" curl --silent "$IssuerData" | base64 >> "$CA_CERT" @@ -560,7 +570,7 @@ copy_file_to_location $DOMAIN_DIR/${DOMAIN}.key $DOMAIN_KEY_LOCATION info "copying CA certificate to $CA_CERT_LOCATION" copy_file_to_location $CA_CERT $CA_CERT_LOCATION -# Run reload command to restart apache / gninx or whatever system +# Run reload command to restart apache / gninx or whatever system if [ ! -z "$RELOAD_CMD" ]; then info "reloading SSL services"