System: Using a Fail2Ban Jail to Whitelist a User
Let's suppose we have a directory /login/ which our users need to connect to using Basic Auth or similar authentication. Most CMS's have an address of this type, and they tend to attract a lot of attention from botnets and other automated scripts.
Leaving aside for now other security options (using a different port, certificate based authentication, port knocking, etc.) let's look at how we can aggressively ban failed login attempts while limiting the chances of also blocking a valid user for mis-typing their login because CAPS was on.
The following has been tested in Fail2Ban 0.9.3. The same approach will also work in 0.8.13, but trying to reload the whitelist jail will hang the process, requiring a messy KILL and restart, so it's not advised. See below for upgrade instructions.
Blocking with Fail2Ban
First the simple part. You should be familiar by now with the basic Fail2Ban configuration for setting up a jail/filter. If not, there are plenty of examples included with a default installation.
First we define the jail apache-loginfail as follows:
[apache-loginfail] enabled = true port = http,https logpath = %(apache_access_log)s maxretry = 5 bantime = 172800
Using Debian, apache_access_log maps to /var/log/apache2/*access.log. Other platforms may use a different path, and earlier versions of Fail2Ban need an explicit path here.
The regular expressions for detecting a failed login go into the filters directory:
[INCLUDES] before = apache-common.conf [Definition] failregex = ^<HOST> .* "(GET|POST) /login/.*" (401|403|404) ignoreregex =
So we're going to catch any requests for the /login/ directory that result in a 40* error, and ban them after five (5) attempts and for a period of 48 hours.
At this point you should run some tests to verify that the jail is set up correctly before you activate it - most easily by just restarting the Fail2Ban daemon:
# systemctl restart fail2ban.service
or more properly using the built-in commands:
# fail2ban-client add apache-loginfail auto # fail2ban-client start apache-loginfail
Make sure that the address you want to protect does not appear on any public pages as a clickable link. If it does, use rel="nofollow" on the link and add /login/ to your robots.txt file. Otherwise you will just end up blocking harmless spiders.
Setting up a whitelist action
Everything to this point is vanilla Fail2Ban. What we want to do next is a bit more complicated.
We know we can 'whitelist' ip addresses using the Fail2Ban configuration files:
ignoreip = 127.0.0.1/8 192.168.0 secure.example.net
Or dynamically using the command-line tool:
# fail2ban-client set apache-loginfail addignoreip 22.214.171.124
But what we want to do is to automatically add users to the whitelist immediately after they perform a successful login. That will protect them from being banned until/unless the whitelisting as expired.
This requires three (3) separate files:
1. Jail definition: /etc/fail2ban/jail.d/apache-whitelist.conf
[apache-whitelist] enabled = true port = http,https logpath = %(apache_access_log)s action = ignoreip[name=apache-loginfail] maxretry = 1 bantime = -1
2. Filter definition: /etc/fail2ban/filter.d/apache-whitelist.conf
[Definition] failregex = ^<HOST> .* "GET /login/.*" 200 ignoreregex =
3. Action definition: /etc/fail2ban/action.d/ignoreip.conf
[Definition] actionstart = actionstop = actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>[ \t]' actionban = fail2ban-client set <name> addignoreip <ip> actionunban = fail2ban-client set <name> delignoreip <ip> [Init] name = default chain = INPUT
In Fail2Ban 0.10 we have had to remove the actioncheck condition as Fail2Ban instantiates the f2b-* iptables chains only on demand and not by default.
What's new in the above jail is the action setting. Instead of using one of the built-in actions to manipulate iptables rules we've created a new action ignoreip which instead runs Fail2Ban client commands. Actions can be used to run just about anything from sending emails to restarting services.
You can think of the ignoreip action as a function, taking the name of a jail as the parameter. When the filter is triggered by a successful login (any request to the secure directory returning 200 OK) the ignoreip action is called with the 'name' variable set to 'apache-loginfail'.
What happens next should be obvious. Instead of being added to an iptables chain, the captured ip address will instead be added to the 'ignoreip' list for the 'apache-loginfail' jail. And because we set 'bantime' to '-1' it will stay there indefinitely so the user can no longer run afoul of the failed login jail.
If you want to be notified when anyone is whitelisted, check out the sendmail-whois action. Otherwise you can follow the logs in /var/log/fail2ban.log, or use the command line:
# fail2ban-client get apache-loginfail ignoreip
Our actual settings are as usual a bit more complex, and we don't monitor the access log/s directly, but via an rsyslog filter. More on that later.
If you get this working on your own server, or have questions, get in touch using our Feedback Form.
Installing Fail2Ban 0.9 from 'stretch'
If you're running Debian stable and want to install a newer version of Fail2Ban from testing, you first need to include the 'testing' archived in you apt-sources:
deb ftp://ftp.debian.org/debian/ stretch main deb-src ftp://ftp.debian.org/debian/ stretch main
Then pin the Fail2Ban package so it prefers the 'testing' version:
Package: * Pin: release o=Debian,a=testing Pin-Priority: -1 Package: fail2ban Pin: release o=Debian,a=testing Pin-Priority: 900
And finally run the installer:
# apt-get -u install fail2ban
Be aware that, if you're not doing a clean install, upgrading to Fail2Ban 0.9 involves changes to existing jail definitions as well as to the Fail2Ban file structure.
Related Articles - Fail2Ban
- System Monitoring the fail2ban log
- System Optimising your Fail2Ban filters
- System Using systemd to bind fail2ban to nftables
- System Blocking FTP Hacking Attempts
- System Using a Fail2Ban Jail to Whitelist a User
- System Implementing Port Knocking with knockd
- System Fail2Ban 0.8.3 Howto
- System fail2ban and sendmail
- System fail2ban and iptables
unknown66 27 January, 2020
I had some problems with f2b v0.11.1 while shutdown. Because of this failure the shutdown timed out while stopping the jail.
The following action is working now:
actionstart = touch <tmpfile>
actionflush = rm <tmpfile>
#actioncheck = iptables -n -L <chain> | grep -q 'f2b-<name>'
actionban = if [ -f <tmpfile> ]; then fail2ban-client set <name> addignoreip <ip>; fi;
actionunban = if [ -f <tmpfile> ]; then fail2ban-client set <name> delignoreip <ip>; fi;
name = default
chain = INPUT
tmpfile = /var/run/fail2ban/tmp-ignoreip
Hope this helps someone else!
John McLaine 21 May, 2018
Created the filter for dovecotWhitelist:
The filter I created to catch the successful logins for dovecot :
failregex = (?: pop3-login|imap-login): .*(?:Login:).*rip=(?P<host>\S*),.*
John McLaine 21 May, 2018
[2. Setting up whitelist action]
What kind of filter do I need to use for dovecot in /etc/fail2ban/filter.d/dovecot-whitelist.cong ?
Sharif 16 May, 2018
Can i get ignoreip list from database.Is it possible.
If you mean can Fail2Ban query an external database directly, I don't think so.
Otherwise you can get a list of ip addresses from the command-line:
sudo fail2ban-client get <JAIL> ignoreip
Or add an IP address using:
sudo fail2ban-client set <JAIL> addignoreip <IP>
Joan 30 June, 2016
Your whitelist solutions saved me a ton of time.
The only thing I do not understand is the "actioncheck"
Why are you checking against iptables?
From what I understand the 'actioncheck' command is run by Fail2Ban before executing 'actionban' or 'actionnban'. In this case it confirms that the chain we're targetting exists in iptables.
If you're using a back-end other than iptables this will need changing.
wqw 19 October, 2015
Upon restarting fail2ban 9.1 on CentOS 6.4:
ERROR fail2ban-client set jail delignoreip XX.XXX.XX.XX -- stderr: 'ERROR Failed to access socket path: /var/run/fail2ban/fail2ban.sock. Is fail2ban running?\n'
This may have been fixed in 0.9.3. (Release notes)
Can anyone confirm?