Today I post something about the nice little tool fail2ban. As you probably know, fail2ban can be used to block those annoying brute force attacks against your servers. Other than the also popular and useful tool DenyHosts it allows the protection of other services than SSH as well (e.g. HTML login pages served by Apache). The working mechanism also differs from that of DenyHosts, as fail2ban uses iptables instead of the BSD style hosts.deny file to block annoying brute forcers. Installation is quite simple, on Debian for example, just install it through apt and you’re good to go even with the default config.
One thing that I was missing, was the option to ban IPs forever. You can basically do this by setting bantime to a negative value, but as soon as you reload your iptables rules (e.g. by restarting the fail2ban service or the whole system) the entries for the permanently banned IPs are gone.
To overcome this issue, I did some minor changes to the actions fail2ban executes on start-up and on banning.
IMPORTANT: I strongly advise you, to be careful while playing around with automated banning tools, especially if you can’t reach your server physically. Make sure, that you have something useful set in the ignoreip option under the [DEFAULT] jail (your current IP address) to not accidentally lock you out of the system (really nasty with permanent banning active…)
- First, check the banaction currently used (you need that, to modify the correct actionfile afterwards)
/etc/fail2ban/jail.local# # ACTIONS # ... banaction = iptables-multiport ...
- Open up the corresponding actionfile and modify according to the sample below (changes are under the # Persistent banning of IPs comment)
/etc/fail2ban/action.d/iptables-multiport.conf... actionstart = iptables -N fail2ban-<name> iptables -A fail2ban-<name> -j RETURN iptables -I INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-<name> # Persistent banning of IPs cat /etc/fail2ban/ip.blacklist | while read IP; do iptables -I fail2ban-<name> 1 -s $IP -j DROP; done ... actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP # Persistent banning of IPs echo '<ip>' >> /etc/fail2ban/ip.blacklist ...
- Your blacklist should look something like this (one IP per line, of course you can add IPs manually)
/etc/fail2ban/ip.blacklist... 10.0.0.242 192.168.1.39 ...
- Restart fail2ban to make the changes active
Now, what happens is that each time fail2ban starts, it loops through your ip.blacklist and blocks the IPs in there. If fail2ban blocks a new IP, it will automatically append it to the blacklist.
Links
http://www.fail2ban.org
http://www.fail2ban.org/wiki/index.php/Whitelist
http://denyhosts.sourceforge.net
Update
The following config adds some nice features that were missing in the example above:
- No duplicate iptables rules (@Lin: might be interesting for you)
- Jail specific blocking rules (similar to Dr. Tyrell’s and samuelE’s suggestions in the comments)
- Reporting offender IPs to badips.com
/etc/fail2ban/action.d/iptables-multiport.conf:
# Fail2Ban configuration file
#
# Author: Cyril Jaquier
# Modified by Yaroslav Halchenko for multiport banning and Lukas Camenzind for persistent banning
#
#
[Definition]
# Option: actionstart
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
#
actionstart = iptables -N fail2ban-<name>
iptables -A fail2ban-<name> -j RETURN
iptables -I INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
# Load local list of offenders
if [ -f /etc/fail2ban/ip.blacklist ]; then cat /etc/fail2ban/ip.blacklist | grep -e <name>$ | cut -d "," -s -f 1 | while read IP; do iptables -I fail2ban-<name> 1 -s $IP -j DROP; done; fi
# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
#
actionstop = iptables -D INPUT -p <protocol> -m multiport --dports <port> -j fail2ban-<name>
iptables -F fail2ban-<name>
iptables -X fail2ban-<name>
# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
#
actioncheck = iptables -n -L INPUT | grep -q fail2ban-<name>
# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD
#
actionban = if ! iptables -C fail2ban-<name> -s <ip> -j DROP; then iptables -I fail2ban-<name> 1 -s <ip> -j DROP; fi
# Add offenders to local blacklist, if not already there
if ! grep -Fxq '<ip>,<name>' /etc/fail2ban/ip.blacklist; then echo '<ip>,<name>' >> /etc/fail2ban/ip.blacklist; fi
# Report offenders to badips.com
wget -q -O /dev/null www.badips.com/add/<name>/<ip>
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: <ip> IP address
# <failures> number of failures
# <time> unix timestamp of the ban time
# Values: CMD
#
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP
# Disabled clearing out entry from ip.blacklist (somehow happens after each stop of fail2ban)
# sed --in-place '/<ip>,<name>/d' /etc/fail2ban/ip.blacklist
[Init]
# Defaut name of the chain
#
name = default
# Option: port
# Notes.: specifies port to monitor
# Values: [ NUM | STRING ] Default:
#
port = ssh
# Option: protocol
# Notes.: internally used by config reader for interpolations.
# Values: [ tcp | udp | icmp | all ] Default: tcp
#
I made a change:
cat /etc/fail2ban/ip.blacklist- | while read IP; do iptables -I fail2ban- 1 -s $IP -j DROP; done
echo ” >> /etc/fail2ban/ip.blacklist-
the basic idea is to save exactly the same list that has been blocked.
Some of the comment has been removed, it should read as follows:
cat /etc/fail2ban/ip.blacklist- | while read IP; do iptables -I fail2ban- 1 -s $IP -j DROP; done
echo '' >> /etc/fail2ban/ip.blacklist-
This creates seperate ip.blacklist files for each row you’re blocking (ssh, apache, etc.).
And it’s been removed again! Even in code tags! Okay, one more time.
cat /etc/fail2ban/ip.blacklist-<name> | while read IP; do iptables -I fail2ban-<name> 1 -s $IP -j DROP; done
echo '<ip>' >> /etc/fail2ban/ip.blacklist-<name>
If it doesn’t work this time I give up.
great info… this would be fail2ban generic setup for all linux varians.
Thank u very much
If only now if we could do the same with those pesky spam referrers !
Thanks for this post – just what I am looking for. Now I can reboot my server without fiddling !
Ciao
Thanks for this, the restart problem has been troubling me for a while.
What would make it even better would be a mechanism to honour the ban-times originally set. I set a pretty high ban-time anyway, but I do think the principle of ban-time is important so your blacklist does not keep growing indefinitely, as this will eventually make iptables become a resource-hog.
Any thoughts on how to do that?
Hi Robin
Thanks for your comment. To achieve something that honors the ban-time, you need to put a bit more logic inside the “action” part (e.g. grepping and removing the banned ip in “actionunban”). Also, you need to record the time the ban took place (see field time in iptables-multiport.conf) in order to avoid “dead” banned IPs in your blacklist in case you restart fail2ban… But that would be nice to have indeed. Probably, I will try to figure out something.
Cheers
Looke
Alteration of original, to keep one banlist file, and be able to know which filter:
actionstart = …
cat /etc/fail2ban/ip.blacklist | while read line; do IP=`echo $line | cut -d’ ‘ -f1`; iptables -I fail2ban- 1 -s $IP -j DROP; done
actionban = …
echo ‘ ‘ >> /etc/fail2ban/ip.blacklist
damn, second try to avoid html interpret…
actionstart = …
cat /etc/fail2ban/ip.blacklist | while read line; do IP=`echo $line | cut -d’ ‘ -f1`; iptables -I fail2ban-<name> 1 -s $IP -j DROP; done
actionban = …
echo ‘<ip> <name>’ >> /etc/fail2ban/ip.blacklist
Format of ip.blacklist file: ip space filtername
This was a nice post.
My requirement is slightly different. I want the IP to be released after specified interval from the ban list. Once released, if an attack comes from the same IP, it bans and releases after the interval again. This loop continues for say 3 / 5 times and after 5 times, the IP is banned permanently.
This can be done with a custom script but does fail2ban has any inbuilt functionality / configuration to achieve this without external scripting?
Thanks all in advance,
Naidu
I believe I jail on fail2ban’s own log might help.
Thanks for this article – it was a huge help in getting a similar solution deployed to my servers. I had a similar requirement to what BTR Naidu describes – normal block-then-unblock for a number of times, then add to permanent blocklist. I’ve detailed the steps here: Permanently Ban Repeat Offenders With fail2ban.
Hello,
I implemented the Persistent banning of IPs
and it works fine but it has an issue.
It duplicates the iptables rules when an offender ip is saved
again in /etc/fail2ban/ip.blacklist by fail2ban
Is there a way to fix it?
Thanks and Best Regards,
Lin
Thanks for the great post.
Do you have any idea how long this list can get without slowing the system too much down?
Cheers
Hi Martin,
I never tested this… but as each line in ip.blacklist will result in a iptables rule, the limiting factor will be the memory of your system. I found a thread (http://www.spinics.net/lists/netfilter/msg51895.html) where somebody calculated that the rule-limit for a 32bit system is around 38million rules :)
Cheers,
Lukas
actionban = iptables -I fail2ban- 1 -s -j DROP
# Persistent banning of IPs
echo ‘ ‘ > > /etc/fail2ban/ip.blacklist
So any banned address is banned permanently ??
Thanks !
Hi Steph,
Yes, if you also set the parameter “bantime” in the jail options to a negative value (http://www.fail2ban.org/wiki/index.php/MANUAL_0_8#Jail_Options), the banned IP addresses will be blocked idefinately (even after a restart of the fail2ban service).
Cheers,
Looke
Hola, realice un cambio a las propuestas anteriores, probé y si agregaba permanentemente pero lo metia en todas las cadenas… con esta modificación lo ingresa en la que originalmente en la cadena que callo por primera vez.
cat /etc/fail2ban/ip.blacklist | while read line; do IP=`echo $line | cut -d ” ” -f1` NAME=`echo $line | cut -d ” ” -f2`; iptables -I fail2ban-$NAME 1 -s $IP -j DROP; done
Saludos espero podamos seguir merojando esto entre todos.
actionstart =…..
cat /etc/fail2ban/ip.blacklist | while read line; do IP=`echo $line | cut -d ” ” -f1` NAME=`echo $line | cut -d ” ” -f2`; iptables -I fail2ban-$NAME 1 -s $IP -j DROP; done
actionban = …………..
echo ‘ ‘ >> /etc/fail2ban/ip.blacklist
logico que se tiene que guardar el nombre y la cadena en el mismo archivo.
jajaja mismo problema que Dr. Tyrell ….
actionban = …………..
echo ‘ ‘ >> /etc/fail2ban/ip.blacklist
ejemplo ip.blacklist
10.1.2.3 Zimbra-audit
10.2.2.3 Zimbra-account
10.3.2.3 -Zimbra-recipient
Saludos
For future googlers, while echoing ip top the ip.blacklist, single quotes wrapped around ip didn’t work for me. I had to remove single quotes and just leave
Hello All,
Great article.
Does anyone have a clean current copy of this config?
I copy & pasted and tried to clean up the above but can’t seem to get it to load the list and for that matter maintain the list.
Thanks!
Thanks you very much for this really good article and the example conf file. Do you such configuration file for iptables.conf?
Thanks in advance.
I doubt this will help anyone, but if there are any anal retentive sorts out there like me, this little snippet of code will get all your banned IPs lined up in numerical order so they are sorted both in the ip.blacklist and in the output of `iptables -L -n’
Note: most of this is from the author of this post. Mine is just the “sort” line and the three lines following:
actionban = if ! iptables -C fail2ban- -s -j DROP
then
iptables -I fail2ban- 1 -s -j DROP
fi
# Add offenders to local blacklist, if not already there
if ! grep -Fxq ‘,’ /etc/fail2ban/ip.blacklist
then
echo ‘,’ >> /etc/fail2ban/ip.blacklist
sort -n -r -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 /etc/fail2ban/ip.blacklist > /etc/fail2ban/temp.txt \
&& mv /etc/fail2ban/temp.txt /etc/fail2ban/ip.blacklist \
&& chmod 640 /etc/fail2ban/ip.blacklist \
&& chown root.root /etc/fail2ban/ip.blacklist
fi
# Report offenders to badips.com
wget -q -O /dev/null http://www.badips.com/add//
Follow up note. The reason they are sorted in reverse in the ip.blacklist file is because of the way they are inserted into the iptables line-up. By putting them in reverse order in blacklist.ip, they will be in normal order when running ‘iptables -L -n’
can you explain this please?
# Disabled clearing out entry from ip.blacklist (somehow happens after each stop of fail2ban)
# sed --in-place '/,/d' /etc/fail2ban/ip.blacklist
I see it’s there but commented out. is it an ongoing issue?
one more question – is the ail name which follows the comma mandatory for this to work? (e.g., “123.45.67.890,ssh”) I tried a couple ips on a line by themself as you mentioned at the top of the article but had no effect until I added “,ssh” after them. Is there a way to make this optional?
Also, are comments allowed? Would be nice but I’m not sure if it would mess something up with the parsing of the blocklist file
Can anyone advise whether it is possible to block entire ip ranges in the above manner? I’d like to add to the ip.blacklist file two Chinese ranges that have been heavily attempting ssh access to servers I manage. So can I add 1.2.3.o/24 to ip.blacklist and have it drop any attempt to access from any ip in that range?
Hi,
Thank you for this great post! I seen that u add the bad ip to “badips.com”.
But what about to get the list of agressive ips? :)
You could add wget -qO- http://www.badips.com/get/list/ssh/5 >> /etc/fail2ban/ip.blacklist;fi” in the actionstart.
Bueno despues de multiples pruebas la solucion es
actionstart = iptables -N fail2ban-
iptables -A fail2ban- -j RETURN
iptables -I -p -m multiport –dports -j fail2ban-
cat /etc/fail2ban/ip.blacklist- | while read IP; do iptables -I fail2ban- 1 -s $IP -j DROP; done
actionban = iptables -I fail2ban- 1 -s -j DROP
echo ” >> /etc/fail2ban/ip.blacklist-
For anyone who has the problem where the “ip.blacklist” file is not created: be sure to check you’re editing the correct action file.
I know it’s been a couple of years since the last comment, but I’m going to give it a shot. This could be a HUGE life saver for me if I could get this to work.
Is there a way to just put in the iptables-allports.conf file a line like the following:
iptables -I f2b-default -1 -s 1.0.0.0/8 -j DROP
I don’t want to permanently block every ip that gets tagged as my iptables would have six million lines in there. But. Being able to block a set of CIDR’s would eliminate 90% of my traffic.
This would allow the other small time kiddies to get blocked for a day and go away as most of them don’t return but once a month.
Thank you for the article. You’ve given me some great ideas.