# ban2fail (C) 2019 John D. Robertson **ban2fail** is a simple and efficient tool to coordinate log file scanning and iptables filtering. As the name implies, *ban2fail* was inspired by the popular *fail2ban* project (http://fail2ban.org). *ban2fail* started with a few hours of frenzied C hacking after my mail server was exploited to deliver spam for others who had cracked a user's SMTP send password. After inspecting the log files I realized that crackers are now using widely distributed attacks, and that I would need an extremely efficient tool that could run in a fraction of a second on my rather modest Linode virtual server to have a chance of stopping them. Here are the timing results for a typical scan on my wimpy server: ``` real 0m0.269s user 0m0.108s sys 0m0.134s ``` Currently I am running *ban2fail* from a *systemd* service file which triggers *ban2fail* whenever a watched log file is modified. This gives attackers at most a 0.4 second window to do their worst. I hope you find this code useful. ## Configuration *ban2fail* works from a configuration file found at "/etc/ban2fail/ban2fail.cfg". The overarching premise is that if any REGEX appearing in a LOGTYPE clause matches a line in an associated log file, then by default that IP will be blocked. ``` LOGTYPE auth { DIR= /var/log PREFIX= auth.log # imapd[20193]= Login failed user=redacted auth=redacted@nowhere.com host=[186.179.170.12] REGEX= imapd.*Login failed.*\[([0-9.a-f:]+)\]$ # sshd[6165]= Failed password for invalid user user from 185.224.137.201 port 44865 ssh2 REGEX= sshd.*Failed password.*from ([0-9.a-f:]+) port [0-9]+ ssh2$ # Unable to negotiate with 193.188.22.188 port ... # REGEX= Unable to negotiate with ([0-9.]+) port REGEX= Unable to negotiate with ([0-9.a-f:]+) port } ``` Syntax in the config file is pretty much the same as the nftables syntax. All keywords must be in upper case. Any values in the key=value pairs have whitespace stripped from the beginning and end of the line. Since there is little escaping of characters going on, regular expressions are mostly WYSIWYG. If you have a hash symbol '#' in your pattern (which is the comment character for the config file parser), you will need to escape it like so: ``` # Nov 27 02:03:03 srv named[764]: client @0x7fe6a0053420 1.192.90.183#27388 (www.ipplus360.com): query (cache) 'www.ipplus360.com/A/IN' denied REGEX= named.*client.* ([0-9.a-f:]+)\#.*denied$ ``` Finding typos and so forth in the config file is easy; use the -v command flag to print all unrecognized content (besides comments). `ban2fail -v` The only way to alter the default blocking behavior is with a MAX\_OFFENSES clause. This clause allows you specify how many offenses are tolerated before an IP is blocked. Offenses will naturally disappear as old logfiles are deleted by *logrotate*. ``` # Take it easy on home boys MAX_OFFENSES 5 { COUNTRY= US } # GeoIP doesn't know the location of every IP address MAX_OFFENSES 3 { COUNTRY= unknown } # This is your whitelist: -1 means no limit. MAX_OFFENSES -1 { # me from home IP= 205.144.171.37/20 # Some user IP= 173.236.196.36 } ``` If you recieve a complaint about an address unjustly getting blocked, place it in one of the MAX\_OFFENSES blocks, and the IP will be unblocked the next time *ban2fail* runs in production mode. ## Working with *ban2fail* There are two primary modes in which *ban2fail* is used: * Production mode, where iptables rules are modified. * Testing mode, where modifications to blocking rules are merely indicated. ### Production In production mode it is expected that *ban2fail* is running from a cron job, and no output is printed unless addresses are (un)blocked. It is also possible to generate a listing of addresses, offense counts, and status with the -a command flag. Likewise, a listing of countries and offense counts is available with the -c flag. ### Testing In test mode (-t flag) the presumption is that you are testing a modified configuration which is not yet in place, and that you don't want to disturb the production setup. This is how you might do that: `ban2fail -t myNew.cfg -a` No iptables rules will be modified. You will be shown in the listing which addresses would be (un)blocked if the contents of "myNew.cfg" was in place, and *ban2fail* was running in production mode. When you are happy with the new configuration, copy it into place, and the the iptable rule changes will be realized the next time *ban2fail* runs in production mode. ## Building the Project I've tested *ban2fail* only on Debian Buster, but it should compile on just about any modern Linux distro. It uses the following libraries: + *libcrypto* from the libssl package, for md5 checksums + *libgeoip* to identify the country of origin for IP addresses + *libz* to read compressed log files Build and install like so: ``` make release sudo make install ``` The executable will be placed in "/usr/local/bin".