diff --git a/cert-autorefresh b/cert-autorefresh new file mode 100755 index 0000000000000000000000000000000000000000..6fd09af5dfa386ecabdb6de0f290dedcd7c375a7 --- /dev/null +++ b/cert-autorefresh @@ -0,0 +1,294 @@ +#!/usr/bin/env bash + +# +# constant +# + +apitokenfile=$( basename $0 ).apitoken + +# +# Help +# + +function helpAndExit() +{ + echo "Syntax: $0 [options]" >&2 + echo >&2 + echo "Options:" >&2 + echo " -t, --token [APITOKEN] API token to use against ra-portal.rwth" >&2 + echo " or be read from ./$apitokenfile">&2 + echo " -h, --hostname [HOSTNAME] set hostname, otherwise hostname will be calculated" >&2 + echo " -r, --revoke revoke old cert, if new one was downloaded" >&2 + echo " -s, --symlink create symlinks" >&2 + echo " -v, --verbose increase verbosity level (up to 3)" >&2 + exit 1 +} + +# +# Get Options +# + +verbose=0 + +while [ "$#" -gt 0 ]; do + case "$1" in + -t|--token) + apitoken="$2" + shift 2 + ;; + -h|--hostname) + hostname="$2" + shift 2 + ;; + -r|--revoke) + revoke=TRUE + shift 1 + ;; + -s|--symlinks) + symlinks=TRUE + shift 1 + ;; + -v|--verbose) + ((verbose++)) + shift 1 + ;; + -?|--help) + helpAndExit + ;; + -*|*) + echo "[ERROR ] Unknown option: $1" + echo + helpAndExit + ;; + esac +done + +# +# Option Check +# + +if [ ! $apitoken ] +then + [ $verbose -ge 1 ] && echo "[INFO ] reading API Token from ./$apitokenfile" + apitoken=$( cat $apitokenfile ) +fi + + +# +# function ra_portal +# + +function ra_portal() +{ + local data="" + local mode=$1 + shift 1 + local link="https://ra-portal.itc.rwth-aachen.de/api/ssl-certificates/$1" + shift 1 + while [ $# -gt 0 ] + do + data+=" --data $1" + shift 1 + done + local cmd="curl --silent --request $mode --header \"Authorization: Basic ${apitoken}\" ${data} \"${link}\"" + [ $verbose -ge 3 ] && echo "[COMMAND ] $cmd" >&2 + curl --silent --request $mode --header "Authorization: Basic ${apitoken}" ${data} "${link}" +} + +# +# API Token Check +# + +content=$( ra_portal GET api-token-info | jq ) +[ $verbose -ge 2 ] && echo -e "[VERBOSE ] API Token content:\n$content" +[ $( echo $content | jq -r '.status' ) == 401 ] && echo "[ERROR ] $( echo $content | jq -r '.error' )" && exit 2 +[ $verbose -ge 1 ] && echo "[INFO ] API Token '$( echo $content | jq -r '.name' )' valid." + +# +# API Token Expiration Check +# + +let tokenExpirationDays=($( date +%s -d $( echo $content | jq -r '.expires_at' ) )-$( date +%s ))/86400 +[ $tokenExpirationDays -lt 90 ] && echo "[WARNING ] API Token '$( echo $content | jq -r '.name' )' expires in $tokenExpirationDays days." || ( [ $verbose -ge 2 ] && echo "[VERBOSE ] API Token '$( echo $content | jq -r '.name' )' expires in $tokenExpirationDays days." ) + +# +# get hostname +# + +if [ -z $hostname ] +then + [ $verbose -ge 1 ] && echo "[INFO ] hostname not set." + hostname=$( hostname ) +fi +[ $verbose -ge 1 ] && echo "[INFO ] hostname is '$hostname'." + +# +# get actual cert data +# + +content=$( ra_portal GET list status=current subject_alternative_name=$hostname | jq ) +[ $verbose -ge 2 ] && echo -e "[VERBOSE ] RA-Portal Cert-List:\n$content" +certListElements=$( echo $content | jq 'length') +[ $certListElements -eq 0 ] && echo "[ERROR ] RA-Portal Cert-List empty." && exit 3 +[ $certListElements -gt 2 ] && echo "[ERROR ] RA-Portal Cert-List contains more than 2 elements." && exit 3 + +i=0 +while [ $i -lt $certListElements ] +do + referenceNumber=$( echo $content | jq -r ".[$i] | .reference_number" ) + enrolledAt=$( echo $content | jq -r ".[$i] | .enrolled_at" ) + expiresAt=$( echo $content | jq -r ".[$i] | .expires_at" ) + if [ "$expiresAt" = "null" ] + then + referenceNumberRenew=$referenceNumber + enrolledAtRenew=$enrolledAt + expiresAtRenew=$expiresAt + else + # did we found a secondary cert? + if [ $expiresAtCert ] + then + if [ $( date +%s -d $expiresAtCert ) -gt $( date +%s -d $expiresAt ) ] + then + referenceNumberRenew=$referenceNumberCert + enrolledAtRenew=$enrolledAtCert + expiresAtRenew=$expiresAtCert + else + referenceNumberRenew=$referenceNumber + enrolledAtRenew=$enrolledAt + expiresAtRenew=$expiresAt + ((i++)) + continue + fi + fi + referenceNumberCert=$referenceNumber + enrolledAtCert=$enrolledAt + expiresAtCert=$expiresAt + fi + ((i++)) +done +let expiresInCert=($( date +%s -d $expiresAtCert )-$( date +%s ))/86400 +[ $verbose -ge 1 ] && echo "[INFO ] RA-Portal Cert Reference Number: $referenceNumberCert" + +# +# renew certificate +# + +if [ $expiresInCert -le 28 ] +then + echo "[WARNING ] '$hostname' cert expires in $expiresInCert days." + if [ $referenceNumberRenew ] + then + echo "[WARNING ] renew '$hostname' already started at $referenceNumberRenew" + else + content=$( ra_portal POST renew reference_number=$referenceNumberCert | jq ) + [ $verbose -ge 1 ] && echo -e "[INFO ] request renew certificate." + [ $verbose -ge 2 ] && echo -e "[VERBOSE ] RA-Portal Renew:\n$content" + referenceNumberRenew=$( echo $content | jq -r ".reference_number" ) + enrolledAtRenew="null" + expiresAtRenew="null" + fi +else + [ $verbose -ge 1 ] && echo -e "[INFO ] everything looks good." + enrolledAtRenew="null" + expiresAtRenew="null" +fi + +# +# send request +# + +if [ $referenceNumberRenew ] +then + if [ "$enrolledAtRenew" = "null" ] + then + content=$( ra_portal POST enroll reference_number=$referenceNumberRenew | jq ) + [ $verbose -ge 1 ] && echo -e "[INFO ] enrolled renew request." + [ $verbose -ge 2 ] && echo -e "[VERBOSE ] RA-Portal Enroll:\n$content" + expiresAtRenew="null" + else + if [ "$expiresAtRenew" = "null" ] + then + echo "[WAITING ] renew '$hostname' already enrolled." + else + [ $verbose -ge 1 ] && echo "[INFO ] new certificate '$hostname' ready for download." + fi + fi +fi + +# +# file basename +# + +if [ $expiresAtRenew = "null" ] +then + referenceNumber=$referenceNumberCert + enrolledAt=$enrolledAtCert + expiresAt=$expiresAtCert +else + referenceNumber=$referenceNumberRenew + enrolledAt=$enrolledAtRenew + expiresAt=$expiresAtRenew +fi + +filebasename="$( echo $enrolledAt | cut -dT -f1 | sed 's/-//g' )-$( echo $expiresAt | cut -dT -f1 | sed 's/-//g' )" +[ ! -d $filebasename ] && mkdir $filebasename && [ $verbose -ge 1 ] && echo "[INFO ] created subdir $filebasename" +filebasename="$filebasename/$hostname.$filebasename" +[ $verbose -ge 2 ] && echo "[VERBOSE ] filebasename: $filebasename" + +# +# download cert +# + +if [ ! -e $filebasename.cert.pem ] +then + echo "[DOWNLOAD] $filebasename.cert.pem" + ra_portal GET download reference_number=$referenceNumber > $filebasename.cert.pem +else + [ $verbose -ge 1 ] && echo "[INFO ] $filebasename.cert.pem already downloaded" +fi +if [ ! -e $filebasename.intermediates.pem ] +then + echo "[DOWNLOAD] $filebasename.intermediates.pem" + ra_portal GET download-intermediates-only reference_number=$referenceNumber > $filebasename.intermediates.pem + [ $verbose -ge 2 ] && echo "[VERBOSE ] splitting intermediates file" + csplit -s -k -f $filebasename.intermediates.pem. -b %d $filebasename.intermediates.pem '/END CERT/+1' + [ $verbose -ge 2 ] && echo "[VERBOSE ] getting root hash" + rootHash=$( openssl x509 -in $( ls -1 $filebasename.intermediates.pem.* | tail -n1 ) -nocert -issuer_hash ) + rm $filebasename.intermediates.pem.* + [ $verbose -ge 2 ] && echo "[VERBOSE ] symlink root cert" + ln -s /etc/ssl/certs/$rootHash.0 $filebasename.root.pem + [ $verbose -ge 1 ] && echo "[INFO ] setting up chain cert+chain cert+intermediates" + cat $filebasename.intermediates.pem $filebasename.root.pem > $filebasename.chain.pem + cat $filebasename.cert.pem $filebasename.chain.pem > $filebasename.cert+chain.pem + cat $filebasename.cert.pem $filebasename.intermediates.pem > $filebasename.cert+intermediates.pem +else + [ $verbose -ge 1 ] && echo "[INFO ] $filebasename.intermediates.pem already downloaded" +fi + +# +# create symlinks +# + +if [ $symlinks ] +then + [ $verbose -ge 1 ] && echo "[INFO ] updating symlinks" + for n in cert chain intermediates cert+chain cert+intermediates + do + [ -L $n.pem ] && unlink $n.pem && [ $verbose -ge 2 ] && echo "[VERBOSE ] unlink $n.pem" + ln -s $filebasename.$n.pem $n.pem && [ $verbose -ge 2 ] && echo "[VERBOSE ] symlink $n.pem" + done +fi + +# +# revoke old cert +# + +if [ $revoke ] +then + if [ $referenceNumber != $referenceNumberCert ] + then + content=$( ra_portal POST revoke reference_number=$referenceNumberCert ) + [ $verbose -ge 1 ] && echo -e "[INFO ] old certificate revoked." + [ $verbose -ge 2 ] && echo -e "[VERBOSE ] RA-Portal revoke:\n$content" + fi +fi