From f3c443d07687fc5621528a76e473c26cb47a3943 Mon Sep 17 00:00:00 2001 From: Jeremy Dubois Date: Tue, 15 Feb 2022 15:50:04 +0100 Subject: [PATCH] openvpn: now check that openvpn has been restarted since last certificates renewal --- CHANGELOG.md | 1 + openvpn/files/check_openvpn_certificates.sh | 176 +++++++++++++------- 2 files changed, 121 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f413ee4..59d04a55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The **patch** part changes is incremented if multiple releases happen the same m * Explicit permissions for systemd overrides * evolinux-base: option to bypass raid-related tasks * kvm-host: add missing default value +* openvpn: now check that openvpn has been restarted since last certificates renewal ### Changed diff --git a/openvpn/files/check_openvpn_certificates.sh b/openvpn/files/check_openvpn_certificates.sh index 47dca4b6..e010ce07 100644 --- a/openvpn/files/check_openvpn_certificates.sh +++ b/openvpn/files/check_openvpn_certificates.sh @@ -16,13 +16,22 @@ CA_ECHO="" error() { if [ $? -eq 2 ] && [ "X$CERT_ECHO" = "X" ] && [ "X$CA_ECHO" = "X" ] ; then - echo "CRITICAL - The check exited with an error. Is the conf_file var containing the real conf file location ? On Debian, is the check executed with sudo ?" + echo "CRITICAL - The check exited with an error. Is the conf_file var containing the real conf file location ? On Debian, is the check executed with sudo ? On OpenBSD, is the check executed with doas ? Is OpenVPN running ?" fi } SYSTEM=$(uname | tr '[:upper:]' '[:lower:]') date_cmd=$(command -v date) +# Some backup servers don't have OpenVPN running while they are backup +is_backup_not_running_openvpn="1" +if [ "$SYSTEM" = "openbsd" ]; then + carp=$(/sbin/ifconfig carp0 2>/dev/null | grep 'status' | cut -d' ' -f2) + if [ "$carp" = "backup" ] && ! rcctl ls on | grep -q openvpn; then + is_backup_not_running_openvpn="0" + fi +fi + # Dates in seconds _15_days="1296000" _30_days="2592000" @@ -30,7 +39,7 @@ current_date=$($date_cmd +"%s") # Trying to define the OpenVPN conf file location - default to /etc/openvpn/server.conf conf_file=$(ps auwwwx | grep openvpn | grep -- --config | grep -v sed | sed -e "s/.*config \(\/etc\/openvpn.*.conf\).*/\1/" | head -1) -[ "$SYSTEM" = "openbsd" ] && conf_file=${conf_file:-$(grep openvpn_flags /etc/rc.conf.local | sed -e "s/.*config \(\/etc\/openvpn.*.conf\).*/\1/")} +if [ "$SYSTEM" = "openbsd" ]; then conf_file=${conf_file:-$(grep openvpn_flags /etc/rc.conf.local | sed -e "s/.*config \(\/etc\/openvpn.*.conf\).*/\1/")}; fi conf_file=${conf_file:-"/etc/openvpn/server.conf"} # Get the cert and ca file location, based on the OpenVPN conf file location @@ -44,22 +53,34 @@ ca_file=$(echo $ca_file | sed -e "s/^ca *\//\//") cert_expiration_date=$(grep "Not After" $cert_file | sed -e "s/.*Not After : //") ca_expiration_date=$(openssl x509 -enddate -noout -in $ca_file | cut -d '=' -f 2) +# Get the date of last modification of cert and ca certificates +if [ "$SYSTEM" = "openbsd" ]; then + seconds_last_cert_modification_date=$(stat -f %m "$cert_file") + seconds_last_ca_modification_date=$(stat -f %m "$ca_file") +else + seconds_last_cert_modification_date=$(stat -c %Y "$cert_file") + seconds_last_ca_modification_date=$(stat -c %Y "$ca_file") +fi + +# Get the date of last OpenVPN restart +last_openvpn_restart_date=$(ps awwwx -O lstart | grep openvpn | grep -vE "grep|check_openvpn_certificates.sh" | awk '{print $3,$4,$5,$6}') + test_cert_expiration() { # Already expired - Cert file if [ $current_date -ge $1 ]; then - CERT_ECHO="CRITICAL - The server certificate has expired on $formatted_cert_expiration_date" + CERT_ECHO="CRITICAL - The server certificate has expired on $formated_cert_expiration_date" CERT_STATE=$STATE_CRITICAL # Expiration in 15 days or less - Cert file elif [ $((current_date+_15_days)) -ge $1 ]; then - CERT_ECHO="CRITICAL - The server certificate expires in 15 days or less : $formatted_cert_expiration_date" + CERT_ECHO="CRITICAL - The server certificate expires in 15 days or less : $formated_cert_expiration_date" CERT_STATE=$STATE_CRITICAL # Expiration in 30 days or less - Cert file elif [ $((current_date+_30_days)) -ge $1 ]; then - CERT_ECHO="WARNING - The server certificate expires in 30 days or less : $formatted_cert_expiration_date" + CERT_ECHO="WARNING - The server certificate expires in 30 days or less : $formated_cert_expiration_date" CERT_STATE=$STATE_WARNING # Expiration in more than 30 days - Cert file else - CERT_ECHO="OK - The server certificate expires on $formatted_cert_expiration_date" + CERT_ECHO="OK - The server certificate expires on $formated_cert_expiration_date" CERT_STATE=$STATE_OK fi } @@ -67,74 +88,117 @@ test_cert_expiration() { test_ca_expiration() { # Already expired - CA file if [ $current_date -ge $1 ]; then - CA_ECHO="CRITICAL - The server CA has expired on $formatted_ca_expiration_date" + CA_ECHO="CRITICAL - The server CA has expired on $formated_ca_expiration_date" CA_STATE=$STATE_CRITICAL # Expiration in 15 days or less - CA file elif [ $((current_date+_15_days)) -ge $1 ]; then - CA_ECHO="CRITICAL - The server CA expires in 15 days or less : $formatted_ca_expiration_date" + CA_ECHO="CRITICAL - The server CA expires in 15 days or less : $formated_ca_expiration_date" CA_STATE=$STATE_CRITICAL # Expiration in 30 days or less - CA file elif [ $((current_date+_30_days)) -ge $1 ]; then - CA_ECHO="WARNING - The server CA expires in 30 days or less : $formatted_ca_expiration_date" + CA_ECHO="WARNING - The server CA expires in 30 days or less : $formated_ca_expiration_date" CA_STATE=$STATE_WARNING # Expiration in more than 30 days - CA file else - CA_ECHO="OK - The server CA expires on $formatted_ca_expiration_date" + CA_ECHO="OK - The server CA expires on $formated_ca_expiration_date" CA_STATE=$STATE_OK fi } -# Linux and BSD systems do not implement 'date' the same way -if [ "$SYSTEM" = "linux" ]; then +test_openvpn_restarted_since_last_ca_cert_modification() { + if [ $is_backup_not_running_openvpn -eq "0" ]; then + RESTART_ECHO="OK - OpenVPN is not running because server is backup" + RESTART_STATE=$STATE_OK + else + if [ $seconds_last_cert_modification_date -ge $1 ] || [ $seconds_last_ca_modification_date -ge $1 ]; then + RESTART_ECHO="CRITICAL - OpenVPN hasn't been restarted since the last renewal of CA or CERT certificate" + RESTART_STATE=$STATE_CRITICAL + else + RESTART_ECHO="OK - OpenVPN has been restarted since the last renewal of CA and CERT certificate" + RESTART_STATE=$STATE_OK + fi + fi +} - # Cert expiration date human formated then in seconds - formatted_cert_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$cert_expiration_date" +"%F %T %Z") - seconds_cert_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$cert_expiration_date" +"%s") +main() { + # Linux and BSD systems do not implement 'date' the same way + if [ "$SYSTEM" = "linux" ]; then + + # Cert expiration date human formated then in seconds + formated_cert_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$cert_expiration_date" +"%F %T %Z") + seconds_cert_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$cert_expiration_date" +"%s") + + # CA expiration date human formated then in seconds + formated_ca_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$ca_expiration_date" +"%F %T %Z") + seconds_ca_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$ca_expiration_date" +"%s") - # CA expiration date human formated then in seconds - formatted_ca_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$ca_expiration_date" +"%F %T %Z") - seconds_ca_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$ca_expiration_date" +"%s") + # Last OpenVPN restart in seconds + seconds_last_openvpn_restart_date=$(TZ="Europe/Paris" $date_cmd -d "$last_openvpn_restart_date" +%s) + + test_cert_expiration $seconds_cert_expiration_date + test_ca_expiration $seconds_ca_expiration_date + test_openvpn_restarted_since_last_ca_cert_modification $seconds_last_openvpn_restart_date + + elif [ "$SYSTEM" = "openbsd" ]; then - test_cert_expiration $seconds_cert_expiration_date - test_ca_expiration $seconds_ca_expiration_date + # Cert expiration date for POSIX date, human formated then in seconds + posix_cert_expiration_date=$(echo "$cert_expiration_date" | awk '{ printf $4" "(index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3" "$2" ",split($3,time,":"); print time[1],time[2],time[3]}' | awk '{printf "%04d%02d%02d%02d%02d.%02d\n", $1, $2, $3, $4, $5, $6}') + cert_zone=$(echo "$cert_expiration_date" | awk '{print $5}') + formated_cert_expiration_date=$(TZ=$cert_zone $date_cmd -j -z "Europe/Paris" "$posix_cert_expiration_date" +"%F %T %Z") + seconds_cert_expiration_date=$(TZ=$cert_zone $date_cmd -j -z "Europe/Paris" "$posix_cert_expiration_date" +"%s") + + # CA expiration date for POSIX date, human formated then in seconds + posix_ca_expiration_date=$(echo "$ca_expiration_date" | awk '{ printf $4" "(index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3" "$2" ",split($3,time,":"); print time[1],time[2],time[3]}' | awk '{printf "%04d%02d%02d%02d%02d.%02d\n", $1, $2, $3, $4, $5, $6}') + ca_zone=$(echo "$ca_expiration_date" | awk '{print $5}') + formated_ca_expiration_date=$(TZ=$ca_zone $date_cmd -j -z "Europe/Paris" "$posix_ca_expiration_date" +"%F %T %Z") + seconds_ca_expiration_date=$(TZ=$ca_zone $date_cmd -j -z "Europe/Paris" "$posix_ca_expiration_date" +"%s") -elif [ "$SYSTEM" = "openbsd" ]; then + test_cert_expiration $seconds_cert_expiration_date + test_ca_expiration $seconds_ca_expiration_date - # Cert expiration date for POSIX date, human formated then in seconds - posix_cert_expiration_date=$(echo "$cert_expiration_date" | awk '{ printf $4" "(index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3" "$2" ",split($3,time,":"); print time[1],time[2],time[3]}' | awk '{printf "%04d%02d%02d%02d%02d.%02d\n", $1, $2, $3, $4, $5, $6}') - cert_zone=$(echo "$cert_expiration_date" | awk '{print $5}') - formatted_cert_expiration_date=$(TZ=$cert_zone $date_cmd -j -z "Europe/Paris" "$posix_cert_expiration_date" +"%F %T %Z") - seconds_cert_expiration_date=$(TZ=$cert_zone $date_cmd -j -z "Europe/Paris" "$posix_cert_expiration_date" +"%s") + if [ $is_backup_not_running_openvpn -eq "0" ]; then + test_openvpn_restarted_since_last_ca_cert_modification 0 + else + # Last OpenVPN restart in POSIX format, then in seconds + posix_last_openvpn_restart_date=$(echo "$last_openvpn_restart_date" | awk '{ printf $4" "(index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3" "$2" ",split($3,time,":"); print time[1],time[2],time[3]}' | awk '{printf "%04d%02d%02d%02d%02d.%02d\n", $1, $2, $3, $4, $5, $6}') + seconds_last_openvpn_restart_date=$($date_cmd -j "$posix_last_openvpn_restart_date" +%s) - # CA expiration date for POSIX date, human formated then in seconds - posix_ca_expiration_date=$(echo "$ca_expiration_date" | awk '{ printf $4" "(index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3" "$2" ",split($3,time,":"); print time[1],time[2],time[3]}' | awk '{printf "%04d%02d%02d%02d%02d.%02d\n", $1, $2, $3, $4, $5, $6}') - ca_zone=$(echo "$ca_expiration_date" | awk '{print $5}') - formatted_ca_expiration_date=$(TZ=$ca_zone $date_cmd -j -z "Europe/Paris" "$posix_ca_expiration_date" +"%F %T %Z") - seconds_ca_expiration_date=$(TZ=$ca_zone $date_cmd -j -z "Europe/Paris" "$posix_ca_expiration_date" +"%s") + test_openvpn_restarted_since_last_ca_cert_modification $seconds_last_openvpn_restart_date + fi - test_cert_expiration $seconds_cert_expiration_date - test_ca_expiration $seconds_ca_expiration_date + # If neither Linux nor BSD + else + + echo "CRITICAL - OS not supported" + STATE=$STATE_CRITICAL + exit $STATE + + fi + + if [ $RESTART_STATE -gt $STATE_OK ]; then + echo $RESTART_ECHO + echo $CERT_ECHO + echo $CA_ECHO + exit $RESTART_STATE + else + # Display the first one that expires first + if [ $CA_STATE -gt $CERT_STATE ]; then + echo $CA_ECHO + echo $CERT_ECHO + echo $RESTART_ECHO + exit $CA_STATE + elif [ $CERT_STATE -gt $CA_STATE ]; then + echo $CERT_ECHO + echo $CA_ECHO + echo $RESTART_ECHO + exit $CERT_STATE + else + echo $CERT_ECHO + echo $CA_ECHO + echo $RESTART_ECHO + exit $CERT_STATE + fi + fi +} -# If neither Linux nor BSD -else - - echo "CRITICAL - OS not supported" - STATE=$STATE_CRITICAL - exit $STATE - -fi - -# Display the first one that expires first -if [ $CA_STATE -gt $CERT_STATE ]; then - echo $CA_ECHO - echo $CERT_ECHO - exit $CA_STATE -elif [ $CERT_STATE -gt $CA_STATE ]; then - echo $CERT_ECHO - echo $CA_ECHO - exit $CERT_STATE -else - echo $CERT_ECHO - echo $CA_ECHO - exit $CERT_STATE -fi +main