Changeset - 0b86d3da5a29
[Not reviewed]
0 5 0
Branko Majic (branko) - 6 years ago 2018-07-23 22:27:52
branko@majic.rs
MAR-133: Improve output for certificate checks:

- Do not produce warnings in case no certificates have been configured
for checking.
- Only send out mails about certificates that are about to expire.
- Include information in how many days a certificate is going to
expire.
- Include information on whether the certificates has already expired.
5 files changed with 70 insertions and 19 deletions:
0 comments (0 inline, 0 general)
docs/releasenotes.rst
Show inline comments
 
@@ -30,12 +30,17 @@ New features/improvements:
 

	
 
  * The ``pip`` requirements upgrade checks are now performed once per
 
    day instead of once per hour.
 
  * The ``pip`` requirements upgrade checks now do not output warning
 
    in case deployed ``.in`` file does not have a matching ``.txt``
 
    file.
 
  * Certificate expiration check is less verbose. No mails are sent
 
    out any longer in case no certificates have been configured for
 
    checking, nor in cases where all certificates have passed the
 
    check. E.g. mails are sent out only in case some of the configured
 
    certificates will expire within next 30 days.
 

	
 

	
 
2.0.0
 
-----
 

	
 
Upgrade to Ansible 2.3.x, minor bug fixes and updates needed for the upgrade.
docs/rolereference.rst
Show inline comments
 
@@ -279,13 +279,15 @@ The role implements the following:
 
  (SSH), and also introduces rate-limitting for incoming ICMP echo request
 
  pacakges and (new) TCP connections. The rate-limitting is based on the source
 
  IP address, using the ``iptables hashlimit`` module.
 
* Sets-up system for performing checks on certificates (currently only if they
 
  expire within less than 30 days). Roles that want their certificates checked
 
  should deploy a ``.conf`` to directory ``/etc/check_certificate/`` with paths
 
  to certificate files, one per line. Certificates are checked on daily basis.
 
  to certificate files, one per line. Certificates are checked on
 
  daily basis, using crontab (resulting in failures being sent out to
 
  the ``root`` user).
 
* Deploys ``apticron`` package that performs checks for available package
 
  upgrades on daily basis. Mails are delivered to local ``root`` account, and
 
  can be redirected elsewhere via aliases. If using ``mail_forwarder`` or
 
  ``mail_server`` roles on the same server, aliases can be set-up through them.
 
* Sets-up system for performing checks on pip requirements files. Roles that
 
  want their requirements files checked should create a sub-directory inside of
docs/usage.rst
Show inline comments
 
@@ -591,17 +591,18 @@ one up first. This includes both the LDAP *server* and *client* configuration.
 
   ``ldap_server`` too. So, let's make a slight detour to create a CA
 
   of our own, plus the necessary server certificate for the LDAP
 
   service...
 

	
 
   .. note::
 
      Another useful feature the roles implement is a check to see if
 
      certificates will expire within the next 30 days. This check is performed
 
      via cronjob at midnight, and results will end-up being delivered to the
 
      ``root`` user on local server. Later on, once you have configured the mail
 
      server, you should be able to set-up the necessary aliases to have the
 
      mails delivered to non-local accounts too.
 
      certificates will expire within the next 30 days. This check is
 
      performed via cronjob at midnight, and failing results will
 
      end-up being delivered to the ``root`` user on local
 
      server. Later on, once you have configured the mail server, you
 
      should be able to set-up the necessary aliases to have the mails
 
      delivered to non-local accounts too.
 

	
 
   1. Let's first install a couple of more tools on the Ansible server, since we
 
      will be using ``certtool`` for our improvised CA needs (run this as
 
      ``root``)::
 

	
 
        apt-get install -y gnutls-bin
roles/common/files/check_certificate.sh
Show inline comments
 
@@ -56,12 +56,14 @@ $program accepts the following options:
 

	
 
    -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.
 
@@ -118,23 +120,21 @@ else
 
    _text_red=""
 
    _text_reset=""
 
fi
 

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

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

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

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

	
 
@@ -152,30 +152,71 @@ function error() {
 
# Returns:
 
#
 
#   0 if check has passed, 1 if check has not passed.
 
#
 
function check_expiration() {
 
    local certificate_file="$1"
 
    local certificate_file_expiration_date
 
    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_file_expiration_date=$(openssl x509 -enddate -noout -in "$certificate_file" | sed -e 's/^notAfter=//')
 
    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 "Expiration check ($expiration_period days) passed for $certificate_file (expires on $certificate_file_expiration_date)."
 
        success "Certificate $certificate_file $status on $certificate_expiration_date, $(print_relative_period "$certificate_expires_in")."
 
	return 0
 
    else
 
	error "Expiration check ($expiration_period days) failed for $certificate_file (expires on $certificate_file_expiration_date)."
 
        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.
 
@@ -183,26 +224,28 @@ 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:xdvh" opt; do
 
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
 
@@ -235,15 +278,15 @@ if [[ ${#certificate_files[@]} == 0 ]]; then
 
		[[ ! $line =~ ^[[:blank:]]*$ ]] && certificate_files+=("$line")
 
	    done < "$configuration_file"
 
	fi
 
    done
 
fi
 

	
 
# Log a warning if list of certificates is empty.
 
# Inform user that no certificates have been configured for checking.
 
if [[ ${#certificate_files[@]} == 0 ]]; then
 
    warning "No certificate files were specified for checking."
 
    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
roles/common/tasks/main.yml
Show inline comments
 
@@ -264,13 +264,13 @@
 
- name: Deploy crontab entry for checking certificates
 
  cron:
 
    name: "check_certificate"
 
    cron_file: "check_certificate"
 
    hour: 0
 
    minute: 0
 
    job: "/usr/local/bin/check_certificate.sh expiration"
 
    job: "/usr/local/bin/check_certificate.sh -q expiration"
 
    state: present
 
    user: nobody
 

	
 
- name: Install apticron (for checking available upgrades)
 
  apt:
 
    name: apticron
0 comments (0 inline, 0 general)