#!/bin/bash
# ======================================================================
#
# NAGIOS CLIENT CHECK :: check SSL certificate
# this pligin show a warning if certificate expires in less than
# ${iWarnDaysBefore} days
#
# REQUIREMENTS
# - openssl
# - single cert on a host ??
#
# ----------------------------------------------------------------------
#
# ah=axel.hahn@iml.unibe.ch
# ds=daniel.schueler@iml.unibe.ch
#
# 2017-03-03  v1.0  ah,ds
# 2020-03-05  v1.1  <axel.hahn@iml.unibe.ch>  switch to ph.* helper functions
# 2023-02-13  v1.2  <axel.hahn@unibe.ch>      some shell fixes
# 2023-08-23  v1.3  <axel.hahn@unibe.ch>      fix wrong exitcode to "critical"
# 2025-02-12  v1.4  <axel.hahn@unibe.ch>      add IML header in help; add warning and critical level
# 2025-02-10  v1.5  <axel.hahn@unibe.ch>      harden sourcing files
# ======================================================================

# shellcheck source=/dev/null
. "$( dirname "$0" )/inc_pluginfunctions" || exit 1

self_APPVERSION=1.5

sDomain=
iPort=443

typeset -i iErrors=0
typeset -i iWarnings=0

sStatus=

# ----------------------------------------------------------------------
# functions
# ----------------------------------------------------------------------

# show help with syntax
function showHelp(){
    local _self; _self="$( basename "$0" )"
cat <<EOH
$( ph.showImlHelpHeader )

Check if ssl certificate of a given domain is still valid.
You can check https or any other port of a ssl enabled service like LDAPS, 
IMPAS and others.

You can customize the values for warning and critical level.

SYNTAX: $_self [options] DOMAIN [PORT]

OPTIONS
    -w VALUE  warning level for expiration in days (default: 28)
    -c VALUE  critical level for expiration in days (default: 7)

PARAMETERS
    DOMAIN    domain to verify the ssl vertificate from (required)
    PORT      optional: port number to connect (default: 443)


EXAMPLES

    $_self www.iml.unibe.ch 443
        check https port 443

    $_self -w 30 -c 14 ldap.example.com 636
        check ldaps port 636 and set custom warning and critical level

EOH
}



# ----------------------------------------------------------------------
# MAIN
# ----------------------------------------------------------------------

# --- check requirements

ph.require openssl

if [ $# -eq 0 ]; then
    showHelp
    exit 0
fi

# --- start

# set default / override from command line params
typeset -i iWarnLimit;     iWarnLimit=$(     ph.getValueWithParam 28 w "$@")
typeset -i iCriticalLimit; iCriticalLimit=$( ph.getValueWithParam 7  c "$@")

sParams="$*"
sP1="$( rev <<< $sParams | cut -f 2 -d ' ' | rev )"
sP2="$( rev <<< $sParams | cut -f 1 -d ' ' | rev )"

if grep -q "^[0-9]*$" <<< $sP2; then
    sDomain=$sP1
    iPort=$sP2
else
    sDomain=$sP2
fi

# --- try to connect

echo | openssl s_client -connect ${sDomain}:${iPort} >/dev/null 2>&1  
if [ $? -ne 0 ]; then
    ph.setStatus "critical"
    ph.status "unable to connect to ${sDomain} via port :${iPort} - maybe wrong host ... or port ... wrong chaining"
    # repeat the last command without redirecting output
    echo | openssl s_client -connect ${sDomain}:${iPort}
    ph.exit
fi

echo | openssl s_client -connect ${sDomain}:${iPort} 2>/dev/null | openssl x509 -noout -subject | grep -F ${sDomain} >/dev/null
if [ $? -ne 0 ]; then
    ph.setStatus "unknown"
    echo SORRY, openssl was unable to fetch the right certificate - this happens on multiple ssl webs - it finds
    echo | openssl s_client -connect ${sDomain}:${iPort} 2>/dev/null | openssl x509 -noout -subject
    ph.exit
fi

# --- unix timestamps valid from .. to

dateFrom=$(echo | openssl s_client -connect ${sDomain}:${iPort} 2>/dev/null | openssl x509 -noout -startdate | cut -f 2 -d "=")
dateTo=$(echo   | openssl s_client -connect ${sDomain}:${iPort} 2>/dev/null | openssl x509 -noout -enddate   | cut -f 2 -d "=")

tsFrom=$(date -d "${dateFrom}" +%s)
tsTo=$(date -d "${dateTo}" +%s)

tsNow=$(date +%s)
typeset -i iDaysLeft=($tsTo-$tsNow)/60/60/24

# --- check date

if [ ${tsFrom} -gt ${tsNow} ]; then
    ph.setStatus "critical"
    ph.status "certificate ${sDomain}:${iPort} is not valid yet - ${dateFrom}"
    else
        if [ ${tsTo} -lt ${tsNow} ]||[ ${iDaysLeft} -le $iCriticalLimit ]; then
            ph.setStatus "critical"
            ph.status "certificate ${sDomain}:${iPort} is out of date - ${dateTo} - ${iDaysLeft} days"
        else
            # --- check close ending day
            if [ ${iDaysLeft} -lt ${iWarnLimit} ]; then
                ph.setStatus "warning"
                ph.status "certificate ${sDomain}:${iPort} is out of date - ${dateTo} - ${iDaysLeft} days"
            else
                ph.setStatus "ok"
                ph.status "${sDomain}:${iPort} - valid to ${dateTo} (${iDaysLeft} days left)"
        fi
    fi
fi

ph.exit

# ----------------------------------------------------------------------