#!/bin/bash # # check_certificate.sh # # Copyright (C) 2017, Branko Majic # # 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 . # program="check_certificate.sh" function usage() { cat <. EOF } function version() { cat < | | | | 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 . | +-----------------------------------------------------------------------+ 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