#!/bin/sh
# -*- sh -*-

: << =cut

=head1 NAME

ssl-certificate-expiry - Plugin to monitor CERTificate expiration on multiple services and ports

=head1 CONFIGURATION

  [ssl-certificate-expiry]
    env.services www.service.tld blah.example.net_PORT

To set warning and critical levels do like this:

  [ssl-certificate-expiry]
    env.services ...
    env.warning 30:

Alternatively, if you want to monitor hosts separately, you can create multiple symlinks named as follows.

  ssl-certificate-expiry_HOST_PORT

For example:

  ssl-certificate-expiry_www.example.net
  ssl-certificate-expiry_www.example.org_443
  ssl-certificate-expiry_192.0.2.42_636
  ssl-certificate-expiry_2001:0DB8::badc:0fee_485

=head1 AUTHOR

Pactrick Domack (ssl_)
Olivier Mehani (ssl-certificate-expiry)

Copyright (C) 2013 Patrick Domack <patrickdk@patrickdk.com>
Copyright (C) 2017 Olivier Mehani <shtrom+munin@ssji.net>

=head1 LICENSE

=cut

# shellcheck disable=SC1090
. "${MUNIN_LIBDIR}/plugins/plugin.sh"

if [ "${MUNIN_DEBUG}" = 1 ]; then
    set -x
fi

HOSTPORT=${0##*ssl-certificate-expiry_}

if [ "${HOSTPORT}" != "${0}" ] \
	&& [ ! -z "${HOSTPORT}" ]; then
	services="${HOSTPORT}"
fi


# Read data including a certificate from stdin and output the (fractional) number of days left
# until the expiry of this certificate. The output is empty if parsing failed.
parse_valid_days_from_certificate() {
    local input_data
    local valid_until_string
    local valid_until_epoch
    local now_epoch
    local input_data
    input_data=$(cat)
    if echo "$input_data" | grep -q -- "-----BEGIN CERTIFICATE-----"; then
        valid_until_string=$(echo "$input_data" | openssl x509 -noout -enddate \
            | grep "^notAfter=" | cut -f 2 -d "=")
        if [ -n "$valid_until_string" ]; then
            valid_until_epoch=$(date --date="$valid_until_string" +%s)
            if [ -n "$valid_until_epoch" ]; then
                now_epoch=$(date +%s)
                # calculate the number of days left
                echo "$valid_until_epoch" "$now_epoch" | awk '{ print(($1 - $2) / (24 * 3600)); }'
            fi
        fi
    fi
}


print_expire_days() {
    local host="$1"
    local port="$2"

    # Wrap IPv6 addresses in square brackets
    echo "$host" | grep -q ':' && host="[$host]"

    echo "" | openssl s_client -CApath /etc/ssl/certs \
            -servername "$host" -connect "${host}:${port}" 2>/dev/null \
        | parse_valid_days_from_certificate
}


case $1 in
    config)

	echo "graph_title SSL Certificates Expiration"
	echo 'graph_args --base 1000'
	echo 'graph_vlabel days left'
	echo 'graph_category security'
	echo "graph_info This graph shows the days left for the certificate"
	for service in $services; do
	    fieldname=$(clean_fieldname "$service")
	    echo "${fieldname}.label $(echo "${service}" | sed 's/_/:/')"
	    print_thresholds "${fieldname}"
	done

	exit 0
	;;
esac


for service in $services; do
    if echo "$service" | grep -q "_"; then
        host=$(echo "$service" | cut -f 1 -d "_")
        port=$(echo "$service" | cut -f 2 -d "_")
    else
        host=$service
        port=443
    fi
    fieldname="$(clean_fieldname "$service")"
    valid_days=$(print_expire_days "$host" "$port")
    [ -z "$valid_days" ] && valid_days="U"
    printf "%s.value %s\n" "$fieldname" "$valid_days"
done
