Files @ 1b8a8d6c641d
Branch filter:

Location: majic-ansible-roles/roles/common/files/check_certificate.sh

branko
MAR-158: Updated release notes.
#!/bin/bash
#
# check_certificate.sh
#
# Copyright (C) 2017, Branko Majic <branko@majic.rs>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

program="check_certificate.sh"

function usage() {
    cat <<EOF
$program, a non-interactive utility for checking certificates

Usage: $program [OPTIONS] check_type...

$program is a non-interactive utility for checking certificates. Utility
supports a number of different checks which are passed in as positional
arguments. The following checks are currently implemented:

expiration
  Checks if certificate expires within designated time. Expiration period can be
  specified via options (see below).

List of certificate files to check can be passed through two mutually exclusive
mechanisms - via options or through configuration files. If certificates are
specified through options, configuration files are not read.

Configuration files are by default read from directory
/etc/check_certificate/. Each configuration file is expected to end with
".conf". All other files will be ignored. A different configuration directory
can be also specified via an option.

Configuration files themselves should contain one certificate per line. Blank
lines will be ignored.


$program accepts the following options:

    -e period
        Number of days before certificate expires after which the certificate
        should be considered as about to expire. Value is used in the following
        check types: expiration.

    -c certificate_file
        Path to certificate file for which the checks should be run. This option
        can be specified multiple times on the command line in order to verify
        multiple certificates.

    -q
        Enable quiet mode. Output only warnings and errors.

    -d
        Enable debug output.

    -v
        Show script version and licensing information.

    -h
        Show usage help.


Please report bugs and send feature requests to <branko@majic.rs>.
EOF
}

function version() {
        cat <<EOF
$program

+-----------------------------------------------------------------------+
| Copyright (C) 2017, Branko Majic <branko@majic.rs>                    |
|                                                                       |
| This program is free software: you can redistribute it and/or modify  |
| it under the terms of the GNU General Public License as published by  |
| the Free Software Foundation, either version 3 of the License, or     |
| (at your option) any later version.                                   |
|                                                                       |
| This program is distributed in the hope that it will be useful,       |
| but WITHOUT ANY WARRANTY; without even the implied warranty of        |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         |
| GNU General Public License for more details.                          |
|                                                                       |
| You should have received a copy of the GNU General Public License     |
| along with this program.  If not, see <http://www.gnu.org/licenses/>. |
+-----------------------------------------------------------------------+

EOF
}

# Set-up colours for message printing if we're not piping and terminal is
# capable of outputting the colors.
_color_terminal=$(tput colors 2>&1)
if [[ -t 1 ]] && (( ${_color_terminal} > 0 )); then
    _text_bold=$(tput bold)
    _text_white=$(tput setaf 7)
    _text_blue=$(tput setaf 6)
    _text_green=$(tput setaf 2)
    _text_yellow=$(tput setaf 3)
    _text_red=$(tput setaf 1)
    _text_reset=$(tput sgr0)
else
    _text_bold=""
    _text_white=""
    _text_blue=""
    _text_green=""
    _text_yellow=""
    _text_red=""
    _text_reset=""
fi

# Set-up functions for printing coloured messages.
function debug() {
    [[ $DEBUG != 0 ]] && echo "${_text_bold}${_text_blue}[DEBUG]${_text_reset}" "$@"
}

function info() {
    [[ $QUIET == 0 ]] && echo "${_text_bold}${_text_white}[INFO] ${_text_reset}" "$@"
}

function success() {
    [[ $QUIET == 0 ]] && echo "${_text_bold}${_text_green}[OK]   ${_text_reset}" "$@"
}

function warning() {
    echo "${_text_bold}${_text_yellow}[WARN] ${_text_reset}" "$@"
}

function error() {
    echo "${_text_bold}${_text_red}[ERROR]${_text_reset}" "$@" >&2
}

#
# Checks expiration of passed-in certificate file.
#
# Arguments:
#
#   $1 - Path to certificate file to check
#
# Returns:
#
#   0 if check has passed, 1 if check has not passed.
#
function check_expiration() {
    local certificate_file="$1"
    local certificate_expiration_date certificate_expiration_date_in_seconds certificate_expires_in
    local current_date_in_seconds
    local expiration_period_seconds
    local status

    let expiration_period_seconds="$expiration_period"*24*60*60

    debug "Running expiration check for file: $certificate_file"
    debug "Expiration period set to: $expiration_period"

    certificate_expiration_date=$(openssl x509 -enddate -noout -in "$certificate_file" | sed -e 's/^notAfter=//')
    certificate_expiration_date_in_seconds=$(date -d "$certificate_expiration_date" "+%s")
    current_date_in_seconds=$(date "+%s")
    let certificate_expires_in="$certificate_expiration_date_in_seconds-$current_date_in_seconds"

    if (( $certificate_expires_in >= 0 )); then
        status="expires"
    else
        status="expired"
    fi

    if openssl x509 -noout -in "$certificate_file" -checkend "$expiration_period_seconds" > /dev/null; then
        success "Certificate $certificate_file $status on $certificate_expiration_date, $(print_relative_period "$certificate_expires_in")."
	return 0
    else
        error "Certificate $certificate_file $status on $certificate_expiration_date, $(print_relative_period "$certificate_expires_in")."
	return 1
    fi
}

#
# Outputs period relative to current time in human-readable format
# with granularity in days.
#
# Arguments:
#
#   $1 - Time period in seconds. Can be negative to denote past.
#
function print_relative_period() {
    local seconds="$1"
    local days leftover

    let days="$seconds/(60*60*24)"

    if (( $days == 1 )); then
        echo "in $days day"
    elif (( $days > 1 )); then
        echo "in $days days"
    elif (( $days == 0 && $seconds > 0 )); then
        echo "in less than a day"
    elif (( $days == 0 && $seconds < 0 )); then
        echo "less than a day ago"
    elif (( $days == -1 )); then
        echo "one day ago"
    elif (( $days < -1 )); then
        echo "${days#-} days ago"
    fi
}

# Exit codes
ERROR_SUCCESS=0
ERROR_PARAMETERS=1
ERROR_FAILED_CHECK=2

# If no arguments were given, just show usage help.
if [[ -z $1 ]]; then
    usage
    exit $ERROR_SUCCESS
fi

# Disable debug by default.
DEBUG=0
QUIET=0

# Set-up default option values.
let expiration_period=30

# List of certificate files to check.
certificate_files=()
configuration_directory="/etc/check_certificate"

# Parse the arguments
while getopts "e:c:C:xqdvh" opt; do
    case "$opt" in
	e) let expiration_period="$OPTARG";;
	c) certificate_files+=("$OPTARG");;
	C) configuration_directory="$OPTARG";;
        q) QUIET=1;;
	d) DEBUG=1;;
        v) version
           exit $ERROR_SUCCESS;;
        h) usage
           exit $ERROR_SUCCESS;;
        *) usage
           exit $ERROR_PARAMETERS;;
    esac
done
i=$OPTIND
shift $(($i-1))

# Verify parameters.
if [[ $# == 0 ]]; then
    error "At least one valid check type must be specified."
    exit $ERROR_PARAMETERS
fi
for check in "$@"; do
    if [[ $check != expiration ]]; then
	error "Unsupported check type specified: $check"
	exit $ERROR_PARAMETERS
    fi
done

# Load list of certificate files from configuration files if none were specified
# via options.
if [[ ${#certificate_files[@]} == 0 ]]; then
    for configuration_file in "$configuration_directory"/*.conf; do
	if [[ -f $configuration_file ]]; then
	    DONE=false
	    until "$DONE"; do
		read line || DONE=true
		[[ ! $line =~ ^[[:blank:]]*$ ]] && certificate_files+=("$line")
	    done < "$configuration_file"
	fi
    done
fi

# Inform user that no certificates have been configured for checking.
if [[ ${#certificate_files[@]} == 0 ]]; then
    info "No certificate files were specified for checking."
fi

# Process the certificate files.
result=$ERROR_SUCCESS
for certificate_file in "${certificate_files[@]}"; do
    for check in "$@"; do
	if ! check_"$check" "${certificate_file}"; then
	    result=$ERROR_FAILED_CHECK
	fi
    done
done

if [[ $result == $ERROR_SUCCESS ]]; then
    success "All checks have passed for all certificates."
else
    error "There are some checks that have failed for at least one certificate file. Check output from the script."
fi

exit $result