Update: Fail2ban Multiline Filters On Jessie 2023-05-01
Today I found myself wanting to do another fail2ban install and as it turns out these instructions still work on Devuan Chimaera, which is essentially the same as Debian Bullseye. I installed fail2ban v0.10.6 with these instructions except the minor modification that python-pyinotify is now called python3-pyinotify.
Update: Fail2ban Multiline Filters On Jessie 2020-08-18
I was fiddling with this on my router today and got
-bash: /usr/bin/fail2ban-client: No such file or directory
when trying to use the fail2ban-client command. So I added a line at the bottom of the instructions to link to the proper fail2ban-client.
Update: Fail2ban Multiline Filters On Jessie 2019-06-11
I was reinstalling a VPS of mine and needed to follow these instructions. As I did, I decided to update the paths to use fail2ban v0.9.7 instead of 0.9.4 (and the instructions still work). As far as I can tell, 0.9.7 is going to be the last version of the 0.9.x series for fail2ban, so I expect I won't need to update this until I find some requirement for 0.10.x.
Update: Fail2ban Multiline Filters On Jessie 2019-03-20
Just made a little update today to add a couple of lines at the beginning of the instructions to remove any existing installation of fail2ban. Since of course if you are reading this you are probably fighting with a version of fail2ban that won't do multiline filters.
Fail2ban Multiline Filters On Jessie 2018-11-19
I like to use Fail2ban on some of my machines to keep some of the losers at bay. It basically keeps an eye on your log files and when it sees someone (an IP address usually) doing something stupid, it can perform an action (usually banning that IP in some kind of firewall). Most of the machines I have that require Fail2ban are running Debian Jessie which comes with version 0.8.13-1 of Fail2ban. This works fine for most of my needs but on my mail server I have been seeing a LOT of this crap in my log files:
Nov 19 13:17:32 mx2 postfix/smtpd[29473]: connect from unknown[60.31.214.44]
Nov 19 13:17:38 mx2 postfix/smtpd[29473]: disconnect from unknown[60.31.214.44]
Nov 19 13:19:45 mx2 postfix/smtpd[29480]: connect from unknown[60.31.214.44]
Nov 19 13:19:47 mx2 postfix/smtpd[29480]: disconnect from unknown[60.31.214.44]
Nov 19 13:20:26 mx2 postfix/smtpd[29480]: connect from unknown[185.36.81.43]
Nov 19 13:20:27 mx2 postfix/smtpd[29480]: disconnect from unknown[185.36.81.43]
Nov 19 13:22:03 mx2 postfix/smtpd[29480]: connect from unknown[60.31.214.44]
Nov 19 13:22:04 mx2 postfix/smtpd[29480]: disconnect from unknown[60.31.214.44]
Nov 19 13:24:02 mx2 postfix/smtpd[29486]: connect from unknown[185.36.81.43]
Nov 19 13:24:02 mx2 postfix/smtpd[29486]: disconnect from unknown[185.36.81.43]
Nov 19 13:24:24 mx2 postfix/smtpd[29486]: connect from unknown[60.31.214.44]
Nov 19 13:24:29 mx2 postfix/smtpd[29486]: disconnect from unknown[60.31.214.44]
Nov 19 13:26:44 mx2 postfix/smtpd[29490]: connect from unknown[60.31.214.44]
Nov 19 13:26:45 mx2 postfix/smtpd[29490]: disconnect from unknown[60.31.214.44]
Nov 19 13:27:42 mx2 postfix/smtpd[29490]: connect from unknown[185.36.81.43]
Nov 19 13:27:42 mx2 postfix/smtpd[29490]: disconnect from unknown[185.36.81.43]
Nov 19 13:29:02 mx2 postfix/smtpd[29490]: connect from unknown[60.31.214.44]
Nov 19 13:29:04 mx2 postfix/smtpd[29490]: disconnect from unknown[60.31.214.44]
Nov 19 13:31:18 mx2 postfix/smtpd[29495]: connect from unknown[60.31.214.44]
Nov 19 13:31:19 mx2 postfix/smtpd[29495]: disconnect from unknown[60.31.214.44]
Nov 19 13:31:21 mx2 postfix/smtpd[29495]: connect from unknown[185.36.81.43]
Nov 19 13:31:21 mx2 postfix/smtpd[29495]: disconnect from unknown[185.36.81.43]
Nov 19 13:33:33 mx2 postfix/smtpd[29497]: connect from unknown[60.31.214.44]
Nov 19 13:33:44 mx2 postfix/smtpd[29497]: disconnect from unknown[60.31.214.44]
Nov 19 13:35:06 mx2 postfix/smtpd[29497]: connect from unknown[185.36.81.43]
Nov 19 13:35:07 mx2 postfix/smtpd[29497]: disconnect from unknown[185.36.81.43]
Nov 19 13:35:58 mx2 postfix/smtpd[29497]: connect from unknown[60.31.214.44]
Nov 19 13:35:59 mx2 postfix/smtpd[29497]: disconnect from unknown[60.31.214.44]
There is nothing terribly harmful there, but this gets pretty tiresome for someone who actually looks at logs. Those of you who do not look at logs can just wink back at Marigold's ass and close this tab. Those of you who would suggest that I could filter that crap from my log analysis application can just wink back at Marigold's ass and close this tab. Those of you who find yourselves thinking:
Hey, you can't write a Fail2ban rule for that because those "connect" and "disconnect" lines happen for every legitimate message that arrives at your mail server!
may be interested in what follows. Yes, v0.8.x of Fail2ban can not filter based on multiple lines... but v0.9.x can. To get v0.9.x on Jessie one might try the Stretch .deb file or the NeuroDebian repos (yes, that is an http link), but it is actually easier than that. If you scratch around in the Fail2ban documentation you'll find a couple of little items that make it a breeze to get v0.9.x working on your Jessie box. All it really takes is this (yes, logged in as root):
# apt-get update
# apt-get purge fail2ban
# apt-get autoremove --purge
# apt-get install python-pyinotify
# cd ~ && mkdir f2btmp && cd f2btmp
# wget https://github.com/fail2ban/fail2ban/archive/0.9.7.tar.gz
# tar -xzf 0.9.7.tar.gz
# cd fail2ban-0.9.7
# python setup.py install
# cp files/debian-initd /etc/init.d/fail2ban
# cd ~
# rm -R f2btmp/
# update-rc.d fail2ban defaults
# ln -s /usr/local/bin/fail2ban-client /usr/bin/fail2ban-client
Once you have Fail2ban running you will also need to configure three parts for your filter.
- a jail
- a filter
- an action
The jail basically defines your new filter system, such as how many times the line must appear, in how many minutes, what filter to use, and what action to take. The filter is one or more regular expressions that tell Fail2ban what to look for in the logs, and the action tells it what to do when it finds it. To block the garbage listed above you would create a new jail.local file like this:
/etc/fail2ban/jail.local
[fu_smtp_empty]
enabled = true
logpath = /var/log/mail.log
maxretry = 1
findtime = 60
bantime = 86400
banaction = fu_smtp_empty
In short, if Fail2ban finds the filter 1 time, in less than 60 seconds, it will ban the IP for 86400 seconds (one day), based on the "fu_smtp_empty" action file. Next, the file defining the actual filter needs to be created:
/etc/fail2ban/filter.d/fu_smtp_empty.conf
[Init]
maxlines = 4
[Definition]
failregex = ^.* mx2 postfix\/smtpd\[\d*\]: connect from .*\[<HOST>\]\n.* mx2 postfix\/smtpd\[\d*\]: disconnect from .*\[.*\]$
ignoreregex =
The "maxlines" directive limits how many lines this filter can span. I chose 4 [possibly foolishly] because I wanted to account for the possibility that log lines from unrelated connections may creep between the two I am looking for, and because I wanted the number to be significantly less than I would normally see in a legitimate session. The failregex line of course tells Fail2ban what to look for, and the "ignoreregex" can be used in case an exception needs to be made. Note the use of \n in the failregex to show that Fail2ban should see a newline to span multiple lines. Finally, an action file is required:
/etc/fail2ban/action.d/fu_smtp_empty.conf
[Definition]
actionstart =
actionstop =
actioncheck =
actionban = iptables -I INPUT -s <ip> -j DROP
logger -t "smtp/fail2ban" -i -p mail.info ALERT <ip> BANNED FOR EMPTY SMTP SESSIONS
actionunban = iptables -D INPUT -s <ip> -j DROP
This pretty simple ban action will add an iptables rule that drops all traffic from this IP address and also barfs a log entry in my mail.log to show that the IP has been dumped. When the 86400 seconds elapses, the actionunban line will remove the iptables rule and allow traffic from that IP address once again.