Check Your SSL Certificates

Letting an SSL certificate expire is definitely an unforced error. To prevent it from happening again, I wrote a bash script that gets the notAfter date from an SSL certificate and returns 1 if expiration is impending, or 0 if not.

This script is written for Linux; the BSD date command has a different interface, and so this code would need to be changed accordingly.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#! /bin/bash

# This script checks the expiration date of an SSL certificate.  If the expiration date is 
# within N days of expiration, the script prints a message to stderr and exits with exit 
# code 1.  Otherwise it exits normally with no output.

#Named argument:

#--days : days before expiration to begin warnings.  Default value is 10.

# Positional arguments:
#     Path to SSL certificate to check.


# Set default value for WARN.
WARN=10

while [ "$#" -gt 0 ]; do
    case $1 in
        --days)       # Takes an option argument, ensuring it has been specified.
            if [ "$#" -gt 0 ]; then
                WARN=$2
                shift 2
                continue
            else
                echo 'ERROR: Must specify a non-empty "--days DAYS" argument.' >&2
                exit 1
            fi
            ;;
        --days=?*)
            WARN=${1#*=} # Delete everything up to "=" and assign the remainder.
            ;;
        --days=)         # Handle the case of an empty --days=
            echo 'ERROR: Must specify a non-empty "--days DAYS" argument.' >&2
            exit 1
            ;;
        --)              # End of all options.
            shift
            break
            ;;
        -?*)
            printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
            ;;
        *)               # Default case: If no more options then break out of the loop.
            break
    esac

    shift
done

SSLCERT_PATH=$1
if [[ -z $SSLCERT_PATH ]]; then
    echo "No path to an SSL certificate was provided." >&2
    exit 1
fi

if [[ ! -e $SSLCERT_PATH ]]; then
    echo "No file exists at path $SSLCERT_PATH." >&2
    exit 1
fi

SSLCERT_EXP_DATE=$(openssl x509 -enddate -noout -in $SSLCERT_PATH | cut -d '=' -f 2 | xargs -I {} date --date={} --rfc-3339 date)
SSLCERT_WARN_DATE=$(date --date "$SSLCERT_EXP_DATE - $WARN days"  --rfc-3339 'date')
TODAY=$(date --rfc-3339 'date')

if [[ "$SSLCERT_EXP_DATE" < "$TODAY" ]]; then
    echo SSL "certificate $SSLCERT_PATH expired on $(date +'%b %d, %Y' --date=$SSLCERT_EXP_DATE)." >&2
    exit 1
else
    if [[ "$SSLCERT_WARN_DATE" > "$TODAY" ]]; then 
        exit 0
    else
        echo SSL "certificate $SSLCERT_PATH expires on $(date +'%b %d, %Y' --date=$SSLCERT_EXP_DATE)." >&2
        exit 1
    fi
fi

I run this script once a day with cron. Here is my cron file, /etc/cron.d/ssl_check.

# This file is managed by Puppet.

SHELL=/bin/bash
MAILTO=charles@declaresub.com

0 6 * * *  root  /usr/local/bin/ssl_cert_check.bash /etc/ssl/localcerts/www.declaresub.com.chained.crt
0 6 * * *  root  /usr/local/bin/ssl_cert_check.bash /etc/ssl/localcerts/moof.declaresub.com.chained.crt

If you copy and paste, make sure that the file contains a trailing linefeed, or cron won't be happy.