NB: This article is not about how Fail2Ban works or how to install it.
If you’re running an Internet facing server, you probably know its exposed services are constantly being probed and attacks are being attempted against it. Fortunately, an extremely useful, nice and nifty tool is here to help: Fail2Ban.
Fail2Ban scans service’s log files for patterns defined as regular expressions and, if an offending pattern is found a certain number of times within a given timeframe, the corresponding source IP is banned (ie: blocked) for a configurable time, using local firewall rules such as iptables.
I’m very touchy when it comes to my server security so I’m using Fail2Ban to perform permanent bans of involved source IPs and I’m going to show you how. The problem however is that those bans do not persist across a Fail2ban server restart or a server reboot.
In this article I will show you how to add two simple lines in Fail2Ban configuration file in order to add persistency across restart.
In a typical installation, Fail2ban configuration files are stored in the /etc/fail2ban/ directory. There’s only two files that needs slight modifications:
- jail.conf : it’s the main file defining default options and behavior for so called jails, that is for each service monitored, the definition of log file, detection patterns (filters), actions, timers (findtime, max retry, bantime).
- action.d/iptables-multiport.conf : iptables-multiport is the default action performed by Fail2Ban when an IP is to be banned (or jailed), as defined in the jail.conf configuration file. If you’ve changed the default action, then you’ll have to modify the corresponding action file accordingly.
Configure permanent bans
This is the easiest part. Ban time can be set either globally (ie: for all jails), or per jail. It is controlled through the ‘bantime‘ parameter which defines the number of seconds an IP is banned.
To set a permanent ban, simply set the bantime parameter to a value of -1. Edit the jail.conf file, comment out the existing ‘bantime’ line, and set a new bantime to -1 :
# "bantime" is the number of seconds that a host is banned. # bantime = 600 # Permanent ban bantime = -1
Configure persistent bans
In order for bans to persist across a service restart, they obviously have to be saved somewhere. No fancy database required, a simple text file will do the trick.
The principle is simple: every time Fail2Ban sets a new ban on an IP, we’ll save the information « jail name and IP address » in a file along the way. Next, upon each Fail2Ban service start, we’ll load this file a re-create the corresponding bans. All it takes is two lines in the right configuration file.
Each ban action is defined in a corresponding configuration file. Within this file, there’s two parameters we’re interested in:
- actionstart : here we can define a list of commands that will be executed only once at the start of Fail2Ban. So we’ll add a custom command loading the file /etc/fail2ban/persistent.bans and re-create the corresponding iptables entries.
- actionban : here we can defined a list of commands that will be executed when banning an IP. So we’ll add a custom command to save the useful information to the file /etc/fail2ban/persistent.bans.
The default action in Fail2Ban is iptables-multiport (as defined in the file jail.conf), so we have to edit the action.d/iptables-multiport.conf file and add the following highlighted lines:
[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 <chain> -p <protocol> -m multiport --dports <port> -j fail2ban-<name> cat /etc/fail2ban/persistent.bans | awk '/^fail2ban-<name>/ {print $2}' \ | while read IP; do iptables -I fail2ban-<name> 1 -s $IP -j <blocktype>; done # Option: actionstop # Notes.: command executed once at the end of Fail2Ban # Values: CMD # actionstop = iptables -D <chain> -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 <chain> | grep -q 'fail2ban-<name>[ \t]' # Option: actionban # Notes.: command executed when banning an IP. Take care that the # command is executed with Fail2Ban user rights. # Tags: See jail.conf(5) man page # Values: CMD # actionban = iptables -I fail2ban-<name> 1 -s <ip> -j <blocktype> echo "fail2ban-<name> <ip>" >> /etc/fail2ban/persistent.bans
Once done, it is required to restart Fail2Ban in order for those change to be applied.
arno@myserver:/etc/fail2ban/action.d $ sudo service fail2ban restart
And that’s it !
Like this article ? Tip me with some bitcoins !
Unbanning must also be addressed, because if not you get duplicate entries every time it is restarted.
# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: See jail.conf(5) man page
# Values: CMD
#
actionunban = -D f2b- -s -j
sed -i ‘/fail2ban- /d’ /etc/fail2ban/persistent.bans
J’aimeJ’aime
You could also do :
actionstart =
cat /etc/fail2ban/bans.d/*.ban | awk ‘/^fail2ban-/ {print $2}’ \
| while read IP; do iptables -I fail2ban- 1 -s $IP -j ; done
actionban =
echo « fail2ban- » > /etc/fail2ban/bans.d/fail2ban–.ban
actionunban =
rm /etc/fail2ban/bans.d/fail2ban–.ban
It gives more flexibility.
Cheers for the good work!
J’aimeJ’aime
Perfect! Thank you for the detailed explanation. I’m using f2b in a VPN server and hate to restart without keeping the bans.
J’aimeJ’aime
Thank you!!! good job!!
J’aimeJ’aime
Pardon Monsieur. it is saving duplicate IP on list each time you restart. On line 34 I believe you need to check if IP is in persistent.bans files before you include it with a echo.
J’aimeAimé par 2 personnes
Yup, simply replace that line with:
if ! grep -q « fail2ban- » /etc/fail2ban/persistent.bans; then echo « fail2ban- » >> /etc/fail2ban/persistent.bans; fi
J’aimeAimé par 1 personne
Argh…. and double quotes are now parsed as ‘ »‘ in the response
J’aimeJ’aime
Hi Atlas, great tips 😉
I did not understand where and how to replace the condition to avoid duplicates on both persistence.bans and iptables rules. Could you indicate in a clearer way? Maybe adding the two options to the post, with the code format (to avoid the double quotes problem you’ve got in your comments).
thank you very much.
J’aimeAimé par 1 personne
Anyone figure it out? I replace line 34 with the example Atlas provides and it fails to work at all.
J’aimeJ’aime
I’ll try to use markdown formatting for the correct line:
« `
if ! grep -q « fail2man- » /etc/fail2ban/persistent.bans; then « echo « fail2ban- » >> /etc/fail2ban/persistent.bans; fi
« `
…and hope it works.
This comment form on WordPress.com has become so « clear » that there are no indications of what is allowed.
Also the UI here is in French and I understand it just partially.
J’aimeAimé par 1 personne
If anyone needs a correct and working updated jail config, I’ve published it as a gist: https://gist.github.com/AndisGrossteins/0b041d1449b8b65ce4ac92d71f0dd2f3
J’aimeAimé par 1 personne
Thanx, you save my day.
J’aimeJ’aime
As of version 0.9.0 (2014/03/14) fail2ban includes a persistent database built-in. Although documentation is non-existent at the time I am writing this. It appears to be working on my server. The default database location is /var/lib/fail2ban/fail2ban.sqlite3
J’aimeAimé par 2 personnes
in my case -1 and any value above 436999 throws the following error: Starting fail2ban: ERROR NOK: (‘database disk image is malformed’,)
J’aimeJ’aime
Awesome guide! Thanks a lot!
J’aimeJ’aime