AlmaLinux

Install and Configure Fail2ban on Rocky Linux 10 / AlmaLinux 10

Install and Configure Fail2ban on Rocky Linux 10 / AlmaLinux 10

Fail2ban is an intrusion prevention tool that monitors log files for failed authentication attempts and automatically bans offending IP addresses using firewall rules. It is one of the first security tools you should deploy on any internet-facing Linux server, protecting services like SSH, Nginx, Apache, and mail servers from brute-force attacks.

This guide covers a full Fail2ban setup on Rocky Linux 10, AlmaLinux 10, and RHEL 10 – from installation through SSH protection, custom jails for web and mail services, email notifications, and monitoring. Every command is tested against the current EPEL packages for EL10.

What is Fail2ban

Fail2ban is an open-source log-parsing application written in Python. It scans log files (such as /var/log/secure or systemd journal) for patterns that indicate failed login attempts, password guessing, or other malicious behavior. When a configured threshold is reached, Fail2ban executes an action – typically adding a firewall rule through firewalld or nftables to block the offending IP address for a set period.

The core concept in Fail2ban is the jail. Each jail combines a filter (regex patterns that match log entries) with an action (what happens when the threshold is hit). You can have separate jails for SSH, Nginx, Postfix, and any other service – each with its own ban time, retry limit, and notification settings.

Prerequisites

  • A server running Rocky Linux 10, AlmaLinux 10, or RHEL 10
  • Root or sudo access
  • Firewalld installed and running (default on all three distros)
  • Internet access for package installation

Verify that firewalld is active before proceeding:

sudo systemctl status firewalld

The output should show active (running). If firewalld is not running, start and enable it with sudo systemctl enable --now firewalld.

Step 1: Install Fail2ban from EPEL

Fail2ban is not included in the default Rocky Linux / AlmaLinux repositories. It ships through the EPEL (Extra Packages for Enterprise Linux) repository.

Enable the EPEL repository:

sudo dnf install -y epel-release

On RHEL 10, use the following instead since epel-release is not available directly:

sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm

Install Fail2ban along with the firewalld integration package:

sudo dnf install -y fail2ban fail2ban-firewalld

The fail2ban-firewalld package provides the ban action that integrates with firewalld instead of raw iptables. This is the recommended setup on RHEL-family systems where firewalld is the default firewall manager.

Step 2: Start and Enable Fail2ban

Enable and start the Fail2ban service so it survives reboots:

sudo systemctl enable --now fail2ban

Verify the service is running:

sudo systemctl status fail2ban

You should see the service as active with no errors in the output:

● fail2ban.service - Fail2Ban Service
     Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled; preset: disabled)
     Active: active (running) since Fri 2026-03-21 10:15:32 UTC; 5s ago
       Docs: man:fail2ban(1)
    Process: 1234 ExecStartPre=/bin/mkdir -p /run/fail2ban (code=exited, status=0/SUCCESS)
   Main PID: 1235 (fail2ban-server)
      Tasks: 3 (limit: 23060)
     Memory: 12.4M
        CPU: 245ms
     CGroup: /system.slice/fail2ban.service
             └─1235 /usr/bin/python3 -s /usr/bin/fail2ban-server -xf start

Step 3: Understanding Fail2ban Configuration

Fail2ban stores all configuration under /etc/fail2ban/. The directory structure looks like this:

  • /etc/fail2ban/jail.conf – the main default configuration file. Never edit this directly – it gets overwritten on package updates
  • /etc/fail2ban/jail.local – your custom configuration. Settings here override anything in jail.conf
  • /etc/fail2ban/jail.d/ – drop-in directory for per-jail configuration files
  • /etc/fail2ban/filter.d/ – filter definitions containing regex patterns for matching log entries
  • /etc/fail2ban/action.d/ – action definitions that specify what happens when a ban is triggered

The key parameters you will configure in most jails:

ParameterDescription
bantimeHow long an IP stays banned (seconds, or use suffix like 1h, 1d)
findtimeThe time window in which maxretry failures must occur to trigger a ban
maxretryNumber of failures allowed within findtime before banning
banactionThe firewall action to use for banning (firewallcmd-rich-rules for firewalld)
ignoreipSpace-separated list of IPs/CIDRs that are never banned
backendHow Fail2ban reads logs – systemd for journal, auto for log files
enabledSet to true to activate a jail

The fail2ban-firewalld package installs /etc/fail2ban/jail.d/00-firewalld.conf which sets banaction = firewallcmd-rich-rules and banaction_allports = firewallcmd-rich-rules. This makes all jails use firewalld by default.

Step 4: Configure SSH Protection

SSH brute-force attacks are the most common threat on any public-facing server. Create a jail.local file with default settings and an SSH jail.

Create the configuration file:

sudo vi /etc/fail2ban/jail.local

Add the following configuration:

[DEFAULT]
# Ban for 1 hour (3600 seconds)
bantime = 1h

# Look for failures within a 10-minute window
findtime = 10m

# Ban after 5 failed attempts
maxretry = 5

# Use systemd journal as log backend
backend = systemd

# Never ban these IPs (add your own management IPs)
ignoreip = 127.0.0.1/8 ::1 10.0.1.0/24

[sshd]
enabled = true
port = ssh
filter = sshd
maxretry = 3
bantime = 6h
findtime = 10m

The [DEFAULT] section applies to all jails unless overridden. The [sshd] jail is stricter – only 3 failed attempts are allowed before a 6-hour ban. Replace 10.0.1.0/24 in ignoreip with your actual management network or IP so you never lock yourself out.

If you have changed your SSH port, update the port value to match. For example, port = 2222.

Restart Fail2ban to apply the configuration:

sudo systemctl restart fail2ban

Verify the sshd jail is active:

sudo fail2ban-client status

The output confirms how many jails are running and lists them by name:

Status
|- Number of jail:	1
`- Jail list:	sshd

Step 5: Test SSH Protection

Test the sshd jail by deliberately failing SSH logins from another machine. Attempt to log in with a wrong password multiple times until the ban threshold is reached.

After the failed attempts, check the sshd jail status:

sudo fail2ban-client status sshd

The output shows the number of failed attempts and any currently banned IPs:

Status for the jail: sshd
|- Filter
|  |- Currently failed:	1
|  |- Total failed:	4
|  `- Journal matches:	_SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
   |- Currently banned:	1
   |- Total banned:	1
   `- Banned IP list:	192.168.1.50

You can also verify the firewalld rules that Fail2ban created. The banned IP appears as a rich rule in firewalld:

sudo firewall-cmd --list-rich-rules

The output shows a reject rule for the banned IP address:

rule family="ipv4" source address="192.168.1.50" port port="22" protocol="tcp" reject type="icmp-port-unreachable"

You can also check the Fail2ban log for ban events:

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

Look for lines containing Ban to confirm the action was taken:

2026-03-21 10:25:14,123 fail2ban.actions [1235]: NOTICE [sshd] Ban 192.168.1.50

Step 6: Protect Additional Services

Fail2ban ships with filters for dozens of services out of the box. Add jails for your running services by appending them to /etc/fail2ban/jail.local.

Nginx Jails

Protect Nginx against HTTP authentication brute-force and bot scanners. Add these jails to your jail.local:

sudo vi /etc/fail2ban/jail.local

Append the following Nginx jail configuration:

[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 1h

[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 1d

The nginx-http-auth jail catches failed HTTP basic authentication attempts. The nginx-botsearch jail catches bots probing for common vulnerability paths like /wp-admin, /phpmyadmin, and similar URLs that return 404 errors.

Apache Jails

For servers running Apache (httpd), add these jails:

[apache-auth]
enabled = true
port = http,https
filter = apache-auth
logpath = /var/log/httpd/error_log
maxretry = 3
bantime = 1h

[apache-badbots]
enabled = true
port = http,https
filter = apache-badbots
logpath = /var/log/httpd/access_log
maxretry = 2
bantime = 1d

Note the log paths – on RHEL-family systems, Apache logs to /var/log/httpd/ with error_log and access_log (no .log extension).

Postfix and Dovecot Jails

If you run a mail server with Postfix and Dovecot, protect against SMTP and IMAP brute-force attacks:

[postfix]
enabled = true
port = smtp,465,submission
filter = postfix
maxretry = 5
bantime = 1h

[postfix-sasl]
enabled = true
port = smtp,465,submission,imap,imaps,pop3,pop3s
filter = postfix[mode=auth]
maxretry = 3
bantime = 6h

[dovecot]
enabled = true
port = pop3,pop3s,imap,imaps,submission,465,sieve
filter = dovecot[mode=aggressive]
maxretry = 3
bantime = 6h

The postfix-sasl jail specifically targets SASL authentication failures, which are common in credential-stuffing attacks against mail servers. The dovecot jail uses aggressive mode to catch more authentication failure patterns.

WordPress Login Protection

WordPress does not have a built-in Fail2ban filter. You need to create a custom one. First, create the filter file:

sudo vi /etc/fail2ban/filter.d/wordpress.conf

Add the following filter definition that matches failed WordPress login attempts from the access log:

[Definition]
failregex = ^ .* "POST /wp-login\.php
            ^ .* "POST /xmlrpc\.php
ignoreregex =

Then add the WordPress jail to your jail.local:

[wordpress]
enabled = true
port = http,https
filter = wordpress
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 5m
bantime = 1h

Change logpath to /var/log/httpd/access_log if you use Apache instead of Nginx.

After adding all the jails you need, restart Fail2ban:

sudo systemctl restart fail2ban

Verify all jails are loaded:

sudo fail2ban-client status

The jail list should show all the jails you enabled:

Status
|- Number of jail:	4
`- Jail list:	nginx-botsearch, nginx-http-auth, sshd, wordpress

Step 7: Custom Filters

When a service does not have a built-in filter, you can write your own. A custom filter is a file in /etc/fail2ban/filter.d/ that contains regex patterns matching failed authentication lines in log files.

Here is an example custom filter for a web application that logs failed logins to its own log file. Create the filter:

sudo vi /etc/fail2ban/filter.d/myapp.conf

Add the filter definition with regex patterns that match your application’s log format:

[Definition]
# Match lines like: 2026-03-21 10:15:32 Authentication failure from 192.168.1.50 for user admin
failregex = ^.* Authentication failure from  for user .*$
            ^.* Failed login attempt from .*$

ignoreregex = ^.* Authentication failure from  for user root$

The <HOST> tag is a special Fail2ban placeholder that captures the IP address from the log line. The ignoreregex section excludes specific patterns you do not want to trigger a ban.

Before activating the filter, test it against your log file using the fail2ban-regex tool:

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

The output shows how many lines matched and which IPs were captured. This confirms your regex patterns work before you deploy the jail:

Running tests
=============

Use   failregex filter file : myapp, basedir: /etc/fail2ban
Use         log file : /var/log/myapp/auth.log
Use         encoding : UTF-8

Results
=======

Failregex: 12 total
|-  #) [# of hits] regular expression
|   1) [8] ^.* Authentication failure from  for user .*$
|   2) [4] ^.* Failed login attempt from .*$
`-

Ignoreregex: 2 total

Date template hits:
|- [# of hits] date format
|  [14] {^LN-BEG}ExYear(?P<_sep>[-/.])Month(?P=_sep)Day 24hour:Minute:Second
`-

Lines: 14 lines, 0 ignored, 12 matched, 2 missed

Once the regex matches correctly, add the jail to your jail.local:

[myapp]
enabled = true
port = http,https
filter = myapp
logpath = /var/log/myapp/auth.log
maxretry = 5
bantime = 1h

Step 8: Unban an IP Address

If a legitimate user or your own IP gets banned, you can remove the ban immediately.

Unban a specific IP from a specific jail:

sudo fail2ban-client set sshd unbanip 192.168.1.50

Unban an IP from all jails at once:

sudo fail2ban-client unban 192.168.1.50

Unban every currently banned IP across all jails:

sudo fail2ban-client unban --all

To prevent accidental bans on your own IPs, always add your management IPs to the ignoreip setting in the [DEFAULT] section of jail.local.

Step 9: Email Notifications

Fail2ban can send email alerts when it bans an IP. This requires a working mail setup on the server – either a local MTA like Postfix or Sendmail.

Install the sendmail MTA if you do not have Postfix running:

sudo dnf install -y sendmail sendmail-cf
sudo systemctl enable --now sendmail

Update the [DEFAULT] section in your jail.local with email settings:

sudo vi /etc/fail2ban/jail.local

Add or update these parameters in the [DEFAULT] section:

[DEFAULT]
# Email settings
destemail = [email protected]
sender = [email protected]
mta = sendmail

# Action with email notification including whois and log lines
action = %(action_mwl)s

The action_mwl action bans the IP, sends an email with whois information, and includes the relevant log lines that triggered the ban. Other action options:

  • %(action_)s – ban only, no notification
  • %(action_mw)s – ban and send email with whois info
  • %(action_mwl)s – ban and send email with whois info plus log lines

For whois lookups in the email alerts, install the whois package:

sudo dnf install -y whois

Restart Fail2ban after changing the configuration:

sudo systemctl restart fail2ban

Step 10: Monitor Fail2ban

Check the overall Fail2ban status and all active jails:

sudo fail2ban-client status

Get detailed status for a specific jail, including banned IPs and failure counts:

sudo fail2ban-client status sshd

View Fail2ban’s own log file for ban and unban events:

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

Use journalctl to check Fail2ban’s systemd journal entries:

sudo journalctl -u fail2ban -f

The -f flag follows the log in real time, which is useful when testing jails.

Check how many IPs a specific jail has banned in total:

sudo fail2ban-client get sshd banip --with-time

This displays each banned IP along with the time it was banned and when the ban expires.

Fail2ban Command Reference

The following table covers the most common fail2ban-client commands for daily administration:

CommandDescription
fail2ban-client statusShow all active jails
fail2ban-client status JAILShow details for a specific jail (banned IPs, failure count)
fail2ban-client set JAIL unbanip IPUnban a specific IP from a jail
fail2ban-client unban IPUnban an IP from all jails
fail2ban-client unban --allUnban all currently banned IPs
fail2ban-client set JAIL banip IPManually ban an IP in a jail
fail2ban-client get JAIL bantimeShow the current ban time for a jail
fail2ban-client get JAIL maxretryShow the max retry value for a jail
fail2ban-client get JAIL findtimeShow the find time for a jail
fail2ban-client get JAIL ignoreipShow ignored IPs for a jail
fail2ban-client set JAIL addignoreip IPAdd an IP to the ignore list at runtime
fail2ban-client reloadReload all jails and configuration
fail2ban-client reload JAILReload a specific jail
fail2ban-client startStart the Fail2ban server
fail2ban-client stopStop the Fail2ban server
fail2ban-client get JAIL banip --with-timeList banned IPs with ban and expiry times
fail2ban-client pingCheck if the Fail2ban server is alive

Troubleshooting

Here are the most common Fail2ban issues and how to fix them.

Fail2ban fails to start

Check the journal for error details:

sudo journalctl -u fail2ban -e --no-pager

Common causes include syntax errors in jail.local (missing brackets, wrong indentation) or a filter file referencing a non-existent log path. Fix the configuration and restart.

Jail is configured but not activating

Verify the jail appears in the status output:

sudo fail2ban-client status

If a jail is missing, check that enabled = true is set and that the filter file exists in /etc/fail2ban/filter.d/. Also verify the logpath points to an existing file. For jails using the systemd backend, make sure the service generates journal entries.

Firewalld conflicts

If Fail2ban bans are not working despite showing in fail2ban-client status, check that firewalld is running and that the fail2ban-firewalld package is installed. Verify rich rules are present:

sudo firewall-cmd --list-rich-rules

If rules are missing, check that /etc/fail2ban/jail.d/00-firewalld.conf exists and contains the correct banaction. If you had previously set banaction = iptables-multiport in jail.local, remove that line and let the firewalld drop-in take precedence.

Log path issues

On RHEL-family systems with systemd, many services log to the journal instead of traditional log files. If a jail is not detecting failures, switch the backend to systemd:

[sshd]
enabled = true
backend = systemd

For services that still use traditional log files (Nginx, Apache), verify the log path exists and that Fail2ban has read permission. Check with:

sudo ls -la /var/log/nginx/error.log

If SELinux is blocking Fail2ban from reading log files, check for AVC denials:

sudo ausearch -m AVC -ts recent

You may need to set the correct SELinux context on custom log files or create a policy module to allow Fail2ban access.

Conclusion

Fail2ban is now protecting your Rocky Linux 10 / AlmaLinux 10 server against brute-force attacks on SSH and any other services you configured. The Fail2ban wiki has additional filter examples and advanced configuration options for more specialized setups.

For production servers, consider setting longer ban times (24h or more) for repeat offenders, enabling incremental banning with bantime.increment = true, and monitoring the Fail2ban log as part of your regular server health checks.

Related Articles

AlmaLinux Configure Firewalld on Rocky Linux 10 / AlmaLinux 10 / RHEL 10 AlmaLinux Configure Zabbix 5 Agent on Rocky Linux 8 | AlmaLinux 8 AlmaLinux Install Jellyfin Media Server on RHEL | Rocky | Alma | CentOS | Oracle Linux CentOS Boot CentOS / RHEL / Fedora into Emergency Recovery

Press ESC to close