Setting up your firewall with fail2ban

Fail2ban scans log files (e.g. /var/log/apache/error_log) and bans IPs that show the malicious signs -- too many password failures, seeking for exploits, etc. Generally Fail2Ban is then used to update firewall rules to reject the IP addresses for a specified amount of time, although any arbitrary other action (e.g. sending an email) could also be configured. Out of the box Fail2Ban comes with filters for various services (apache, courier, ssh, etc).

I will show shortly how to install and configure fail2ban to ban malicious IPs, expecially those related to the qmail-dnsrbl patch. This will avoid to be banned ourselves by spamhaus, which is free up to 100.000 queries per day.

fail2ban requires that you have a firewall as iptables active.

Downloading and installing

cd /usr/local/src
wget --no-check-certificate --output-document=fail2ban-0.x.x.tar.gz
tar xzf fail2ban-0.x.x.tar.gz
cd fail2ban-0.x.x
chown -R root:root .
./ install

Running script

In order to start the server you can use an init script that you can copy from the files/ folder of the source dir, where you can probably find one suitable for your distribution. I use this one (download here) that I don't remember where I found:

# Copyright (c) 2008-2013, Nishant Limbachia, Hoffman Estates, IL, USA
# <nishant _AT_ mnspace _DOT_ net>
# /etc/rc.d/rc.fail2ban
# start|stop|restart|reload|status|ping fail2ban server
# To start fail2ban automatically at boot, make this file executable:
# chmod 755 /etc/rc.d/rc.fail2ban
# you must also add this to rc.local for fail2ban to start during boot.

# default socket file is /var/run/fail2ban/fail2ban.sock which can be
# changed via the config file: /etc/fail2ban/fail2ban.conf

fail2ban_start() {
  if [ -x /etc/rc.d/rc.fail2ban ]; then
        echo "Starting fail2ban: "
        ### using -x option to remove any stale socket file.
        /usr/bin/fail2ban-client -x start

fail2ban_stop() {
        echo "Stopping fail2ban"
        /usr/bin/fail2ban-client stop

fail2ban_reload() {
        echo "Reloading fail2ban"
        /usr/bin/fail2ban-client reload

fail2ban_status() {
        echo "Status: fail2ban"
        /usr/bin/fail2ban-client status

fail2ban_ping() {
        echo "Pinging fail2ban"
        /usr/bin/fail2ban-client ping

case "$1" in
        sleep 5
        echo "USAGE: $0 start|stop|restart|reload|status|ping"
        exit 1

Copy in /etc/rc.d/ or wherever you want, run it and remember to launch it at boot time as well:

/etc/rc.d/rc.fail2ban start

logrotate script

You can copy a logrotate script from the install dir as follows:

cd /etc/logrotate.d
cp /usr/local/src/fail2ban-x.x.x/files/fail2ban-logrotate fail2ban

jails configuration

To understand the terminology and how fail2ban works you are invited to read the manual (quite concise and easy to read).

It is a good practice not to modify the /etc/fail2ban/*.conf files, but edit a customized file with a .local extension, which will be read by the server after each .conf to eventually overwrite the lines that you modified.

cd /etc/fail2ban
cp jail.conf jail.local

Enable the jails according to your needs.

This is what I have in my jail.local, concerning the qmail/dovecot part:

# nano /etc/fail2ban/jail.local

enabled  = true
filter   = qmail-smtp
action   = iptables[name=SMTP, port=25, protocol=tcp]
           sendmail-whois[name=SMTP, sendername="Fail2Ban SMTP"]
logpath  = /var/log/qmail/smtpd/current
maxretry = 2
bantime  = 86400
findtime = 3600

enabled  = true
filter   = qmail-smtp
action   = iptables[name=SUBMISSION, port=587, protocol=tcp]
           sendmail-whois[name=SUBMISSION, sendername="Fail2Ban SUBMISSION"]
logpath  = /var/log/qmail/submission/current
maxretry = 2
bantime  = 86400
findtime = 3600

enabled  = true
filter   = vpopmail
action   = iptables[name=VPOPMAIL, port=587, protocol=tcp]
           sendmail-whois[name=VPOPMAIL, sendername="Fail2Ban vpopmail"]
# check your syslog mail related log (mail.log in some systems)
logpath  = /var/log/maillog
maxretry = 5
bantime  = 86400
findtime = 3600

enabled  = true
filter   = qmailadmin
action   = iptables[name=QMA, port=443, protocol=tcp]
           sendmail-whois[name=QMA, sendername="Fail2Ban qmailadmin"]
logpath  = /var/log/qma-auth.log
maxretry = 4
bantime  = 86400
findtime = 3600

enabled  = true
filter   = roundcube-auth
action   = iptables[name=RC, port=80, protocol=tcp]
           sendmail-whois[name=RC, sendername="Fail2Ban RoundCube"]
logpath  = /path/to/apache/htdocs/roundcube/logs/userlogins
maxretry = 4
bantime  = 86400
findtime = 3600

enabled  = true
filter   = dovecot
action   = iptables-multiport[name=DOVECOT, port="143,993,110,995", protocol=tcp]
           sendmail-whois[name=DOVECOT, sendername="Fail2Ban dovecot"]
logpath  = /var/log/dovecot/dovecot.log
maxretry = 6
bantime  = 3600
findtime = 3600

As you can see, we have three jails, so fail2ban will look for the files qmail-smtp.conf, vpopmail.conf and dovecot.conf under the filter.d directory. I'll show the content of these files below.

The qmail-smtp jail is related to a filter named "qmail-smtp" which matches lines in the qmail-smtp log related to qmail-dnsbl and chkuser failures. The filter has to be declared in the filter.d/qmail-smtp.conf file.

Similarly, the vpopmail jail will try to ban clients trying to guess the users' password in the submission port, while the dovecot jail will do the same as far as imap/pop3 is concerned.

Here is the content of the filter files:


# nano /etc/fail2ban/filter.d/qmail-smtp.conf

# Fail2Ban filters for qmail-smtp patched for qmail-dnsbl (, chkuser ( and greetdelay
# the log shows a line like this:
# @4000000052aebcd831f42f64 qmail-smtpd: message rejected (qmail-dnsbl) ( from to localuser@domain.xy helo Wireless_Broadband_Router
# @4000000053ce3d860e9dd04c CHKUSER rejected rcpt: from <|remoteinfo/auth:|chkuser-identify:> remote <|remotehostname:unknown|remotehostip:> rcpt <localuser@domain.xy> : not existing recipient
# @4000000053fb43de062c21ac CHKUSER rejected rcpt: from <info@domain.xy|remoteinfo/auth:|chkuser-identify:> remote <|remotehostname:unknown|remotehostip:> rcpt <> : invalid rcpt MX domain
# @400000005357ecd40012e5e4 CHKUSER rejected relaying: from <yxqdi@yourdomain.xy|remoteinfo/auth:|chkuser-identify:> remote <helo:gruporga-f2de56|remotehostname:unknown|remotehostip:> rcpt <> : client not allowed to relay
# @400000005450602534f65f5c GREETDELAY from client sent data before greeting
# @40000000545076ad1de678ec GREETDELAY from client disconnected
# qmail-smtpd: read failed: (null) from to (null) helo
# @4000000055154dc40e884894 qmail-smtpd: timeout: (null) from to (null) helo
# Be aware that the following regex match only my patched chkuser at
# If you are using a standard version of chkuser you can refer to this page for the correct filter:


failregex = qmail-smtpd: message rejected \(qmail-dnsbl\) .* from <HOST>
            CHKUSER rejected rcpt: from <.*> remote <.*remotehostip:<HOST>>
            CHKUSER rejected relaying: from <.*> remote <.*remotehostip:<HOST>> .* : client not allowed to relay$
            GREETDELAY from <HOST>:
            qmail-smtpd: read failed: \(null\) from <HOST>
            qmail-smtpd: timeout: \(null\) from <HOST>

ignoreregex =

# DEV Notes:
# Author: Roberto Puzzanghera


# nano /etc/fail2ban/filter.d/vpopmail.conf


# Jul 10 12:05:53 qmail vpopmail[3076]: vchkpw-submission: vpopmail user not found helpdesk@yourdomain.xy:
# Jul 22 17:31:46 qmail vpopmail[6383]: vchkpw-submission: password fail (pass: 'dasdas') postmaster@yourdomain.xy:

failregex = vchkpw-submission: vpopmail user not found .*:<HOST>$
            vchkpw-submission: password fail .*:<HOST>$

# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
ignoreregex =

# DEV Notes:
# Author: Roberto Puzzanghera


# bans qmailadmin login attempts looking for lines like this
# 2015/05/27 15:45:58 user:postmaster@domain.xy ip: auth:failed [@domain.xy]
# qmailadmin must be patched with
# (thanks to Tony)

before = common.conf

failregex = ip:<HOST> auth:failed

ignoreregex =


The filter roundcube-auth.conf already exists, so we'll overwrite it.

# Fail2Ban configuration file for roundcube webmail
# Author: Roberto Puzzanghera
# 29/09/2014
# Log line to match
# [01-Sep-2014 00:07:11 +0200]: IMAP Error: Login failed for from AUTHENTICATE PLAIN: Authentication failed. in /usr/local/www/htdocs/roundcubemail-1.0.2/progra$

before = common.conf

failregex = IMAP Error: (FAILED login|Login failed) for .*? from <HOST>\.

ignoreregex =


The filter dovecot.conf already exists, so we'll overwrite it.

# nano /etc/fail2ban/filter.d/dovecot.local

# Fail2Ban filter Dovecot authentication and pop3/imap server
# Jul 22 23:33:29 auth-worker(27283): Info: sql(user@yourdomain.xy: Password mismatch
# Jul 22 23:33:31 imap-login: Info: Disconnected (auth failed, 1 attempts in 2 secs): user=<user@yourdomain.xy>, method=PLAIN, rip=, lip=, session=<k2t5+c7+5AAKAAAC>
# Jul 22 23:34:04 auth-worker(27283): Info: sql(adminww@yourdomain.xy: unknown user
# Jul 22 23:34:06 imap-login: Info: Disconnected (auth failed, 1 attempts in 2 secs): user=<adminww@yourdomain.xy>, method=PLAIN, rip=, lip=, session=<ONqY+87+7gAKAAAC>


failregex = Info: Disconnected \(auth failed, .* rip=<HOST>,

ignoreregex =

# DEV Notes:
# Author: Roberto Puzzanghera

Finally you may want to overwrite the file /etc/fail2ban/action.d/sendmail-common.conf just to set the recipient email address where to send the alerts

# nano /etc/fail2ban/action.d/sendmail-common.local

# Fail2Ban configuration file
# Common settings for sendmail actions


# Recipient mail address
dest = postmaster@yourdomain.xy

# Sender mail address
sender = fail2ban@yourdomain.xy


When you modify a jail, you have to reload the jails in this way:

/etc/rc.d/rc.fail2ban reload

or simply

fail2ban-client reload

Look at the jail list

# fail2ban-client status
Status: fail2ban
|- Number of jail:      3
`- Jail list:           vpopmail, qmail-smtp, dovecot

Before you turn on a jail it's always a good practice to test your newly created filter against a log file as follows:

# fail2ban-regex /var/log/qmail/smtpd/current /etc/fail2ban/filter.d/qmail-smtp.conf

Running tests


Use   failregex file : /etc/fail2ban/filter.d/qmail-smtp.conf
Use         log file : /usr/local/vservers/qmail/usr/local/var/log/qmail/smtpd/@40000000532f677b088a7854.s


Failregex: 65 total
|-  #) [# of hits] regular expression
|   1) [58] qmail-smtpd: message rejected \(qmail-dnsbl\) .* from <HOST>
|   2) [3] CHKUSER rejected rcpt: from <.*> remote <.*remotehostip:<HOST>> .* : not existing recipient$
|   3) [4] CHKUSER rejected relaying: from <.*> remote <.*remotehostip:<HOST>> .* : client not allowed to relay$

Ignoreregex: 0 total

Date template hits:
|- [# of hits] date format
|  [1596] TAI64N

Lines: 1596 lines, 0 ignored, 65 matched, 1531 missed
Missed line(s): too many to print.  Use --print-all-missed to print all 1531 lines



In /etc/fail2ban/jail.local, in the [vpopmail] section, you have:

logpath = /var/log/maillog

But in the scripts setting up qmail-submission (which is the service listening on port 587) the log file path is set to:

#!/bin/sh exec /usr/local/bin/setuidgid qmaill /usr/local/bin/multilog t /var/log/qmail/submission



/ Otto

That's correct, Otto. The failed login attempts are logged exactly there.

Sorry, I was expecting the submission service to log to that fiile, but I suppose the login comes earlier in the chain and those errors are routed to a custom location?

However, after searching through  my logs, the failed login attempts from the submission service are logged in:


(Please notice the dot).

Not sure wher this is configured?


/ Otto

You are right. The log are directed to syslog (look around line no. 679 of vchkpw.c). My slackware put all mail.* logs in /var/log/maillog, so it's worth that I clarify this point as soon as possible.

But you can manage what syslog is going to write at configure time:

--enable-logging=OPT       Log to syslog: n=nothing, e=errors only (default), y=all attempts, p=errors with passwords, v=verbose (all attempts, with passwords).
--enable-log-name=TEXT     Set syslog name vpopmail.

The second one is not very clear to me, but maybe can adjust the log file name.

As an alternative you may want to record your logs on mysql

--enable-sql-logging       Enable authentication logging to MySQL/Postgres.

but this is not convenient if you are going to use fail2ban

Hi Roberto,

qmail-smtp fail2ban is not catching ylmf-pc

@400000005682c0591bee0064 qmail-smtpd: read failed: (null) from to (null) helo ylmf-pc

What can i do to get it working?



Hi Nic,

according to my examples, fail2ban is tuned with a "qmail-smtpd: read failed:" string, not with a particular helo string. In my example there must be two such events within an hour (findtime parameter).

If you want to match other string you have to modify your qmail filter accordingly