314 lines
10 KiB
Bash
Executable File
314 lines
10 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
: <<=cut
|
|
|
|
=head1 NAME
|
|
|
|
sshd_log - Munin plugin to monitor auth.log or journald for sshd
|
|
server events.
|
|
|
|
=head1 CONFIGURATION
|
|
|
|
This plugin requires read permission for the logfile or journald.
|
|
|
|
On busy servers you can change value type to COUNTER and set min to 0
|
|
to avoid minus peaks at logrotate.
|
|
|
|
The following environment variables are used by this plugin:
|
|
|
|
logfile - path to the auth log file, or "journald" to use journald.
|
|
default: /var/log/secure
|
|
|
|
journalctlargs - space separated list of arguments to pass to
|
|
journalctl to get the sshd logs.
|
|
default: _COMM=sshd
|
|
|
|
type - "GAUGE" or "DERIVE"
|
|
default: GAUGE
|
|
|
|
warning - defines a general warning level
|
|
default: UNSET (meaning not configured -> no warnings)
|
|
|
|
critical - defines a general critical level
|
|
default: UNSET (meaning not configured -> no criticals)
|
|
|
|
logpass_warning - defines a warning level for successful password logins
|
|
default: same value as "warning", so effectively UNSET (not
|
|
configured) if "warning" also is not configured
|
|
|
|
logpass_critical - defines a critical level for successful password logins
|
|
default: same value as "critical", so effectively UNSET (not
|
|
configured) if "critical" also is not configured
|
|
|
|
logpasspam_warning - same as "logpass_warning" but for successful PAM logins
|
|
|
|
logpasspam_critical - same as "logpass_critical" but for successful PAM logins
|
|
|
|
logkey_warning - same as "logpass_warning" but for successful PublicKey
|
|
logins
|
|
|
|
logkey_critical - same as "logpass_critical" but for successful PublicKey
|
|
logins
|
|
|
|
noid_warning - same as "logpass_warning" but for attempts with no user
|
|
identification
|
|
|
|
noid_critical - same as "logpass_critical" but for attempts with no user
|
|
identification
|
|
|
|
rootattempt_warning - same as "logpass_warning" but for root login attempts
|
|
|
|
rootattempt_critical - same as "logpass_critical" but for root login attempts
|
|
|
|
invusr_warning - same as "logpass_warning" but for invalid user login
|
|
attempts
|
|
|
|
invusr_critical - same as "logpass_critical" but for invalid user login
|
|
attempts
|
|
|
|
nordns_warning - same as "logpass_warning" but for connections with reverse
|
|
DNS for peer
|
|
|
|
nordns_critical - same as "logpass_critical" but for connections with reverse
|
|
DNS for peer
|
|
|
|
breakin_warning - same as "logpass_warning" but for potential breakin
|
|
attempts
|
|
|
|
breakin_critical - same as "logpass_critical" but for potential breakin
|
|
attempts
|
|
|
|
If the "logfile" environment variable is set to "journald" the sshd
|
|
logs are read from journald, filtering on program "sshd". The filtering
|
|
may be changed using "journalctlargs".
|
|
|
|
|
|
Config examples for /etc/munin/plugin-conf.d/munin-node:
|
|
|
|
[sshd_log]
|
|
user root
|
|
group root
|
|
env.logfile /var/log/messages
|
|
|
|
Config example with journald:
|
|
|
|
[sshd_log]
|
|
group systemd-journal
|
|
env.logfile journald
|
|
|
|
Config example with journald on the sshd.service unit only:
|
|
|
|
[sshd_log]
|
|
group systemd-journal
|
|
env.logfile journald
|
|
env.journalctlargs --unit=sshd.service
|
|
|
|
Config example with journald and type DERIVE:
|
|
|
|
[sshd_log]
|
|
group systemd-journal
|
|
env.logfile journald
|
|
env.type DERIVE
|
|
|
|
Config example setting general warning and critical values and specific ones for
|
|
root login attempts:
|
|
|
|
[sshd_log]
|
|
env.warning 100
|
|
env.critical 500
|
|
env.rootattempt_warning 1
|
|
env.rootattempt_critical 100
|
|
|
|
=head1 MAGIC MARKERS
|
|
|
|
#%# family=auto
|
|
#%# capabilities=autoconf
|
|
|
|
=head1 AUTHOR
|
|
|
|
Copyright (C) 2009 zlati
|
|
|
|
Copyright (C) 2009 ckujau
|
|
|
|
Copyright (C) 2010 pmoranga
|
|
|
|
Copyright (C) 2016 Thomas Riccardi
|
|
|
|
=cut
|
|
|
|
|
|
# Script parameters:
|
|
#
|
|
# config (required)
|
|
# autoconf (optional - used by munin-config)
|
|
|
|
|
|
LOG=${logfile:-/var/log/secure}
|
|
JOURNALCTL_ARGS=${journalctlargs:-_COMM=sshd}
|
|
TYPE=${type:-GAUGE}
|
|
|
|
WARNING=${warning:-UNSET}
|
|
CRITICAL=${critical:-UNSET}
|
|
LOGPASS_WARNING=${logpass_warning:-$WARNING}
|
|
LOGPASS_CRITICAL=${logpass_critical:-$CRITICAL}
|
|
LOGPASSPAM_WARNING=${logpasspam_warning:-$WARNING}
|
|
LOGPASSPAM_CRITICAL=${logpasspam_critical:-$CRITICAL}
|
|
LOGKEY_WARNING=${logkey_warning:-$WARNING}
|
|
LOGKEY_CRITICAL=${logkey_critical:-$CRITICAL}
|
|
NOID_WARNING=${noid_warning:-$WARNING}
|
|
NOID_CRITICAL=${noid_critical:-$CRITICAL}
|
|
ROOTATTEMPT_WARNING=${rootattempt_warning:-$WARNING}
|
|
ROOTATTEMPT_CRITICAL=${rootattempt_critical:-$CRITICAL}
|
|
INVUSR_WARNING=${invusr_warning:-$WARNING}
|
|
INVUSR_CRITICAL=${invusr_critical:-$CRITICAL}
|
|
NORDNS_WARNING=${nordns_warning:-$WARNING}
|
|
NORDNS_CRITICAL=${nordns_critical:-$CRITICAL}
|
|
BREAKIN_WARNING=${breakin_warning:-$WARNING}
|
|
BREAKIN_CRITICAL=${breakin_critical:-$CRITICAL}
|
|
|
|
if [ "$LOG" = "journald" -a "$TYPE" = "DERIVE" ]; then
|
|
TYPE=ABSOLUTE
|
|
fi
|
|
|
|
|
|
if [ "$1" = "autoconf" ]; then
|
|
if [ "$LOG" = "journald" ]; then
|
|
# shellcheck disable=SC2086,SC2034
|
|
if journalctl --no-pager --quiet --lines=1 $JOURNALCTL_ARGS | read -r DUMMY; then
|
|
echo "yes"
|
|
else
|
|
echo "no (journald empty log for '$JOURNALCTL_ARGS' not found)"
|
|
fi
|
|
else
|
|
if [ -r "$LOG" ]; then
|
|
echo "yes"
|
|
else
|
|
echo "no (logfile '$LOG' not readable)"
|
|
fi
|
|
fi
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$1" = "config" ]; then
|
|
echo 'graph_title SSHD login stats'
|
|
echo 'graph_info SSHD login stats from' "$LOG"
|
|
echo 'graph_args --base 1000 -l 0'
|
|
echo 'graph_vlabel logins'
|
|
echo 'graph_category' security
|
|
|
|
echo 'LogPass.label Successful password logins'
|
|
echo 'LogPass.min 0'
|
|
echo 'LogPass.type' "$TYPE"
|
|
if [ "$LOGPASS_WARNING" != "UNSET" ]; then
|
|
echo 'LogPass.warning' "$LOGPASS_WARNING"
|
|
fi
|
|
if [ "$LOGPASS_CRITICAL" != "UNSET" ]; then
|
|
echo 'LogPass.critical' "$LOGPASS_CRITICAL"
|
|
fi
|
|
|
|
echo 'LogPassPAM.label Successful login via PAM'
|
|
echo 'LogPassPAM.min 0'
|
|
echo 'LogPassPAM.type' "$TYPE"
|
|
if [ "$LOGPASSPAM_WARNING" != "UNSET" ]; then
|
|
echo 'LogPassPAM.warning' "$LOGPASSPAM_WARNING"
|
|
fi
|
|
if [ "$LOGPASSPAM_CRITICAL" != "UNSET" ]; then
|
|
echo 'LogPassPAM.critical' "$LOGPASSPAM_CRITICAL"
|
|
fi
|
|
|
|
echo 'LogKey.label Successful PublicKey logins'
|
|
echo 'LogKey.min 0'
|
|
echo 'LogKey.type' "$TYPE"
|
|
if [ "$LOGKEY_WARNING" != "UNSET" ]; then
|
|
echo 'LogKey.warning' "$LOGKEY_WARNING"
|
|
fi
|
|
if [ "$LOGKEY_CRITICAL" != "UNSET" ]; then
|
|
echo 'LogKey.critical' "$LOGKEY_CRITICAL"
|
|
fi
|
|
|
|
echo 'NoID.label No identification from user'
|
|
echo 'NoID.min 0'
|
|
echo 'NoID.type' "$TYPE"
|
|
if [ "$NOID_WARNING" != "UNSET" ]; then
|
|
echo 'NoID.warning' "$NOID_WARNING"
|
|
fi
|
|
if [ "$NOID_CRITICAL" != "UNSET" ]; then
|
|
echo 'NoID.critical' "$NOID_CRITICAL"
|
|
fi
|
|
|
|
echo 'rootAttempt.label Root login attempts'
|
|
echo 'rootAttempt.min 0'
|
|
echo 'rootAttempt.type' "$TYPE"
|
|
if [ "$ROOTATTEMPT_WARNING" != "UNSET" ]; then
|
|
echo 'rootAttempt.warning' "$ROOTATTEMPT_WARNING"
|
|
fi
|
|
if [ "$ROOTATTEMPT_CRITICAL" != "UNSET" ]; then
|
|
echo 'rootAttempt.critical' "$ROOTATTEMPT_CRITICAL"
|
|
fi
|
|
|
|
echo 'InvUsr.label Invalid user login attempts'
|
|
echo 'InvUsr.min 0'
|
|
echo 'InvUsr.type' "$TYPE"
|
|
if [ "$INVUSR_WARNING" != "UNSET" ]; then
|
|
echo 'InvUsr.warning' "$INVUSR_WARNING"
|
|
fi
|
|
if [ "$INVUSR_CRITICAL" != "UNSET" ]; then
|
|
echo 'InvUsr.critical' "$INVUSR_CRITICAL"
|
|
fi
|
|
|
|
echo 'NoRDNS.label No reverse DNS for peer'
|
|
echo 'NoRDNS.min 0'
|
|
echo 'NoRDNS.type' "$TYPE"
|
|
if [ "$NORDNS_WARNING" != "UNSET" ]; then
|
|
echo 'NoRDNS.warning' "$NORDNS_WARNING"
|
|
fi
|
|
if [ "$NORDNS_CRITICAL" != "UNSET" ]; then
|
|
echo 'NoRDNS.critical' "$NORDNS_CRITICAL"
|
|
fi
|
|
|
|
echo 'Breakin.label Potential Breakin Attempts'
|
|
echo 'Breakin.min 0'
|
|
echo 'Breakin.type' "$TYPE"
|
|
if [ "$BREAKIN_WARNING" != "UNSET" ]; then
|
|
echo 'Breakin.warning' "$BREAKIN_WARNING"
|
|
fi
|
|
if [ "$BREAKIN_CRITICAL" != "UNSET" ]; then
|
|
echo 'Breakin.critical' "$BREAKIN_CRITICAL"
|
|
fi
|
|
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$LOG" = "journald" -a "$TYPE" = "ABSOLUTE" ]; then
|
|
CURSOR_FILE="$MUNIN_STATEFILE"
|
|
# read cursor
|
|
# format: "journald-cursor <cursor>"
|
|
CURSOR=
|
|
if [ -f "$CURSOR_FILE" ]; then
|
|
CURSOR=$(awk '/^journald-cursor / {print $2}' "$CURSOR_FILE")
|
|
fi
|
|
else
|
|
CURSOR_FILE=
|
|
fi
|
|
|
|
if [ "$LOG" = "journald" ]; then
|
|
# shellcheck disable=SC2086
|
|
if [ "$TYPE" = "ABSOLUTE" ]; then
|
|
journalctl --no-pager --quiet --show-cursor ${CURSOR:+"--after-cursor=$CURSOR"} $JOURNALCTL_ARGS
|
|
else
|
|
journalctl --no-pager --quiet --since=$(date -dlast-sunday +%Y-%m-%d) $JOURNALCTL_ARGS
|
|
fi
|
|
else
|
|
cat "$LOG"
|
|
fi | \
|
|
awk -v cursor_file="$CURSOR_FILE" 'BEGIN{c["LogPass"]=0;c["LogKey"]=0;c["NoID"]=0;c["rootAttempt"]=0;c["InvUsr"]=0;c["LogPassPAM"]=0;c["Breakin"]=0;c["NoRDNS"]=0; }
|
|
/sshd\[.*Accepted password for/{c["LogPass"]++}
|
|
/sshd\[.*Accepted publickey for/{c["LogKey"]++}
|
|
/sshd\[.*Did not receive identification string/{c["NoID"]++}
|
|
/sshd\[.*Failed password for root/{c["rootAttempt"]++}
|
|
/sshd\[.*Invalid user/{c["InvUsr"]++}
|
|
/sshd\[.*POSSIBLE BREAK-IN ATTEMPT!/{c["Breakin"]++}
|
|
/sshd\[.*keyboard-interactive\/pam/{c["LogPassPAM"]++}
|
|
/sshd\[.*reverse mapping checking getaddrinfo/{c["NoRDNS"]++}a
|
|
END{if (cursor_file != "") { print "journald-cursor " $3 > cursor_file };for(i in c){print i".value " c[i]} }'
|