Dynamic pf blocking of attempted logins

Hackers are constantly trying to break in. As a rule, most (all?) of these attempts are logged in /var/log/auth.log, so there's a good way to detect them and block them with a shell script.

#!    /usr/local/bin/fish

#    auth_block.sh
#    reads auth.log, looks for "Authenticatian error lines" or "Invalid user", then adds those IPs to pfctl blacklist
#    Robb Allan, 4 February 2021

set LOGFILE /var/log/auth_block.log
set BLACKLIST_FILE /usr/local/etc/pf/blacklist
set TMP_FILE /tmp/hack.txt
set TMP_TMP /tmp/hack2.txt

if test (count $argv) -eq 1
    switch $argv

        case "--review"
            /bin/rm $TMP_FILE 2>> /dev/null

            /usr/bin/bzgrep -e "error: PAM: Authentication error for illegal user" /var/log/auth.log* | /usr/bin/grep -ve "COMMAND=" | sed -nEe 's/.* ([.0-9]{7,15})$/\1/p' > $TMP_FILE
            /usr/bin/bzgrep -e "[Ii]nvalid user" /var/log/auth.log* | grep -ve "COMMAND=" | sed -nEe 's/.* ([.0-9]{7,15}) port.*/\1/p' >> $TMP_FILE

            /usr/bin/sort -n $TMP_FILE | /usr/bin/uniq > $TMP_TMP
            /bin/cat $TMP_TMP
            /bin/rm $TMP_FILE $TMP_TMP

        case "--refresh"
            /bin/echo (/bin/date "+%b %e %T") "running auth_block in refresh mode..." >> $LOGFILE
            /bin/echo -n (/bin/date "+%b %e %T") "replacing blacklist..." >> $LOGFILE
            /sbin/pfctl -t blacklist -T replace -f /usr/local/etc/pf/blacklist >> $LOGFILE 2>> $LOGFILE 

            /bin/echo -n (/bin/date "+%b %e %T") "replacing whitelist..." >> $LOGFILE
            /sbin/pfctl -t whitelist -T replace -f /usr/local/etc/pf/whitelist >> $LOGFILE 2>> $LOGFILE 
            
            /sbin/pfctl -f /etc/pf.conf 

            /bin/echo (/bin/date "+%b %e %T") "done."\n >> $LOGFILE

        case "--update"
            /bin/echo (/bin/date "+%b %e %T") "running auth_block in update mode..." >> $LOGFILE

            /bin/rm $TMP_FILE 2>> /dev/null

            /usr/bin/bzgrep -e "error: PAM: Authentication error for illegal user" /var/log/auth.log* | /usr/bin/grep -ve "COMMAND=" | sed -nEe 's/.* ([.0-9]{7,15})$/\1/p' > $TMP_FILE
            /usr/bin/bzgrep -e "[Ii]nvalid user" /var/log/auth.log* | grep -ve "COMMAND=" | sed -nEe 's/.* ([.0-9]{7,15}) port.*/\1/p' >> $TMP_FILE

            if test -e $TMP_FILE
                if test -e $BLACKLIST_FILE
                    /bin/cat $BLACKLIST_FILE >> $TMP_FILE
                end

                /usr/bin/sort -n $TMP_FILE | /usr/bin/uniq > $TMP_TMP
                
                cmp $TMP_TMP $BLACKLIST_FILE >> /dev/null
                if test $status -eq 1
                    /bin/cp $BLACKLIST_FILE $BLACKLIST_FILE.(date -j "+%Y%m%d.%H%M")
                    /bin/echo (diff -C 0 $BLACKLIST_FILE $TMP_TMP)| /usr/bin/mail -s "blacklist IPs changed" <my email address>

                    /bin/cat $TMP_TMP > $BLACKLIST_FILE
                                        
                    /bin/chmod a+w $BLACKLIST_FILE

                    /bin/echo (/bin/date "+%b %e %T") "blacklist file rebuilt..." >> $LOGFILE

                    /bin/echo -n (/bin/date "+%b %e %T") "replacing blacklist..." >> $LOGFILE
                    /sbin/pfctl -t blacklist -T replace -f /usr/local/etc/pf/blacklist >> $LOGFILE 2>> $LOGFILE 

                    /bin/echo -n (/bin/date "+%b %e %T") "replacing whitelist..." >> $LOGFILE
                    /sbin/pfctl -t whitelist -T replace -f /usr/local/etc/pf/whitelist >> $LOGFILE 2>> $LOGFILE 
                    
                    /sbin/pfctl -f /etc/pf.conf 

                    /bin/echo (tail -5 /var/log/auth_block.log) | /usr/bin/mail -s "blacklist updated" <my email address>

                else
                    /bin/echo (/bin/date "+%b %e %T") "nothing to change." >> $LOGFILE
#                     /bin/echo "nada" (tail -3 /var/log/auth_block.log) | /usr/bin/mail -s "blacklist NOT updated" <my email address>
            
                end

                /bin/rm $TMP_FILE $TMP_TMP
                /bin/echo (/bin/date "+%b %e %T") "done."\n >> $LOGFILE                

            end
            
        
        case '*'
            /bin/echo "unknown argument(s): " $argv

    end

else 
      /bin/echo "auth_block requires an argument."
      /bin/echo "Usage: auth_block [--review (list IPs to screen)| --refresh (reload pf)| --update (scan logs and rebuild blacklist)]"
end

Run this script with one of the following arguments:

  • "--review": reads the logfile, looks for invalid login attempts, collects the IPs, uniq's and sort's them, and prints them to stdout.
  • "--refresh": reloads the blacklist and whitelist files in /usr/local/pf, and resets the pf packet-filtering daemon.
  • "--update": backs up the original /usr/local/pf/blacklist file, reads the logfile, looks for invalid login attempts, collects the IPs, merges them with the blacklist file, uniq's and sort's them, and tests for any change. If so, writes a new blacklist file, reloads it to pf, resets pf, and sends a notification email.