Find and ban recidive subnets using fail2ban.
Fail2ban is perfect to ban single hosts that cause authentication errors or try some other bad stuff on your server. But watching the fail2ban.log I discovered that some attackers use ip ranges. Some ip's are found by fail2ban but not banned, because the attack is distributed from a complete subnet. Fail2ban by now is not able to discover and ban those subnets. There is an issue for that #927 and a python solution fail2ban-subnet. But both did't satisfy my needs, so this little project is born.
There is a shell script, that starts an awk script which scans the fail2ban.log file. The shell script should be started on a regular basis (e.g. cronjob). Every ip where fail2ban said "Found" is remembered by the awk script. If the /24 subnet of that ip has too many "Founds", then the subnet is written in the fail2ban-subnet.log file. Now fail2ban itself has a jail and filter configuration to watch this log file and will ban and unban the subnet according to your configuration.
- fail2ban installed and working (tested with v0.10.2)
- gawk installed
- Copy the scripts
fail2ban-subnet.awkandfail2ban-subnet-starter.shto a location of your choice - Ensure the
fail2ban-subnet-starter.shis executable (chmod +x ...) - Copy
iptables-subnet.localto/etc/fail2ban/action.d/ - Copy
recidive-subnet.localto/etc/fail2ban/filter.d - Add filter definition in
jail.localto your/etc/fail2ban/jail.local(at the very end) - Configure settings in
fail2ban-subnet-starter.sh - Configure settings in
/etc/fail2ban/jail.local(esp. bantime) - Restart fail2ban (depends on your unix distro)
- Call your
fail2ban-subnet-starter.sh(for the first and the last time by hand) - Add
fail2ban-subnet-starter.shto your cron to be started regularly (e.g. every hour)
findtime- Time period in seconds
- A subnet is banned if it generated more than
maxips"Found" messages with different ip's in the lastfindtimeseconds - My setting: 5 days
findtime=$((5*24*60*60))
excluded_jails- String with jailnames separated with <space>
- The jailnames given in
excluded_jailswill not be checked - My setting: exclude recidive and recidive-subnet (myself)
excluded_jails="recidive recidive-subnet"
maxipsmaxipsis the number of maximum different ips from a /24 subnet with a "Found" in the fail2ban logfile before the subnet gets banned.- smaller numbers make the tool more "aggressive".
- In my opinion this should be not smaller than 3
- My setting:
maxips=5
logfilename- The path and the name of the fail2ban logfile
- My setting:
logfilename=/var/log/fail2ban.log
outputfile- File path and name where to log the found subnet
- Should correspond to the parameter
logpathin the[recidive-subnet]jail definition injail.local - My setting:
outputfile=/var/log/fail2ban-subnet.log
awkscript- File path and name where you stored the awk script
fail2ban-subnet.awk - My setting:
awkscript=/opt/fail2ban-subnet/fail2ban-subnet.awk
- File path and name where you stored the awk script
Fail2ban recommends to not directly change the jail.conf file, but to create a jail.local file with your local configuration. So if you correctly installed and configured fail2ban you should have a jail.local file.
The jail.local file given in this repository should not override your jail.local file but you should copy the content at the end of your existing jail.local.
Then you can adapt the configuration to your needs.
enabled- This enables or disables the jail
- My setting:
enabled = true
action- The action to start, if fail2ban found a recidive subnet
- This is a copy of the default action
action_mwlwhich bans and sends a mail with whois information, but I changed the%(banaction)stoiptables-subnetwhich is my own defined action for banning /24 subnets - My setting:
action = iptables-subnet[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"%(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
logpath- The path to the file which the filter should parse
- This is the file, which is written by the scripts above
- My setting:
logpath = /var/log/fail2ban-subnet.log
bantime- Time period how long the subnet should be banned
- As I'm really bored by those attackers, I give them half a year!
- My setting:
bantime = 26week
findtime- A subnet is banned if it has generated
maxretrylogs during the lastfindtimeseconds. - A change only makes sense, if you change the
maxretryto something other than1 - My setting:
findtime = 1day
- A subnet is banned if it has generated
maxretrymaxretryis the number of founds before a subnet get banned.- As most of the work is done by the scripts which generate the
fail2ban-subnet.logonce a subnet is found it should be banned directly, so I setmaxretryto1 - If you don't want to be as aggressive as I, then better change the
maxipsconfig above to a higher value or lower thebantime - My setting:
maxretry = 1