Automatic certificate expiration check

If you authenticate certain services on your Linux server like your WLAN or webserver towards users for their security you will likely have multiple certificates. These usually have a certain period during which they are valid. This means you have to renew them from time to time.

If you don't track the certificates' expiration times you likely only notice an outdated certificate if a horde of users knocks on your door complaining that a certain service is out of order. Good luck fixing this problem with a mob of people in your back throwing torches and pitchforks at you.

One option is to keep a Post-It with all important dates for each single certificate on your screen. Unfortunately if you do this for other jobs too the notes will either cover your screen or each other. The result is the same. You won't be able to do your work properly any more. The more lazy (i.e. better) way is to let a script automatically warn you in advance before expiration.

The good news is that you don't have to write the script on your own. I already prepared one which you can use under a CC BY license.

It can be executed as cron job or used as Nagios check out of the box. A Zabbix check will need some minor adjustments. I recommend just echoing the remaining days. In that case you can completely drop the existing output part and criticality check including the days input parameters. This is all done in Zabbix

The script calculates the remaining days until a certificate expires. Note that I ignore leap years and that months have a diffent amount of days. This is sufficiently accurate for me. As follows I don't see the point in wasting my time in programming a more detailed solution.

You can give the script the path to any certificate (for which you have read permission). Furthermore it will throw a critical or warning message depending on an adjustable amount of days left. A separate message will be given if the certificate already expired.

check_certificate.sh:
#!/bin/sh
# This script is published under a Creative Commons BY license
# Written by XXLRay http://www.xxlray.bplaced.net


USAGE="\n
Checks whether a certificate expires. The script ignores leap years and that months have a different amount of days.\n
\n
Written by XXLRay http://www.xxlray.bplaced.net
\n
Usage: $(basename $0) [-h] -i -w <warnlevel> -c <critlevel>\n
  file: path and name of certficate to check\n
  warnlevel: warning shall be given when that many days are left\n
  critlevel: critical shall be reported when that many days are left\n
\n
e.g.: ./$(basename $0) -i /etc/raddb/certs/radius.cert.pem -w 30 -c 7\n"

while getopts hi:c:w: OPT
do
  case "$OPT" in
    h)  echo -e "$USAGE"
        exit 1
        ;;
    i)  FILE=$OPTARG
        ;;
    w)  WARNLEVEL=$OPTARG
        ;;
    c)  CRITLEVEL=$OPTARG
        ;;
  [?])  echo -e "$USAGE" >&2
        exit 1
        ;;
  esac
done

# check parameters
if [ "$FILE" == "" -o "$WARNLEVEL" == "" -o "$CRITLEVEL" == "" ]
then
  echo -e "At least one of the arguments was empty!\n"
  echo -e $USAGE
  exit 1
fi
# determine when certificate expires
EXPIRATIONDATE=$(openssl x509 -in $FILE -noout -enddate | sed -e 's/notAfter=//g' -e 's/ *Jan */01;/g' -e 's/ *Feb */03;/g' -e 's/ *Mar */03;/g' -e 's/ *Apr */04;/g' -e 's/ *May */05;/g' -e 's/ *Jun */06;/g' -e 's/ *Jul */07;/g' -e 's/ *Aug */08;/g' -e 's/ *Sep */09;/g' -e 's/ *Oct */10;/g' -e 's/ *Nov */11;/g' -e 's/ *Dec */12;/g' -e 's/ *[0-9][0-9]:[0-9][0-9]:[0-9][0-9] */;/g' -e 's/ *GMT *//g')
EXPIRATIONYEAR=$(echo $EXPIRATIONDATE | cut -f3 -d";")
EXPIRATIONMONTH=$(echo $EXPIRATIONDATE | cut -f1 -d";" | sed 's/^0*//')
EXPIRATIONDAY=$(echo $EXPIRATIONDATE | cut -f2 -d";" | sed 's/^0*//')
# inspect todays date
CURRENTDATE=$(date +%m\;%d\;%Y)
CURRENTYEAR=$(echo $CURRENTDATE | cut -f3 -d";")
CURRENTMONTH=$(echo $CURRENTDATE | cut -f1 -d";" | sed 's/^0*//')
CURRENTDAY=$(echo $CURRENTDATE | cut -f2 -d";" | sed 's/^0*//')
# calculate how many days are left
YEARDIST=$(($EXPIRATIONYEAR - $CURRENTYEAR))
MONTHDIST=$(($EXPIRATIONMONTH - $CURRENTMONTH))
DAYDIST=$(($EXPIRATIONDAY - $CURRENTDAY))
REMAININGDAYS=$(($YEARDIST*365)) # ignoring leap years
REMAININGDAYS=$(($REMAININGDAYS + $MONTHDIST*30)) # ignoring different day count per month
REMAININGDAYS=$(($REMAININGDAYS + $DAYDIST))
# preparing for human readable output
if [ $DAYDIST -lt 0 ]
then
  DAYDIST=$((30 + $DAYDIST)) # ignoring different day count per month
  MONTHDIST=$(($MONTHDIST - 1))
fi
if [ $MONTHDIST -lt 0 ]
then
  MONTHDIST=$((12 + $MONTHDIST))
  YEARDIST=$(($YEARDIST - 1))
fi
# check criticality
if [ $REMAININGDAYS -lt 1 ]
then
  echo "Certificate $FILE expired!"
  exit 2
fi
if [ $REMAININGDAYS -lt $CRITLEVEL ]
then
  echo "Certificate $FILE expires in $YEARDIST years $MONTHDIST months $DAYDIST days. This is critical!"
  exit 2
fi
if [ $REMAININGDAYS -lt $WARNLEVEL ]
then
  echo "Warning - Certificate $FILE expires in $YEARDIST years $MONTHDIST months $DAYDIST days."
  exit 1
fi
# remaining time is still fine
exit 0