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, especially 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
- Jul 15, 2023
- the installation and the configuration has been revised in order to work on
python2is missing (tx Gabriel Torres)
- Nov 20, 2022
- switched all actions to
nftables, as it has now replaced
fail2banhas support for it. Just replace "iptables" with "nftables" in your jails.
- Nov 18, 2022
- fail2ban upgraded to v. 1.0.2
- jails now have a different action's declaration (iptables[type=multiport] instead of iptables-multiport)
- added a short note on how to configure the server with a network bridge
Downloading and installing
On Debian you may want to install these packages before installing
apt-get install python3-pyinotify python3-systemd 2to3
Now proceed to the installation:
cd /usr/local/src wget --no-check-certificate https://github.com/fail2ban/fail2ban/archive/1.0.2.tar.gz --output-document=fail2ban-1.0.2.tar.gz tar xzf fail2ban-1.0.2.tar.gz cd fail2ban-1.0.2 chown -R root:root .
setup script is based on
python2. If your system is fine with
python2 then go on with it:
If your system only has
Debian) you have to run
fail2ban-2to3 before the setup:
python3 setup.py install
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 a very simple one (download here); I don't remember where I found it.
Download it in /usr/local/bin or wherever you want, run it and remember to launch it at boot time as well:
cd /usr/local/bin wget https://notes.sagredo.eu/files/qmail/fail2banctl chmod +x fail2banctl fail2banctl start
You can copy a logrotate script from the install dir as follows:
cp files/fail2ban-logrotate /etc/logrotate.d/fail2ban
Fail2Ban main configuration file fail2ban.local
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. Furthermore, the .local files should contain only the modified lines, to avoid that, after an upgrade, obsolete or incompatible settings are kept and continue to overwrite the corresponding .conf. In what follows I'll assume that you have created a .local file to overwrite the .conf file that we are talking about.
To understand the terminology and how
fail2ban works you are invited to read the manual (quite concise and easy to read).
This is the only option I touched with respect to the original .conf file:
[Definition] allowipv6 = auto
Enable the jails according to your needs. This is what I have in my jail.local, concerning the
[qmail-smtp] enabled = true filter = qmail-smtp action = nftables[type=multiport, name=SMTP, port="25,465,587"] sendmail-whois-lines[name=SMTP, logpath="%(logpath)s"] logpath = /var/log/qmail/smtpd/current maxretry = 5 bantime = 1d findtime = 1h [qmail-submission] enabled = true filter = qmail-smtp action = nftables[type=multiport, name=SUBMISSION, port="25,465,587"] sendmail-whois-lines[name=SUBMISSION, logpath="%(logpath)s"] logpath = /var/log/qmail/submission/current maxretry = 5 bantime = 1d findtime = 1h [vpopmail] enabled = true filter = vpopmail action = nftables[type=multiport, name=VPOPMAIL, port="25,465,587"] sendmail-whois-lines[name=VPOPMAIL, logpath="%(logpath)s"] # check your syslog mail related log (mail.log in some systems) logpath = /var/log/maillog maxretry = 5 bantime = 86400 findtime = 3600 [qmailadmin] enabled = true filter = qmailadmin action = nftables[type=multiport, name=QMA, port="80,443"] sendmail-whois-lines[name=QMA, logpath="%(logpath)s"] logpath = /var/log/qma-auth.log maxretry = 4 bantime = 1d findtime = 1h [roundcube-auth] enabled = true filter = roundcube-auth action = nftables[type=multiport, name=RC, port="80,443"] sendmail-whois-lines[name=RC, logpath="%(logpath)s"] logpath = /var/www/roundcube/logs/userlogins.log maxretry = 4 bantime = 1d findtime = 1h [dovecot-pop3] enabled = true filter = dovecot action = nftables[type=multiport, name=POP3, port="993,995"] sendmail-whois-lines[name=POP3, logpath="%(logpath)s"] logpath = /var/log/dovecot/dovecot.log maxretry = 6 bantime = 1h findtime = 1h [dovecot-imap] enabled = true filter = dovecot action = nftables[type=multiport, name=IMAP, port="993,995"] sendmail-whois-lines[name=IMAP, logpath="%(logpath)s"] logpath = /var/log/dovecot/dovecot.log maxretry = 6 bantime = 1h findtime = 1h
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-smtpd log mainly related to the
qlogenvelope line, which records almost all kind of rejects. The filter has to be declared in the filter.d/qmail-smtp.conf file.
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.
Remember to add your server's IP and any other trusted IP in the DEFAULT section, just to avoid to ban yourself expecially in case someone is imitating your own IP (spoofing):
[DEFAULT] # # MISCELLANEOUS OPTIONS # # "ignoreip" can be an IP address, a CIDR mask or a DNS host. Fail2ban will not # ban a host which matches an address in this list. Several addresses can be # defined using space separator. ignoreip = 127.0.0.1/8 10.0.0.1/8 <my-external-ip>
Here is the content of the filter files:
# Fail2Ban filters for qmail-smtp patched for qmail-dnsbl (http://qmail-dnsbl.sourceforge.net), chkuser (http://opensource.interazioni.it/qmail/chkuser.html) and greetdelay # # Here is an example of log lines that this filter is going to hit: # # @40000000545076ad1de678ec GREETDELAY from 220.127.116.11: client sent data before greeting # @4000000055154dc40e884894 qmail-smtpd: timeout: (null) from 18.104.22.168 to (null) helo host220-227-149-62.serverdedicati.aruba.it # # All lines concerning chkuser, qmail-dnsbl and others like these are now catched by the qlogenvelope line: # # @4000000059f5194706e649ec CHKUSER accepted sender: from <email@example.com|remoteinfo/auth:|chkuser-identify:> remote <helo:free-112-191.mediaworksit.net|remotehostname:unknown|remotehostip:22.214.171.124> rcpt <> : sender accepted # @4000000059f519470be7b0fc CHKUSER accepted rcpt: from <firstname.lastname@example.org|remoteinfo/auth:|chkuser-identify:> remote <helo:free-112-191.mediaworksit.net|remotehostname:unknown|remotehostip:126.96.36.199> rcpt <email@example.com> : found existing recipient # @4000000059f519470be860c4 qmail-smtpd: rcptcheck: checking <firstname.lastname@example.org> at 188.8.131.52 # @4000000059f519470c084ca4 qmail-smtpd: rcptcheck: ignore address <email@example.com> at 184.108.40.206 # @4000000059f5195c1f6d7e7c qmail-smtpd: rbl: ip=220.127.116.11 query=18.104.22.168.zen.spamhaus.org result=ignore message='' # @4000000059f5195c211f1294 qmail-smtpd: rbl: ip=22.214.171.124 query=126.96.36.199.b.barracudacentral.org result=reject message='Client host blocked using Barracuda Reputation, see http://www.barracudanetworks.com/reputation/?r=1&ip=188.8.131.52' # @4000000059f5195c211f2234 qlogenvelope: result=rejected code=553 reason=rblreject detail=b.barracudacentral.org helo=free-112-191.mediaworksit.net firstname.lastname@example.org email@example.com relay=no rcpthosts=yes size= authuser= authtype= encrypted= sslverified=no localip=10.0.0.4 localport=25 remoteip=184.108.40.206 remoteport=15630 remotehost= qp= pid=20003 # 2022-02-18 16:23:03.719762500 helo-dns-check: blocked with: HELO doesn't match IP [220.127.116.11] # # Be aware that the following regex match only my patched chkuser at https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html # If you are using a standard version of chkuser you can refer to this page for the correct filter: http://wiki.qmailtoaster.com/index.php/Fail2Ban [Definition] failregex = qlogenvelope: result=rejected .* remoteip=<HOST> helo-dns-check: blocked with: .* \[<HOST>\] GREETDELAY from <HOST>: client sent data before greeting qmail-smtpd: reject \(auth not available\): \(null\) from <HOST> ignoreregex = # DEV Notes: # # Author: Roberto Puzzanghera
[Definition] # Jul 10 12:05:53 qmail vpopmail: vchkpw-submission: vpopmail user not found firstname.lastname@example.org:18.104.22.168 # Jul 22 17:31:46 qmail vpopmail: vchkpw-submission: password fail (pass: 'dasdas') email@example.com:22.214.171.124 failregex = vchkpw-submission: .* 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:firstname.lastname@example.org ip:126.96.36.199 auth:failed [@domain.xy] # qmailadmin must be patched with http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/qmailadmin/qmailadmin-1.2.16-log.patch # (thanks to Tony) [INCLUDES] before = common.conf [Definition] 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 # 15/07/2022 # # Log line to match (the 1st one in case of rc behind a firewall) # [12-Jul-2022 08:56:39 +0200]: <3lq5onb8> Failed login for postmaster from 10.0.0.2 (X-Forwarded-For: 188.8.131.52) in session 3lq5onb87b7oqnc7 (error: 1) # [01-Sep-2014 00:07:11 +0200]: IMAP Error: Login failed for email@example.com from 184.108.40.206. AUTHENTICATE PLAIN: Authentication failed. in /usr/local/www/htdocs/roundcubemail-1.0.2/progr> [INCLUDES] before = common.conf [Definition] failregex = Failed login for from .* \(X-Forwarded-For: <HOST>\) IMAP Error: (FAILED login|Login failed) for .*? from <HOST>\. ignoreregex =
The filter dovecot.conf already exists, so we'll overwrite it.
# Fail2Ban filter Dovecot authentication and pop3/imap server # # Jul 22 23:33:29 auth-worker(27283): Info: sql(firstname.lastname@example.org:220.127.116.11): Password mismatch # Jul 22 23:33:31 imap-login: Info: Disconnected (auth failed, 1 attempts in 2 secs): user=<email@example.com>, method=PLAIN, rip=18.104.22.168, lip=22.214.171.124, session=<k2t5+c7+5AAKAAAC> # Jul 22 23:34:04 auth-worker(27283): Info: sql(firstname.lastname@example.org:126.96.36.199): unknown user # Jul 22 23:34:06 imap-login: Info: Disconnected (auth failed, 1 attempts in 2 secs): user=<email@example.com>, method=PLAIN, rip=188.8.131.52, lip=184.108.40.206, session=<ONqY+87+7gAKAAAC> [Definition] failregex = \(\S*,<HOST>(?:,\S*)?\): (?:unknown user|invalid credentials|Password mismatch) ignoreregex = # DEV Notes: # # Author: Roberto Puzzanghera
Locate to the action.d directory and modify the action you are using to your needs, always copying the original .conf file in a .local
Create a action.d/nftables-common.local file that we'll use to overwrite nftables.conf.
I did the following modification to ban all protocols as default, instead of banning the only
protocol = tcp,udp,udplite,sctp
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
# Fail2Ban configuration file # # Common settings for sendmail actions [Init] # Recipient mail address # dest = firstname.lastname@example.org # Sender mail address # sender = email@example.com
When you modify something, you have to reload the jails in this way:
Look at the jail list
# fail2ban-client status Status: fail2ban Status |- 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 : /firstname.lastname@example.org Results ======= Failregex: 65 total |- #) [# of hits] regular expression | 1)  qmail-smtpd: message rejected \(qmail-dnsbl\) .* from | 2)  CHKUSER rejected rcpt: from <.*> remote <.*remotehostip:> .* : not existing recipient$ | 3)  CHKUSER rejected relaying: from <.*> remote <.*remotehostip:> .* : client not allowed to relay$ `- Ignoreregex: 0 total Date template hits: |- [# of hits] date format |  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
fail2ban with a network bridge
In case your services are in a localnet and the packets to be filtered go through a network bridge, then your
nftables rules must be written into the
FORWARD chain instead of the
INPUT one, like
fail2ban normally does.
Modify your nftables-common.local action as follows
# Option: chain_hook # Notes.: refers to the kind of chain to be created # Values: [ prerouting | input | forward | output | postrouting ] Default: input # chain_hook = forward # was input