Configure Fail2Ban on a VM

About Fail2Ban

Fail2Ban is a log-parsing tool that scans log files for specific patterns and, using the firewall present in the system, temporarily bans IP addresses that show malicious signs, such as frequent failed login attempts. It is commonly used to protect services like SSH, databases, and web applications from brute-force attacks.

All virtual machine instances deployed on INFN Cloud already have the SSH connection monitored and protected by a local Fail2Ban service. However, it is recommended to install and configure Fail2Ban on your VM to protect additional services that you may deploy on it, such as web servers or databases.

In this guide, we will walk you through the steps to install and configure Fail2Ban on your VM, providing some basic examples on how to set it up for commonly adopted services.

Install Fail2Ban

Fail2Ban is available in the default repositories of most Linux distributions and should be already installed in your VM. If not, you can easily install it by using the commands listed in the following subsections, depending on the Linux distribution present on the VM.

Debian/Ubuntu

sudo apt update
sudo apt install fail2ban

AlmaLinux/RHEL-based

sudo dnf update
sudo dnf install fail2ban

Check or Start Fail2Ban service

After installing Fail2Ban, the corresponding systemd unit should be started automatically. You can check the status of the service using the following command:

sudo systemctl status fail2ban

If the service is not running, you can manually start it with:

sudo systemctl enable --now fail2ban

In subsequent boots of the VM, the Fail2Ban service should start automatically.

Configure Fail2Ban

Fail2Ban configuration is organized into "jails", which are specific instructions for each service. Each jail defines the log file to monitor, the patterns to look for in logs, and the actions to take when a pattern is matched (e.g., ban an IP address).

Fail2Ban uses a set of configuration files to define how it operates:

  • /etc/fail2ban/fail2ban.conf is the main configuration file for the Fail2Ban daemon;
  • /etc/fail2ban/jail.conf is the main jail configuration file and it should not be manually modified.
  • the /etc/fail2ban/jail.local file should be used for custom configurations, overriding settings in jail.conf.
  • the /etc/fail2ban/filter.d/ directory contains filter definitions that specify the patterns to look for in log files.
  • the /etc/fail2ban/action.d/ directory contains actions that can be taken when a pattern is matched, such as to ban an IP address.

To configure Fail2Ban, you should create or modify the /etc/fail2ban/jail.local file. This file allows you to customize the settings for your jails without modifying the default jail.conf file, which may be overridden across updates and is maintained by the upstream developers.

sudo nano /etc/fail2ban/jail.local

Global Configuration

Before configuring specific services, you can set global defaults in the [DEFAULT] section of the /etc/fail2ban/jail.local file, that will apply to all jails unless overridden by a specific jail configuration file. Here are the most important general configuration options:

[DEFAULT]

# Engine settings

# Ban duration - how long an IP address is banned
bantime = 1h
# Can also use: 10m (10 minutes), 1d (1 day), 1w (1 week)
# If no time unit is specified, it defaults to seconds
# Use -1 for permanent bans

# Time window to count failures, this defines the time period during which failures are counted
findtime = 10m

# Number of failures before banning a certain IP address
maxretry = 5

# Whitelist trusted IP addresses and networks
ignoreip = 127.0.0.1/8 ::1
# Add your trusted IP ranges here to prevent self-lockout

# Action to take when banning
action = %(action_)s
# Options:
# %(action_)s - just ban the IP
# %(action_mw)s - ban and send email with whois info
# %(action_mwl)s - ban, send email with whois and log lines

# Log level
loglevel = INFO
# Options: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG

# Log target
logtarget = /var/log/fail2ban.log
# Where to write Fail2Ban logs

For more advanced configurations, you can also set:

[DEFAULT]

# Maximum number of lines to process per check
maxlines = 10000

# Backend to use for log monitoring
backend = systemd
# Options: auto, systemd, polling, pyinotify, gamin

# Enable/disable DNS lookups for hostnames
usedns = warn
# Options: yes, no, warn (log warnings for DNS issues)

# Action that will be used when the threshold is reached
banaction = iptables
# This should correspond to a defined action inside the /etc/fail2ban/action.d/ directory,
# such as iptables.conf or another custom action file.

# Ban time increment
bantime.increment = true
# Progressively increase ban times for repeat offenders
bantime.factor = 1
# Multiplier for ban time increases
bantime.multipliers = 1 2 4 8 16 32 64
# Sequence of multipliers for repeat offenses
bantime.maxtime = 1w
# Maximum ban time regardless of increments

# Retry limits
bantime.rndtime = 60
# Add random time (0-60 seconds) to ban duration
bantime.overalljails = false
# Whether ban time increments apply across all jails

Configuration tips:

  • Test with short ban times: start with shorter ban times (like 2m) while testing your configuration
  • Don't lock yourself out: always include your own IP or network in ignoreip. You can check the IP address your VM sees your PC connecting from by checking the $SSH_CONNECTION environment variable from the remote shell on the VM
  • Monitor logs: check /var/log/fail2ban.log regularly to ensure everything works as expected
  • Progressive banning: use bantime.increment = true to increase ban times for repeated offenders

Jails

Fail2Ban uses "jails" to define how to monitor specific services. Each jail can have its own settings, such as the log file to monitor, the filter to apply, and the actions to take when a pattern is matched.

Here is a basic example of a jail configuration:

[service-name]
enabled = true
port = <service port>
filter = <filter name>
logpath = <path to log file to monitor>
maxretry = <number of failures>
bantime = <duration of ban>
findtime = <time window for failures>
banaction = <action to take when banning>

The options are the same as the global settings defined in the [DEFAULT] section, but they can be overridden inside a jail section for each service to monitor.

Filters

Filters are used to define the patterns that Fail2Ban will look for in log files. Each filter is defined in a separate file located in the /etc/fail2ban/filter.d/ directory.

You can create custom filters or use existing ones. The filter files contain regular expressions that match log entries indicating failed login attempts or other suspicious activity.

You can create a custom filter file like the following:

sudo nano /etc/fail2ban/filter.d/custom-filter.conf

Add the following content to define a simple filter:

[Definition]
failregex = <your-regex-pattern>
ignoreregex =

Finally, test the custom filters you created by using the fail2ban-regex tool prior to production use:

sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/myfilter.conf

If you want to know more about how to write custom filters, you can refer to the official Fail2Ban documentation. You can then reference this filter in your jail configuration:

[custom-service]
enabled = true
port = <service-port>
...
filter = custom-filter
...

Actions

Actions define what happens when a pattern is matched. Fail2Ban can ban IP addresses, send email notifications, or execute custom scripts. You can define custom actions in the /etc/fail2ban/action.d/ directory. The default actions include banning IPs using iptables, sending emails, and more.

You can create a custom action file like this:

sudo nano /etc/fail2ban/action.d/custom-action.conf

Add the following content to define a simple action:

[Definition]
actionstart = <command-to-run-on-start>
actionstop = <command-to-run-on-stop>
actioncheck = <command-to-check-status>
actionban = <command-to-ban-ip>
actionunban = <command-to-unban-ip>

You can then reference this action in your jail configuration:

[custom-service]
enabled = true
...
action = custom-action
...

Example Configurations

Here are some example configurations services like SSH, MySQL, and Traefik, that are commonly deployed in INFN-Cloud. If the service you want to protect is running in a container, you should bind-mount the log file or directory to monitor accordingly to the path defined the logpath field in each jail.

SSH

SSH is one of the most common targets for brute-force attacks. Here's how to configure Fail2Ban to protect SSH.

Edit your /etc/fail2ban/jail.local file to add the following lines:

[sshd]
enabled = true
port = ssh
filter = sshd
maxretry = 3
banaction = iptables
bantime = 1h
findtime = 1d
logpath = %(sshd_log)s

MySQL/MariaDB Configuration

To protect your MySQL database from brute-force attacks add these lines to your /etc/fail2ban/jail.local file:

[mysqld-auth]
enabled = true
filter = mysqld-auth
port = 3306
logpath = /var/log/mysql/error.log
maxretry = 5
bantime = 1h
findtime = 10m

A custom filter for MySQL should be already present in the /etc/fail2ban/filter.d/mysqld-auth.conf path, otherwise you can create it.

sudo nano /etc/fail2ban/filter.d/mysqld-auth.conf

with the following content:

[INCLUDES]

# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf

[Definition]

_daemon = mysqld

failregex = ^%(__prefix_line)s(?:(?:\d{6}|\d{4}-\d{2}-\d{2})[ T]\s?\d{1,2}:\d{2}:\d{2} )?(?:>

ignoreregex =

Note

Make sure your MySQL server is configured to log authentication failures. If not, add the following lines to your MySQL configuration (/etc/mysql/my.cnf or /etc/mysql/mysql.conf.d/mysqld.cnf):

ini [mysqld] log-error = /var/log/mysql/error.log log-warnings = 2

Traefik Configuration

Traefik is a popular reverse proxy and load balancer. You can use fail2ban to monitor the access log and ban undesired attempt to access a service protected by authentication or block bots trying to scan the services exposed by Traefik.

[traefik-auth]
enabled = true
filter = traefik-auth
port = http,https
logpath = /var/log/traefik/access.log
maxretry = 5
bantime = 1h
findtime = 10m

[traefik-botsearch]
enabled = true
filter = traefik-botsearch
port = http,https
logpath = /var/log/traefik/access.log
maxretry = 3
bantime = 24h
findtime = 10m

To use 'traefik-auth' filter you have to configure your Traefik instance to write the access logs as described in Traefik's logs documentation page into a log file on host and specify users for Basic Authentication as described in Traefik's basic auth documentation page.

The traefik-auth filter should be already present in the filter.d folder, if that is not the case you can create it

sudo nano /etc/fail2ban/filter.d/traefik-auth.conf

with the following content:

[Definition]

# Parameter "method" can be used to specifiy request method
req-method = \S+
# Usage example (for jail.local):
#   filter = traefik-auth[req-method="GET|POST|HEAD"]

failregex = ^<HOST> \- <usrre-<mode>> \[\] \"(?:<req-method>) [^\"]+\" 401\b

ignoreregex =

# Parameter "mode": normal (default), ddos or aggressive
# Usage example (for jail.local):
#   [traefik-auth]
#   mode = aggressive
#   # or another jail (rewrite filter parameters of jail):
#   [traefik-auth-ddos]
#   filter = traefik-auth[mode=ddos]
#
mode = normal

# part of failregex matches user name (must be available in normal mode, must be empty in dd>
usrre-normal = (?!- )<F-USER>\S+</F-USER>
usrre-ddos = -
usrre-aggressive = <F-USER>\S+</F-USER>

Next, you need to create the bot search filter for Traefik:

sudo nano /etc/fail2ban/filter.d/traefik-botsearch.conf

with the following content:

[INCLUDES]
before = botsearch-common.conf

[Definition]
failregex = ^<HOST> \- \S+ \[\] \"(GET|POST|HEAD) \/<block> \S+\" 404 .+$

This filter will import the instructions included in the default botsearch-common.conf (which allows to use the <block> tag) and uses them to define the failregex.

Test and Apply Configuration

After creating or modifying the configuration files, you should check the syntax of your configuration before applying it. You can do this by running:

sudo fail2ban-client -t

If there are no syntax errors, you can then restart Fail2Ban to apply the changes:

sudo systemctl restart fail2ban

and check it is running:

sudo systemctl status fail2ban

Ensure Log Files Exist

Before starting Fail2Ban, ensure that the log files it needs to monitor actually exist, otherwise the service will not start.

If the service does not start and you receive errors, you can see details in the logs using the command:

sudo journalctl -u fail2ban -f

Verify Active Jails

To see which jails are active:

sudo fail2ban-client status

To check the status of a specific jail:

sudo fail2ban-client status sshd
sudo fail2ban-client status mysqld-auth
sudo fail2ban-client status traefik-auth

Manual Testing

You can manually test your configuration by attempting failed logins and checking if the IP gets banned:

# Check current banned IPs for SSH
sudo fail2ban-client status sshd

# Manually ban an IP (for testing)
sudo fail2ban-client set sshd banip <IP_ADDRESS>

# Manually unban an IP
sudo fail2ban-client set sshd unbanip <IP_ADDRESS>

Check Logs

Monitor Fail2Ban logs to see what's happening:

sudo tail -f /var/log/fail2ban.log

Important Notes

  • Log Paths: make sure the log paths in your configuration match the actual log file locations on your system
  • Traefik Logs: ensure Traefik is configured to write access logs to the specified path
  • MySQL Logs: verify that MySQL error logging is enabled and writing to the correct location
  • Firewall Integration: Fail2Ban works with your system's firewall (iptables, firewalld, etc.)
  • Regular Monitoring: regularly check Fail2Ban logs and status to ensure it's working properly