On Ubuntu servers, ufw (Uncomplicated Firewall) is a great tool to setup firewall rules on ports without having to use
iptables commands which are very difficult.
root@vpsfrsqlpac2$ ufw status verbose
To Action From -- ------ ---- 22 ALLOW IN Anywhere # SSH 8600:8800/tcp ALLOW IN 10.128.30.32 # MariaDB CS 20501:20509/tcp ALLOW IN 10.128.30.32 # PostgreSQL
But that’s not enough. For SSH services, one can define another port than the default one (port 22), but unfortunately
some products, MariaDB ColumnStore for example, use
ssh in their administration routines without
allowing the ability to override the default port 22.
Thus, the port 22 must be opened and in the file
/var/log/auth.log we notice many connections attempts coming
from unknown machines, sometimes with very short time intervals between each attempt from an IP.
Feb 2 06:25:42 vpsfrsqlpac2 sshd: Disconnected from invalid user tester 188.8.131.52 port 42416 [preauth] Feb 2 06:29:32 vpsfrsqlpac2 sshd: Invalid user tester from 184.108.40.206 port 41670 Feb 2 06:29:34 vpsfrsqlpac2 sshd: Failed password for invalid user tester from 220.127.116.11 port 41670 ssh2 Feb 2 06:29:34 vpsfrsqlpac2 sshd: Disconnected from invalid user tester 18.104.22.168 port 41670 [preauth] Feb 2 06:35:50 vpsfrsqlpac2 sshd: Invalid user tester from 22.214.171.124 port 45178 Feb 2 06:35:52 vpsfrsqlpac2 sshd: Failed password for invalid user tester from 126.96.36.199 port 45178 ssh2
Fail2Ban is the complementary tool to ufw in order to protect an Ubuntu system from these attacks.
- Its installation is easy.
- Fail2Ban manages
iptablesrules in order to ban IP addresses during a period following configurable conditions.
Fail2Ban is a python package and can be installed in a Python virtual environment, that’s the case in this article.
The installation must be performed with
root privileges as this tool manages
Preparing the Python virtual environment
Python 3.8 with
virtualenv is installed in the directory
/opt/python/.python-3.8 sources the needed environment variables :
export PYHOME=/opt/python/python-3.8 export PATH=$PYHOME/bin:$PATH export LD_LIBRARY_PATH=$PYHOME/lib:$LD_LIBRARY_PATH export PYTHONPATH=/opt/python/packages
The Python virtual environment for fail2ban is installed in the directory
root@vpsfrsqlpac2$ source /opt/python/.python-3.8 root@vpsfrsqlpac2$ virtualenv --system-site-packages /opt/monitoring/fail2ban
The environment is activated :
root@vpsfrsqlpac2$ source /opt/monitoring/fail2ban/bin/activate (fail2ban) root@vpsfrsqlpac2$ which python3
Download fail2ban from GitHub and run the installation :
(fail2ban) root@vpsfrsqlpac2$ cd /opt/monitoring/setup (fail2ban) root@vpsfrsqlpac2$ wget https://github.com/fail2ban/fail2ban/archive/0.11.1.tar.gz (fail2ban) root@vpsfrsqlpac2$ tar -xvzf 0.11.1.tar.gz (fail2ban) root@vpsfrsqlpac2$ cd fail2ban-0.11.1 (fail2ban) root@vpsfrsqlpac2$ python3 setup.py install
… changing mode of /opt/monitoring/fail2ban/bin/fail2ban-testcases to 755 Please do not forget to update your configuration files. They are in "/etc/fail2ban/". You can also install systemd service-unit file from "build/fail2ban.service" resp. corresponding init script from "files/*-initd".
The package fail2ban is installed successfully in the virtual environment
All configuration files are installed in the directory
The main configuration file
fail2ban.conf does not contain many parameters to configure. In this one, we can
customize verbosity, directories and filenames for the log file, the pid file…
[DEFAULT] loglevel = INFO logtarget = /var/log/fail2ban.log socket = /var/run/fail2ban/fail2ban.sock pidfile = /var/run/fail2ban/fail2ban.pid dbfile = /var/lib/fail2ban/fail2ban.sqlite3 dbpurgeage = 15d
Fail2ban uses a sqlite3 database. When installing, update the retention parameter
dbpurgeage to at least 8 days,
it will be already done when configuring later the recidivists attacks.
Fail2Ban rules are configured in the file
Copy the file
/etc/fail2ban/jail.local. The local file prevents
loosing any configuration when upgrading fail2ban :
(fail2ban) root@vpsfrsqlpac2$ cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
[DEFAULT] ignoreip = 127.0.0.1/8 10.128.31.48 # "bantime" is the number of seconds that a host is banned. bantime = 30m # A host is banned if it has generated "maxretry" during the last "findtime" seconds. findtime = 10m # "maxretry" is the number of failures before a host get banned. maxretry = 3
- define the IPs to ignore in the
DEFAULTsection, especially its machines IPs.
- the default banishment time may be adjusted : here it is set to 30 minutes instead of the default 10 minutes.
Now banishment rules for
ssh are enabled :
[sshd] enabled = true port = ssh logpath = %(sshd_log)s backend = %(sshd_backend)s
Many sections are available and predefined in the file
jail.local : sshd, apache, mysql…
All the parameters defined in the section
be customized in each section. Take care about the parameter
ignoreip, if specific IP address is defined in the child,
it will override the default value, no merge is performed.
In the "fail2ban language", a section in the configuration file is a jail. The jail
sshd has been enabled in the
On Ubuntu the IP tables
iptables are not stored in a file, but in the kernel. Fail2ban will manage
iptables, so a backup, in case of, should be performed before starting fail2ban :
(fail2ban) root@vpsfrsqlpac2$ iptables-save > /etc/iptables_rules.txt
If unexpected behaviours occur after Fail2ban activation, the restore to a stable state will then be easy :
(fail2ban) root@vpsfrsqlpac2$ iptables-restore < /etc/iptables_rules.txt
Everything is ready, to start fail2ban :
(fail2ban) root@vpsfrsqlpac2$ fail2ban-server -xf start
2020-02-07 10:08:11,078 fail2ban.server : INFO -------------------------------------------------- 2020-02-07 10:08:11,078 fail2ban.server : INFO Starting Fail2ban v0.11.1 2020-02-07 10:08:11,079 fail2ban.server : INFO Daemon started 2020-02-07 10:08:11,080 fail2ban.observer : INFO Observer start... 2020-02-07 10:08:11,137 fail2ban.database : INFO Connected to fail2ban persistent database '/var/lib/fail2ban/fail2ban.sqlite3' 2020-02-07 10:08:11,140 fail2ban.database : WARNING New database created. Version '4'
To stop, restart the server or to reload the configuration globally or just for a jail :
(fail2ban) root@vpsfrsqlpac2$ fail2ban-client stop | restart | reload [jail]
fail2ban-client and jails
fail2ban-client to manage jails : status, unban IPs, start, stop…
Banned IPs appear in the jail
sshd shortly after activation :
(fail2ban) root@vpsfrsqlpac2$ fail2ban-client status
Status |- Number of jail: 2 `- Jail list: recidive, sshd
(fail2ban) root@vpsfrsqlpac2$ fail2ban-client status sshd
Status for the jail: sshd |- Filter | |- Currently failed: 3 | |- Total failed: 2022 | `- File list: /var/log/auth.log `- Actions |- Currently banned: 4 |- Total banned: 466 `- Banned IP list: 188.8.131.52 184.108.40.206…
Jails configuration parameters can be listed and modified dynamically :
(fail2ban) root@vpsfrsqlpac2$ fail2ban-client get sshd bantime
(fail2ban) root@vpsfrsqlpac2$ fail2ban-client set sshd bantime 60m
fail2ban-client set only updates the values for a running Fail2Ban server,
do not forget to update the configuration file accordingly if they have to be persistent.
First command to know with Fail2ban (when you ban yourself 🙂) : how to unban an IP ?
(fail2ban) root@vpsfrsqlpac2$ fail2ban-client set sshd unbanip 220.127.116.11
Many options exist : for help, run
fail2ban and IP Tables
How Fail2ban works with
Fail2ban creates for each jail a user chain with the name
f2b-sshd in the above example, then a rule
is added in this chain for port 22 / INPUT :
(fail2ban) root@vpsfrsqlpac2$ iptables -S
-N f2b-sshd -A INPUT -p tcp -m multiport --dports 22 -j f2b-sshd
Banned IPs are appended in the chain :
(fail2ban) root@vpsfrsqlpac2$ iptables -L f2b-sshd
Chain f2b-sshd (1 references) target prot opt source destination REJECT all -- 18.104.22.168 anywhere reject-with icmp-port-unreachable REJECT all -- 22.214.171.124 anywhere reject-with icmp-port-unreachable REJECT all -- 126.96.36.199 anywhere reject-with icmp-port-unreachable REJECT all -- 188.8.131.52 anywhere reject-with icmp-port-unreachable REJECT all -- 184.108.40.206 anywhere reject-with icmp-port-unreachable
The chain is removed when stopping the Fail2ban server or stopping the jail (
fail2ban-client stop sshd).
As expected, the chain is not removed when Fail2ban server is killed (
No loss when restarting the Fail2ban server, iptables chains are rebuilt based on the informations stored in the sqlite3 database.
(fail2ban) root@vpsfrsqlpac2$ sqlite3
sqlite> .open /var/lib/fail2ban/fail2ban.sqlite3 sqlite> .tables
bans bips fail2banDb jails logs
sqlite> select ip, jail, datetime(timeofban,'unixepochs'), bantime from bips;
220.127.116.11|sshd|2020-02-07 16:17:03|3600 18.104.22.168|sshd|2020-02-07 16:19:25|3600 22.214.171.124|sshd|2020-02-07 16:24:28|3600 …
fail2ban is installed in a Python virtual environment but it does not prevent defining a service for the automatic startup.
.fail2ban is created for sourcing and activating the Python virtual environment.
export F2BDIR=/opt/monitoring/fail2ban source /opt/python/.python-3.8 source $F2BDIR/bin/activate
A template file for the service is available in the directory from which the installation has been performed (
The service is customized by integrating the call to the file
[Unit] Description=Fail2Ban Service Documentation=man:fail2ban(1) After=network.target iptables.service firewalld.service ip6tables.service ipset.service nftables.service PartOf=iptables.service firewalld.service ip6tables.service ipset.service nftables.service [Service] Type=simple ExecStartPre=/bin/mkdir -p /run/fail2ban ExecStart=/bin/bash -c "source /opt/monitoring/fail2ban/.fail2ban; /opt/monitoring/fail2ban/bin/fail2ban-server -xf start" ExecStop=/bin/bash -c "source /opt/monitoring/fail2ban/.fail2ban; /opt/monitoring/fail2ban/bin/fail2ban-client stop" ExecReload=/bin/bash -c "source /opt/monitoring/fail2ban/.fail2ban; /opt/monitoring/fail2ban/bin/fail2ban-client reload" PIDFile=/run/fail2ban/fail2ban.pid Restart=on-failure RestartPreventExitStatus=0 255 [Install] WantedBy=multi-user.target
The service is then enabled :
root@vpsfrsqlpac2$ cd /etc/systemd/system root@vpsfrsqlpac2$ ln -fs /lib/systemd/system/fail2ban.service fail2ban.service root@vpsfrsqlpac2$ systemctl enable fail2ban
Created symlink /etc/systemd/system/multi-user.target.wants/fail2ban.service → /lib/systemd/system/fail2ban.service.
root@vpsfrsqlpac2$ systemctl start fail2ban root@vpsfrsqlpac2$ systemctl status fail2ban
● fail2ban.service - Fail2Ban Service Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2020-02-09 17:23:29 CET; 23h ago Docs: man:fail2ban(1) Process: 4240 ExecStop=/bin/bash -c source /opt/monitoring/fail2ban/.fail2ban; /opt/monitoring/fail2ban/bin/fail2ban-clien Process: 4250 ExecStartPre=/bin/mkdir -p /run/fail2ban (code=exited, status=0/SUCCESS) Main PID: 4259 (bash) Tasks: 8 (limit: 4587) CGroup: /system.slice/fail2ban.service ├─4259 /bin/bash -c source /opt/monitoring/fail2ban/.fail2ban; /opt/monitoring/fail2ban/bin/fail2ban-server -xf s └─4264 /opt/monitoring/fail2ban/bin/python3 /opt/monitoring/fail2ban/bin/fail2ban-server -xf start
Jail for recidivists
A very interesting jail to setup : the
[recidive] enabled = true logpath = /var/log/fail2ban.log banaction = %(banaction_allports)s bantime = 1w findtime = 1d
When activating this jail, Fail2ban scans its own log file searching for recurrent IPs banned by the other defined jail rules.
The IPs are then banned for a longer period (here 1 week) for ALL input ports. That’s why the retention parameter
dbpurgeage has been
set to 15 days, otherwise the jail
recidive would not properly work.
root@vpsfrsqlpac2$ iptables -S
… -N f2b-recidive -A INPUT -p tcp -j f2b-recidive … -A f2b-recidive -s 126.96.36.199/32 -j REJECT --reject-with icmp-port-unreachable -A f2b-recidive -s 188.8.131.52/32 -j REJECT --reject-with icmp-port-unreachable …
Some other jails seem to be of interest, especially jails detecting floods and injections over Apache / PHP, but it should be studied deeper.
The first opened jails (
recidive) are largely enough for the moment when trying to protect better
the sshd port of a Linux Ubuntu server located on the internet network.
About performances : fail2ban configured with
recidive jails needs 450 - 650 Mb. Its CPU usage
is very low (less than 0,2% on average on a 1 Core machine 2 GHz). Number of rules created in
iptables should not degrade