Fail2ban: Store banned IP addresses in SQL database


Check out the lightweight on-premises email archiving software developed by iRedMail team: Spider Email Archiver.


Since iRedMail-1.2, Fail2ban is configured to store banned IP addresses in SQL database. If you run iRedAdmin-Pro or your own web admin panel, it will be very easy to check and manage banned IP addresses. But if you don't run iRedAdmin-Pro or don't have custom web admin panel, this integration is totally optional.

With this SQL integration, to unban an IP address from web admin panel, you can simply update value of column banned.remove to 1, then wait for up to one minute, a cron job will call fail2ban-client to actually unban it.

With iRedAdmin-Pro, you can login as global admin, go to Activities -> Banned IP Addresses, then click the Unban button to unban it.

How it works

When some (bad) client triggers the ban, Fail2ban will perform actions defined in action = parameter in jail config file. For example, in jail dovecot (/etc/fail2ban/jail.d/dovecot.local):

enabled     = ...
filter      = ...
logpath     = ...
action      = iptables-multiport[name=dovecot, port="80,443,25,587,465,110,995,143,993,4190", protocol=tcp]

Action name iptables-multipart maps to commands defined in /etc/fail2ban/action.d/iptables-multiport.conf for different fail2ban actions. For example:


# Notes.:  command executed once at the start of Fail2Ban.
actionstart = ...

# Notes.:  command executed once at the end of Fail2Ban
actionstop = ...

# Notes.:  command executed once before each actionban command
actioncheck = ...

# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
actionban = ...

# Notes.:  command executed when unbanning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
actionunban = ...

In this tutorial, we will add a custom action config file and update jail config files to use this action.

Create required SQL database

For OpenLDAP backend and MySQL/MariaDB backends

We will create a new database named fail2ban to store banned IP addresses, also a SQL user fail2ban.

cd /tmp
CREATE DATABASE fail2ban DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL ON fail2ban.* TO 'fail2ban'@'localhost' IDENTIFIED BY '<my-secret-password>';

USE fail2ban;
SOURCE /tmp/fail2ban.mysql;

For PostgreSQL backend

We will create a new database named fail2ban to store banned IP addresses, also a SQL user fail2ban.

cd /tmp
su - postgres
psql -d template1
ALTER DATABASE fail2ban OWNER TO fail2ban;

-- PostgreSQL will prompt to input password for user "fail2ban" with command below.
\c fail2ban fail2ban;
\i /tmp/fail2ban.pgsql;

Add required Fail2ban config file and script

On Linux, run commands below as root user:

mv banned_db.conf /etc/fail2ban/action.d/

mv fail2ban_banned_db /usr/local/bin/
chmod 0550 /usr/local/bin/fail2ban_banned_db

File /etc/fail2ban/action.d/banned_db.conf indicates we now have a new action named banned_db (it's file name without extension). Feel free to open this file and check what it does.

Script /usr/local/bin/fail2ban_banned_db will read SQL username and password from /root/.my.cnf-fail2ban (OpenLDAP/MySQL/MariaDB backends) or ~postgresql/.pgpass (PostgreSQL backend), then connect to SQL server and add or remove banned IP addresses.

Enable the new action banned_db

Now go to directory /etc/fail2ban/jail.d/ and update config files for the jails you want to store banned IP in SQL db. Let's take dovecot.local for example.

action      = iptables-multiport[name=dovecot, port="80,443,25,587,465,110,995,143,993,4190", protocol=tcp]
action      = iptables-multiport[name=dovecot, port="80,443,25,587,465,110,995,143,993,4190", protocol=tcp]
              banned_db[name=dovecot, port="80,443,25,587,465,110,995,143,993,4190", protocol=tcp]

That's it. It's recommend to enable this new action banned_db for all jails.

Now restart fail2ban service to load modified config files.

Add required cron job to query SQL database and unban IP addresses

Now add a cron job for root user with command crontab -e -u root:

# Fail2ban: Unban IP addresses pending for removal (stored in SQL db).
* * * * * /bin/bash /usr/local/bin/fail2ban_banned_db unban_db

It runs every minute and queries SQL database to get banned IP addresses which are pending for removal.

Optional: look up and store country name of banned IP address

Script /usr/local/bin/fail2ban_banned_db detects whether commands geoiplookup and geoiplookup6 exist, if exist, it runs the command to query country of banned IP address and store it in SQL database.

Both commands are offered by GeoIP related packages, please install them.

yum -y install GeoIP GeoIP-data
yum -y install GeoIP GeoIP-GeoLite-data
apt -y install geoip-bin geoip-database
pkg_add GeoIP geolite-country

For iRedAdmin-Pro users: Enable the SQL integration


iRedAdmin-Pro upgrade script will detect Fail2ban + SQL integration, so if you're upgrading iRedAdmin-Pro, you don't need to add missing configuration parameters manually.

Please add Fail2ban SQL credential in iRedAdmin-Pro config file /opt/www/iredadmin/, then restart iredadmin service. If you're still running Apache web server, you should restart Apache service instead.


If you're running PostgreSQL backend, the port number is 5432.

fail2ban_enabled = True
fail2ban_db_host = ''
fail2ban_db_port = '3306'
fail2ban_db_name = 'fail2ban'
fail2ban_db_user = 'fail2ban'
fail2ban_db_password = '<my-secret-password>'



We use MySQL for example here.

Run fail2ban-client command as root user to ban 2 IP addresses like below:

fail2ban-client set dovecot banip
fail2ban-client set dovecot banip

You can see the banned IP address with command fail2ban-client status <jail>:

fail2ban-client status dovecot

Command output:

Status for the jail: dovecot
|- Filter
|  |- Currently failed: 0
|  |- Total failed: 0
|  `- File list:    ...
`- Actions
   |- Currently banned: 2
   |- Total banned: 2
   `- Banned IP list:

Now run command below to query SQL table fail2ban.banned as root user:

mysql fail2ban -e "SELECT * FROM banned"

You should see the command output like below:

| id | ip      | ports | protocol | jail    | hostname         | country       | timestamp           | remove |
|  3 | | 22    | tcp      | dovecot | ob66.localdomain | AU, Australia | 2020-04-15 13:34:57 |      0 |
|  4 | | 22    | tcp      | dovecot | ob66.localdomain | AU, Australia | 2020-04-15 13:34:58 |      0 |

Now run fail2ban-client command to unban IP and query SQL table fail2ban.banned again, you should see unbanned IP is gone:

fail2ban-client set dovecot unbanip

Now run command as root user to update SQL column banned.remove=1 to simulate the unban triggered by iRedAdmin-Pro:

mysql fail2ban -e "UPDATE banned SET remove=1 WHERE ip=''"

Run script /usr/local/bin/fail2ban_banned_db with argument unban_db as root user:

/usr/local/bin/fail2ban_banned_db unban_db

Again, query SQL table fail2ban.banned as root user, you should see the IP stored in SQL db with remove=1 is gone, and unbanned in fail2ban too:

mysql fail2ban -e "SELECT id,ip,jail FROM banned"
fail2ban-client status dovecot


If there's something, you should see related log in syslog log file or Fail2ban log file:

If you can not solve the error, feel free to create a new forum topic and paste related log in your post.