#!/bin/bash
##
# check_ssl_cert_wrapper
#
# * wrapper wraps check_ssl_cert to validate cert with tls destination and port
# * wrapper checks if a tls endpoint offers tlsv1.2 by using openssl
#
# * On every host with a cert, puppet module twitch_ssl_certificate builds a file /etc_ssl_validate.conf ,
#   which contains a line for every managed cert on the box.  Each line in /etc/ssl_validate.conf is parsed
#   into server:port:path-to-cert arguments to be used with check_ssl_cert
# * check_ssl_cert is run with arguments from /etc/ssl_validate.conf
# * output of check_ssl_cert is saved in ERRMSG .
# * if check_ssl_cert was OK, info about cert is saved in PERF_DATA
# * exit code of check_ssl_cert is saved
# * if check_ssl_cert exit code is OK , openssl is used to see if tls endpoint offers tls v1.2
# * if tls endpoint offers tls v1.2, tls info is saved to PERF_DATA
# * if tls endpoint does not offer tls v1.2, informative error is added to ERRMSG
# * if status of check_ssl_cert and checking tls v1.2 is OK, PERF_DATA is added to ERRMSG
# * script outputs ERRMSG and exit code

command="/opt/twitch/certzero/bin/check_ssl_cert"

function usage {
    echo "Usage: ${0} config_file [OPTIONS]"
    echo
    echo "Arguments:"
    echo "  config_file    Config file to load containing args for ${command}"
    echo "  [OPTIONS]      Further options are passed on to each check verbatim"
    echo 
    echo "Config file contains one check per line, args separated by | (pipe)"
    echo "      it is naive  does not handle | in the args themselves"
    exit 3
}

function cert_cn() {
  echo $1 | awk -F"'" '{ print $2 }'
}

function cert_expiry() {
  echo $1 | sed 's/.*expires in //' | sed 's/).*$//'
}

if [[ -f $1 ]] ; then
    CONF_FILE=$1 && shift
    # config file passed, loop the $command on the config items
    COUNT=0
    ERRSTAT=0
    ERRMSG=''
    while read -r line ; do

	# parse /etc/ssl_validate.conf into
	# array that will used as parameters to check_ssl_cert
        args=()
        IFS='|'
        o=0
        nerr=0
        args=( $line )

        ((++COUNT))

	# save output of check_ssl_cert + parameters
        errm=$(${command} "${@}" "${args[@]}" 2>&1)

	# save exit value of check_ssl_cert
	# possible values are the usual nagios exit codes
	#
        # 0 - status is OK
        # 1 - status is WARNING
        # 2 - status is CRITICAL
        # 3 - status is UNKNOWN
        nerr=$?

        CERT_PATH="${args[5]}"
        TLS_ENDPOINT_HOST=${args[1]}
        TLS_ENDPOINT_PORT=${args[3]}
        CERT_CN="$(cert_cn ${errm})"
        CERT_EXPIRY="$(cert_expiry ${errm})"

        if [ $nerr -eq 0 ]; then
	  # if status is OK, add to array PERF_DATA info about cert.
	  # PERF_DATA array will be printed out by nagios check at end if status OK for all checks
          PERF_DATA+=("OK: valid cert ${CERT_PATH}, CN ${CERT_CN}, expires ${CERT_EXPIRY}")
	  # we use check_ssl_cert to check expiry date of cert, even if there is no
	  # corresponding TLS endpoint.
	  #
	  # check_ssl_cert requires a tls endpoint host and tls endpoint port to check any cert,
	  # even if there is no corresponding TLS endpoint.
	  # in the case of checking a cert and no TLS endpoint, check_ssl_cert requires a host
	  # and port, but the script does not actually do anything with the host and port.
	  #
	  # in the case of the Puppet CA cert, we put a host and port in /etc/ssl_validate , but
	  # it does not make sense to check if the service that uses that cert supports tls v1.2,
	  # because there is no host and port to check.
	  #
	  # a more robust solution would be rework the way twitch_ssl_validate writes
	  # check_ssl_cert args to /etc/ssl_validate.conf , use a json blob instead, make
	  # a field that signals we are validating a cert with no TLS endpoint
          #
	  # skip getting TSLv1.2 availability info for the Puppet CA cert:
          if [[ "${CERT_CN}" != *"Puppet CA: video-puppet-ca.prod.puppet.live-video.a2z.com"* ]]; then
	    if openssl s_client -tls1_2 -connect ${TLS_ENDPOINT_HOST}:${TLS_ENDPOINT_PORT} > /dev/null 2>&1 </dev/null; then
              PERF_DATA+=("OK: TLS endpoint ${TLS_ENDPOINT_HOST}:${TLS_ENDPOINT_PORT} offers TLSv1.2")
            else
	      # if TLS endpoint did not offer TLSv1.2, add this to TLS_VERSION_ERRORS , which will be printed out
	      # by the check if check_ssl_cert returns CRITICAL
              TLS_VERSION_ERRORS+="TLS CRIT: service ${TLS_ENDPOINT_HOST}:${TLS_ENDPOINT_PORT} does not offer TLSv1.2 (required)"
              nerr=2
            fi
          fi
        fi

        # Keep CRITs first
        if [ $nerr -eq 2 ] ; then
            ERRSTAT=2
	    # add to ERRMSG existing errors from check_ssl_script and openssl tlsv1.2 checking
            ERRMSG+="${errm}:${args[1]} ${ERRMSG} ${TLS_VERSION_ERRORS}"
        else
            [ $ERRSTAT -ne 2 ] && [ $nerr -gt $ERRSTAT ] && ERRSTAT=$nerr
            [ $nerr -gt 0 ] && ERRMSG+="${errm}:${args[1]}"
        fi
    done < $CONF_FILE
    # if we only got OK from check_ssl_cert and status from check_ssl_cert was never CRITICAL,
    # add the values from PERF_DATA array to ERRMSG.
    # ERRMSG will be printed out at end of Nagios check
    if [ $ERRSTAT -eq 0 ]; then
      for line in "${PERF_DATA[@]}"; do
        ERRMSG+=${line}
        ERRMSG+=$'\n'
      done
    fi

    # building the ERRMSG output adds an extra newline.  remove it. 
    NL=$'\n'
    ERRMSG=${ERRMSG%$NL}

    echo ${ERRMSG}
    exit $ERRSTAT
else
    usage
fi
