#!/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

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

=head1 MAGIC MARKERS

  #%# family=auto
  #%# capabilities=autoconf

=head1 AUTHOR

Revision 2.0  2016/11/11 15:42:00  Thomas Riccardi
Revision 1.2  2010/03/19 15:03:00  pmoranga
Revision 1.1  2009/04/26 23:28:00  ckujau
Revision 1.0  2009/04/22 22:00:00  zlati

=cut


# Script parameters:
#
#       config   (required)
#       autoconf (optional - used by munin-config)


LOG=${logfile:-/var/log/secure}
JOURNALCTL_ARGS=${journalctlargs:-_COMM=sshd}
TYPE=${type:-GAUGE}
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 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"

        echo 'LogPassPAM.label Successful login via PAM'
        echo 'LogPassPAM.min 0'
        echo 'LogPassPAM.type' "$TYPE"

        echo 'LogKey.label Successful PublicKey logins'
        echo 'LogKey.min 0'
        echo 'LogKey.type' "$TYPE"

        echo 'NoID.label No identification from user'
        echo 'NoID.min 0'
        echo 'NoID.type' "$TYPE"

        echo 'rootAttempt.label Root login attempts'
        echo 'rootAttempt.min 0'
        echo 'rootAttempt.type' "$TYPE"

        echo 'InvUsr.label Invalid user login attempts'
        echo 'InvUsr.min 0'
        echo 'InvUsr.type' "$TYPE"

        echo 'NoRDNS.label No reverse DNS for peer'
        echo 'NoRDNS.min 0'
        echo 'NoRDNS.type' "$TYPE"

        echo 'Breakin.label Potential Breakin Attempts'
        echo 'Breakin.min 0'
        echo 'Breakin.type' "$TYPE"

        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]} }'
