#! /bin/zsh # MIT License # # Copyright (c) 2015-2019 Josef Friedrich # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ## # Default check values # # In this section you can set your default check options by uncommenting some OPT_* variables ## # Check if the configuration 'APT::Periodic::AutocleanInterval' is set # properly. #OPT_AUTOCLEAN=7 # Check if the package 'anacron' is installed. #OPT_ANACRON=1 # Time interval since the last execution to result in a critical state # (seconds). #OPT_CRITICAL=187200 # Check if the configuration # 'APT::Periodic:Download-Upgradeable-Packages' is set properly. #OPT_DOWNLOAD=1 # Check if the configuration 'APT::Periodic::Enable' is set properly. #OPT_ENABLE=1 # Check if the configuration 'APT::Periodic::Update-Package-Lists' is # set properly. #OPT_LISTS=1 # Check if the configuration 'Unattended-Upgrade::Mail' is set properly. #OPT_MAIL="you@example.com" # Check if 'unattended-upgrades --dry-run' is working. #OPT_DRY_RUN=1 # Check if 'Unattended-upgrades' is configured to include specific repo. #OPT_CUSTOM_REPO="your-repo" # Check if the configuration # 'Unattended-Upgrade::Remove-Unused-Dependencies' is set properly. #OPT_REMOVE="true" # Check if the machine needs a reboot. #OPT_REBOOT=1 # Check if 'Unattended-upgrades' is configured to handle security # updates. #OPT_SECURITY=1 # Check if the configuration 'APT::Periodic::RandomSleep' is set # properly. #OPT_SLEEP=0 # Check if the appropriate Systemd timers are enabled ( apt-daily-upgrade.timer, apt-daily.timer ). #OPT_SYSTEMD_TIMERS=1 # Check if the configuration 'APT::Periodic::Unattended-Upgrade' is set # properly. #OPT_UNATTENDED=1 # Time interval since the last execution to result in a warning state # (seconds). #OPT_WARNING=93600 FIRST_RELEASE=2015-04-25 VERSION=1.4 PROJECT_PAGES='https://github.com/Josef-Friedrich/check_unattended_upgrades https://exchange.icinga.com/joseffriedrich/check_unattended_upgrades https://exchange.nagios.org/directory/Plugins/Software/check_unattended_upgrades/details' SHORT_DESCRIPTION="Monitoring plugin to check automatic updates \ (unattended-upgrades) on Debian / Ubuntu." USAGE="check_unattended_upgrades v$VERSION Copyright (c) $(date +%Y --date=$FIRST_RELEASE)-$(date +%Y) Josef Friedrich $SHORT_DESCRIPTION Usage: check_unattended_upgrades Options: -A, --anacron Check if the package 'anacron' is installed. -a, --autoclean Check if the configuration 'APT::Periodic::AutocleanInterval' is set properly. -c, --critical Time interval since the last execution to result in a critical state (seconds). -D, --short-description Show a short description of this check plugin. -d, --download Check if the configuration 'APT::Periodic:Download-Upgradeable-Packages' is set properly. -e, --enable Check if the configuration 'APT::Periodic::Enable' is set properly. -h, --help Show this help message. -l, --lists Check if the configuration 'APT::Periodic::Update-Package-Lists' is set properly. -m, --mail Check if the configuration 'Unattended-Upgrade::Mail' is set properly. -n, --dry-run Check if 'unattended-upgrades --dry-run' is working. Warning: If you use this option the performance data last_ago is always 0 or near to 0. -p, --repo Check if 'Unattended-upgrades' is configured to include the specified custom repository. -R, --reboot Check if the machine needs a reboot. -r, --remove Check if the configuration 'Unattended-Upgrade::Remove-Unused- Dependencies' is set properly. -S, --security Check if 'Unattended-upgrades' is configured to handle security updates. -s, --sleep Check if the configuration 'APT::Periodic::RandomSleep' is set properly. -t, --systemd-timers Check if the appropriate Systemd timers are enabled ( apt-daily-upgrade.timer, apt-daily.timer ). -u, --unattended Check if the configuration 'APT::Periodic::Unattended-Upgrade' is set properly. -v, --version Show the version number. -w, --warning Time interval since the last execution to result in a warning state (seconds). Performance data: - last_ago Time interval in seconds for last unattended-upgrades execution. - warning Interval in seconds. - critical Interval in seconds. About file system permissions: The user which executes this plugin must have read permissions to this log file: /var/log/unattended-upgrades/unattended-upgrades.log To allow every user on your system to read the mentioned log file this permissions are recommended: 751 (drwxr-x--x) /var/log/unattended-upgrades 644 (-rw-r--r--) /var/log/unattended-upgrades/unattended-upgrades.log " # Exit codes STATE_OK=0 STATE_WARNING=1 STATE_CRITICAL=2 STATE_UNKNOWN=3 _getopts() { while getopts ":Aa:c:Dd:e:hl:m:np:Rr:Ss:tu:vw:-:" OPT; do case $OPT in A) OPT_ANACRON=1 ;; a) OPT_AUTOCLEAN="$OPTARG" ;; c) OPT_CRITICAL="$OPTARG" ;; D) echo "$SHORT_DESCRIPTION" ; exit 0 ;; d) OPT_DOWNLOAD="$OPTARG" ;; e) OPT_ENABLE="$OPTARG" ;; h) echo "$USAGE" ; exit 0 ;; l) OPT_LISTS="$OPTARG" ;; m) OPT_MAIL="$OPTARG" ;; n) OPT_DRY_RUN=1 ;; p) OPT_CUSTOM_REPO="$OPTARG" ;; R) OPT_REBOOT=1 ;; r) OPT_REMOVE="$OPTARG" ;; S) OPT_SECURITY=1 ;; s) OPT_SLEEP="$OPTARG" ;; t) OPT_SYSTEMD_TIMERS=1 ;; u) OPT_UNATTENDED="$OPTARG" ;; v) echo "$VERSION" ; exit 0 ;; w) OPT_WARNING="$OPTARG" ;; \?) echo "Invalid option '-$OPTARG'!" >&2 ; exit 2 ;; :) echo "Option '-$OPTARG' requires an argument!" >&2 ; exit 3 ;; -) LONG_OPTARG="${OPTARG#*=}" case $OPTARG in anacron) OPT_ANACRON=1 ;; autoclean=?*) OPT_AUTOCLEAN="$LONG_OPTARG" ;; critical=?*) OPT_CRITICAL="$LONG_OPTARG" ;; short-description) echo "$SHORT_DESCRIPTION" ; exit 0 ;; download=?*) OPT_DOWNLOAD="$LONG_OPTARG" ;; dry-run) OPT_DRY_RUN=1 ;; enable=?*) OPT_ENABLE="$LONG_OPTARG" ;; help) echo "$USAGE" ; exit 0 ;; lists=?*) OPT_LISTS="$LONG_OPTARG" ;; mail=?*) OPT_MAIL="$LONG_OPTARG" ;; repo=?*) OPT_CUSTOM_REPO="$LONG_OPTARG" ;; reboot) OPT_REBOOT=1 ;; remove=?*) OPT_REMOVE="$LONG_OPTARG" ;; security) OPT_SECURITY=1 ;; systemd-timers) OPT_SYSTEMD_TIMERS=1 ;; sleep=?*) OPT_SLEEP="$LONG_OPTARG" ;; unattended=?*) OPT_UNATTENDED="$LONG_OPTARG" ;; version) echo "$VERSION" ; exit 0 ;; warning=?*) OPT_WARNING="$LONG_OPTARG" ;; autoclean*|critical*|download*|enable*|lists*|mail*|repo*|remove*|sleep*|unattended*|warning*) echo "Option '--$OPTARG' requires an argument!" >&2 exit 3 ;; anacron*|short-description*|dry-run*|help*|reboot*|security*|systemd-timers*|version*) echo "No argument allowed for the option '--$OPTARG'!" >&2 exit 4 ;; '') break ;; # "--" terminates argument processing *) echo "Invalid option '--$OPTARG'!" >&2 ; exit 2 ;; esac ;; esac done } _get_config() { eval $(apt-config shell "$2" "$1") } _check_config() { local CONFIG=$1 local DEBIT=$2 _get_config $CONFIG "CREDIT" if [ "$DEBIT" != "$CREDIT" ]; then echo "CRITICAL - The configuration '$CONFIG' is not \ configured properly. It has the value '$CREDIT', \ but it should have the value '$DEBIT'." exit $STATE_CRITICAL fi } _check_systemd_timer() { local TIMER=$1 if test -d /run/systemd/system ; then if ! systemctl is-enabled $TIMER 1>/dev/null 2>/dev/null; then echo "CRITICAL - Systemd timer $TIMER is not enabled." exit $STATE_CRITICAL fi fi } _performance_data() { echo "| \ last_ago=$DIFF \ warning=$OPT_WARNING \ critical=$OPT_CRITICAL" } ## This SEPARATOR is required for test purposes. Please don’t remove! ## _getopts $@ # 26h = 1d + 2h [ -z "$OPT_WARNING" ] && OPT_WARNING=93600 # 52h = 2d + 4h [ -z "$OPT_CRITICAL" ] && OPT_CRITICAL=187200 if [ -n "$OPT_DRY_RUN" ]; then unattended-upgrades --dry-run > /dev/null 2>&1 DRY_RUN=$? if [ "$DRY_RUN" -ne 0 ]; then echo 'CRITICAL - unattended-upgrades --dry-run exits with a non-zero status.' exit $STATE_CRITICAL fi fi ## # Check for auxiliary packages. ## # -A if [ -n "$OPT_ANACRON" ] && ! test -x /usr/sbin/anacron ; then echo "CRITICAL - Package 'anacron' is not installed." exit $STATE_CRITICAL fi ## # Configuration checks ## if which apt-config > /dev/null 2>&1; then # -e if [ -n "$OPT_ENABLE" ]; then _check_config "APT::Periodic::Enable" $OPT_ENABLE fi # -u if [ -n "$OPT_UNATTENDED" ]; then _check_config "APT::Periodic::Unattended-Upgrade" $OPT_UNATTENDED fi # -m if [ -n "$OPT_MAIL" ]; then _check_config "Unattended-Upgrade::Mail" $OPT_MAIL fi # -d if [ -n "$OPT_DOWNLOAD" ]; then _check_config "APT::Periodic::Download-Upgradeable-Packages" $OPT_DOWNLOAD fi # -r if [ -n "$OPT_REMOVE" ]; then _check_config "Unattended-Upgrade::Remove-Unused-Dependencies" $OPT_REMOVE fi # -s if [ -n "$OPT_SLEEP" ]; then _check_config "APT::Periodic::RandomSleep" $OPT_SLEEP fi # -l if [ -n "$OPT_LISTS" ]; then _check_config "APT::Periodic::Update-Package-Lists" $OPT_LISTS fi # -a if [ -n "$OPT_AUTOCLEAN" ]; then _check_config "APT::Periodic::AutocleanInterval" $OPT_AUTOCLEAN fi if [ -n "$OPT_SECURITY" ]; then if ! apt-config dump \ | grep 'Unattended-Upgrade::Allowed-Origins\|Unattended-Upgrade::Origins-Pattern' \ | grep -i 'security' > /dev/null ; then echo "CRITICAL - Unattended-upgrades is not \ configured to handle security updates." exit $STATE_CRITICAL fi fi if [ -n "$OPT_CUSTOM_REPO" ]; then if ! apt-config dump \ | grep 'Unattended-Upgrade::Allowed-Origins\|Unattended-Upgrade::Origins-Pattern' \ | grep -i "${OPT_CUSTOM_REPO}" > /dev/null ; then echo "CRITICAL - Unattended-upgrades is not \ configured to handle updates for custom repository '${OPT_CUSTOM_REPO}'." exit $STATE_CRITICAL fi fi if [ -n "$OPT_SYSTEMD_TIMERS" ]; then _check_systemd_timer apt-daily.timer _check_systemd_timer apt-daily-upgrade.timer fi fi ## # Reboot ## if [ -n "$OPT_REBOOT" ] && test -f /var/run/reboot-required ; then echo 'WARNING - Machine requires a reboot.' exit $STATE_WARNING fi ## # Time checks ## # Log file check LOG_FILE='/var/log/unattended-upgrades/unattended-upgrades.log' if ! test -f $LOG_FILE ; then echo "CRITICAL - The log file ($LOG_FILE) doesn't exist or you haven't sufficient read permissions." exit $STATE_CRITICAL fi LOG_TEXT="$(cat $LOG_FILE)" if [ -z "$LOG_TEXT" ]; then LATEST_COMPRESSED_LOG=$(ls -r ${LOG_FILE}.*.gz | tail -n 1) LOG_TEXT="$(zcat $LATEST_COMPRESSED_LOG)" fi while IFS= read -r line; do if [ ! -z "$(echo "$line" | grep -E ' (WARNING|ERROR) ')" ]; then # added -s check because it returns error on an empty file if [ ! -z "$(echo "$line" | grep ' ERROR ')" ]; then printf "CRITICAL - In the log file is an ERROR message.\n${line}" exit $STATE_CRITICAL fi # added -s check because it returns error on an empty file if [ ! -z "$(echo "$line" | grep ' WARNING ')" ]; then printf "WARNING - In the log file is a WARNING message.\n${line}" exit $STATE_WARNING fi fi done < <(printf $LOG_TEXT | tail -n "+$(echo $LOG_TEXT | grep -n 'Starting unattended upgrades script' | tail -n1 | cut -d: -f1)") LAST_LOG_LINE=$(echo "$LOG_TEXT" | grep -v ' WARNING ' | grep -v ' ERROR ' | tail -n 1) LAST_RUN_DATE=$(echo $LAST_LOG_LINE | cut -d "," -f 1) if [ -z "$LAST_RUN_DATE" ]; then echo "CRITICAL - The date on which the command \ 'unattended-upgrades' ran for the last time, could not be determined. \ The log file '$LOG_FILE' is empty." exit $STATE_CRITICAL fi LAST_RUN=$(date +%s -d "$LAST_RUN_DATE") CURRENT=$(date +%s) DIFF=$((CURRENT - LAST_RUN)) MESSAGE="- The last execution of 'unattended-upgrades' was at \ $LAST_RUN_DATE. $(_performance_data)" # Critical if [ "$DIFF" -ge "$OPT_CRITICAL" ]; then echo "CRITICAL $MESSAGE" exit $STATE_CRITICAL # Warning elif [ "$DIFF" -ge "$OPT_WARNING" ]; then echo "WARNING $MESSAGE" exit $STATE_WARNING # ok elif [ "$DIFF" -lt "$OPT_WARNING" ]; then echo "OK $MESSAGE" exit $STATE_OK else echo "UNKOWN $MESSAGE" exit $STATE_UNKNOWN fi