Title: Ban scanners IPs from OpenSMTP logs
       Author: Solène
       Date: 22 June 2023
       Tags: security opensmtpd openbsd pf
       Description: In this article you will find a script to parse OpenSMTP
       logs to ban IP which failed too many login attempts
       
       # Introduction
       
       If you are an OpenBSD running an OpenSMTP email server, you may want to
       ban IPs used by bots trying to bruteforce logins.  OpenBSD doesn't have
       fail2ban available in packages, and sshguard isn't extensible enough to
       support the multiline log format used by OpenSMTP.
       
       Here is a short script that looks for authentication failures in
       `/var/mail/maillog` and will add the IPs into the PF table `bot` after
       too many failed login.
       
       # Setup
       
       ## PF
       
       Add this rule to your PF configuration:
       
       ```pf
       block in quick on egress from <bot> to any
       ```
       
       This will block any connection from banned IPs, on all ports, not only
       smtp.  I see no reason to allow them to try other doors.
       
       ## Script
       
       Write the following content in an executable file, this could be
       `/usr/local/bin/ban_smtpd` but this doesn't really matter.
       
       ```
       #!/bin/sh
       
       TRIES=10
       EXPIRE_DAYS=5
       
       awk -v tries="$TRIES" '
               / smtp connected / {
                           ips[$6]=substr($9, 9)
               }
       
               / smtp authentication / && /result=permfail/ {
                           seen[ips[$6]]++
               }
       
               END {
                           for(ip in seen) {
                               if(seen[ip] > tries) {
                                           print ip
                                   }
                       }
               }' /var/log/maillog | xargs pfctl -T add -t bot
       
       # if the file exists, remove IPs listed there
       if [ -f /etc/mail/ignore.txt ]
       then
           cat /etc/mail/ignore.txt | xargs pfctl -T delete -t bot
       fi
       
       # remove IPs from the table after $EXPIRE_DAYS days
       pfctl -t bot -T expire "$(( 60 * 60 * 24 * $EXPIRE_DAYS ))"
       ```
       
       This parses the maillog file, so by default it has a rotation every
       day, you could adapt the script to your log rotation policy to match
       what you want, users failing with permfail are banned after some tries,
       configurable with `$TRIES`.
       
       I added support for an ignore list, to avoid blocking yourself out,
       just add IP addresses in `/etc/mail/ignore.txt`.
       
       Finally, banned IPs are unbanned after 5 days, you can change it using
       the variable EXPIRE_DAYS.
       
       ## Cronjob
       
       Now, edit root's crontab, you want to run this script at least every
       hour, and get a log if it fails.
       
       ```crontab
       ~ * * * * -sn /usr/local/bin/ban_smtpd
       ```
       
       This cron job will run every hour at a random minute (defined each time
       crond restarts, so it stays consistent for a while).  The periodicity
       may depend on the number of scan your email server receives and also
       the log size vs the CPU power.
       
       # Conclusion
       
       This would be better to have an integrated banning system supporting
       multiple logfiles / daemons, such as fail2ban, but in the current state
       it's not possible.  This script is simple, fast, extensible and does
       the job.