Configure Linux firewall rules with fail2ban for SSH brute force protection and intrusion prevention

Intermediate 25 min Apr 03, 2026 42 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Set up fail2ban to automatically block SSH brute force attacks and protect your Linux server from unauthorized access attempts. Configure custom jails, firewall integration, and email notifications for comprehensive intrusion prevention.

Prerequisites

  • Root or sudo access
  • SSH service running
  • Basic Linux command line knowledge
  • Email service for notifications (optional)

What this solves

SSH brute force attacks are one of the most common threats to Linux servers, with attackers constantly scanning for weak passwords and misconfigurations. Fail2ban monitors log files for suspicious activity and automatically creates firewall rules to block repeat offenders, preventing unauthorized access attempts and reducing server load from malicious traffic. This tutorial shows you how to configure fail2ban with custom SSH protection rules, email notifications, and integration with modern Linux firewall backends like nftables and firewalld.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest security updates and dependencies.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install fail2ban and required dependencies

Install fail2ban along with email utilities for notifications and ensure the firewall backend is available.

sudo apt install -y fail2ban ufw sendmail iptables-persistent
sudo dnf install -y fail2ban firewalld sendmail iptables-services

Configure firewall backend

Enable and configure the appropriate firewall service for your distribution to work with fail2ban.

sudo ufw enable
sudo systemctl enable netfilter-persistent
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

Create fail2ban local configuration

Create a local configuration file to override default settings without modifying the original configuration files.

[DEFAULT]

Ban hosts for 1 hour (3600 seconds)

bantime = 3600

Find time window - 10 minutes

findtime = 600

Number of failures before ban

maxretry = 5

Backend for persistent bans

banaction = iptables-multiport banaction_allports = iptables-allports

Email notifications

destemail = admin@example.com sender = fail2ban@example.com mta = sendmail action = %(action_mwl)s

Whitelist your management IPs

ignoreip = 127.0.0.1/8 ::1 203.0.113.0/24 [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 3 bantime = 7200 findtime = 300

Configure distribution-specific firewall backend

Set the appropriate firewall backend based on your Linux distribution for optimal compatibility.


Ubuntu/Debian specific backend

[DEFAULT] banaction = ufw banaction_allports = ufw

RHEL/Fedora specific backend

[DEFAULT] banaction = firewallcmd-ipset banaction_allports = firewallcmd-allports

Create custom SSH filter for enhanced detection

Create a custom filter to catch additional SSH attack patterns beyond the default configuration.

[INCLUDES]
before = common.conf

[Definition]
failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|error|failed) for . from ( via \S+)?\s$
            ^%(__prefix_line)s(?:error: )?Received disconnect from : 3: .: Auth fail.$
            ^%(__prefix_line)sFailed (?:password|publickey) for . from (?: port \d)?(?: ssh\d*)?$
            ^%(__prefix_line)sROOT LOGIN REFUSED. FROM \s$
            ^%(__prefix_line)siI user . from \s$
            ^%(__prefix_line)sUser .+ from  not allowed because not listed in AllowUsers\s*$
            ^%(__prefix_line)sUser .+ from  not allowed because listed in DenyUsers\s*$
            ^%(__prefix_line)sUser .+ from  not allowed because not in any group\s*$
            ^%(__prefix_line)srefused connect from \S+ \(\)\s*$
            ^%(__prefix_line)s(?:error: )?Received disconnect from : 14: No supported authentication methods available.*$
            ^%(__prefix_line)sSSH: Server;Ltype: Authname;Remote: -\d+;Name: [^;];(?:Key|Passwd): 0;Result: Failed.$

ignoreregex = 

[Init]
journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd

Configure advanced SSH jail with custom settings

Replace the basic SSH configuration with advanced settings including the custom filter and escalating ban times.

[sshd-custom]
enabled = true
port = ssh
filter = sshd-custom
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
findtime = 300
bantime = 3600

Escalating ban times for repeat offenders

bantime.increment = true bantime.factor = 2 bantime.formula = ban.Time (1<<(ban.Count if ban.Count<20 else 20)) banFactor bantime.multipliers = 1 2 4 8 16 32 64 128 256 512 bantime.maxtime = 604800

Email alerts for this jail

action = %(action_mwl)s %(action_cf_mwl)s[cftoken="YOUR_CF_TOKEN", cfuser="admin@example.com"]

Whitelist specific IPs (add your management IPs)

ignoreip = 127.0.0.1/8 ::1 203.0.113.0/24

Custom log path for different distributions

logpath = /var/log/auth.log /var/log/secure %(sshd_log)s

Configure email notification settings

Set up detailed email notifications to alert you when bans occur and provide useful information about the attacks.

[INCLUDES]
before = mail.conf

[Definition]
actionname = mail-custom
actionstart = printf %%b "Subject: [Fail2ban] %(name)s: started on %(hostname)s
Date: LC_ALL=C date -u +"%%a, %%d %%h %%Y %%T +0000"
From: %(sender)s
To: %(dest)s

Hi,

The jail %(name)s has been started successfully.

Regards,
Fail2Ban" | /usr/sbin/sendmail -f %(sender)s %(dest)s

actionstop = printf %%b "Subject: [Fail2ban] %(name)s: stopped on %(hostname)s
Date: LC_ALL=C date -u +"%%a, %%d %%h %%Y %%T +0000"
From: %(sender)s
To: %(dest)s

Hi,

The jail %(name)s has been stopped.

Regards,
Fail2Ban" | /usr/sbin/sendmail -f %(sender)s %(dest)s

actionban = printf %%b "Subject: [Fail2ban] %(name)s: banned %(ip)s from %(hostname)s
Date: LC_ALL=C date -u +"%%a, %%d %%h %%Y %%T +0000"
From: %(sender)s
To: %(dest)s

Hi,

The IP %(ip)s has just been banned by Fail2Ban after
%(failures)s attempts against %(name)s on %(hostname)s.

Ban duration: %(bantime)s seconds
Attack details:
%(matches)s

Regards,
Fail2Ban" | /usr/sbin/sendmail -f %(sender)s %(dest)s

actionunban = printf %%b "Subject: [Fail2ban] %(name)s: unbanned %(ip)s from %(hostname)s
Date: LC_ALL=C date -u +"%%a, %%d %%h %%Y %%T +0000"
From: %(sender)s
To: %(dest)s

Hi,

The IP %(ip)s has just been unbanned from %(name)s on %(hostname)s.

Regards,
Fail2Ban" | /usr/sbin/sendmail -f %(sender)s %(dest)s

[Init]
dest = admin@example.com
sender = fail2ban@example.com
name = default

Configure web service protection jails

Add additional jails to protect web services from common attacks like HTTP auth failures and exploit attempts.

[apache-auth]
enabled = true
filter = apache-auth
logpath = /var/log/apache/error.log
maxretry = 3
bantime = 3600

[apache-badbots]
enabled = true
filter = apache-badbots
logpath = /var/log/apache/access.log
maxretry = 2
bantime = 7200

[apache-noscript]
enabled = true
filter = apache-noscript
logpath = /var/log/apache/access.log
maxretry = 6
bantime = 3600

[nginx-http-auth]
enabled = true
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600

[nginx-limit-req]
enabled = true
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 10
findtime = 60
bantime = 7200

[postfix-sasl]
enabled = true
filter = postfix-sasl
logpath = /var/log/mail.log
maxretry = 3
bantime = 3600

Set correct file permissions and ownership

Ensure fail2ban configuration files have proper permissions for security and functionality.

sudo chown -R root:root /etc/fail2ban/
sudo chmod 755 /etc/fail2ban/
sudo chmod 644 /etc/fail2ban/*.conf
sudo chmod 644 /etc/fail2ban/jail.d/*.conf
sudo chmod 644 /etc/fail2ban/filter.d/*.conf
sudo chmod 644 /etc/fail2ban/action.d/*.conf

Test configuration syntax

Validate your fail2ban configuration before starting the service to catch any syntax errors.

sudo fail2ban-client -t
sudo fail2ban-client --dp

Enable and start fail2ban service

Start fail2ban and enable it to run automatically at boot time.

sudo systemctl enable fail2ban
sudo systemctl start fail2ban
sudo systemctl status fail2ban

Verify your setup

Check that fail2ban is running properly and monitoring your SSH service.

sudo fail2ban-client status
sudo fail2ban-client status sshd-custom
sudo fail2ban-client get sshd-custom logpath
sudo tail -f /var/log/fail2ban.log

Test the SSH protection by checking if fail2ban detects authentication failures:

sudo grep "Failed password" /var/log/auth.log | tail -5
sudo fail2ban-client status sshd-custom

View current banned IPs across all jails:

sudo fail2ban-client banned

Advanced configuration and monitoring

Configure persistent ban database

Set up a database to persist banned IPs across fail2ban restarts for better long-term protection.

# Persistent ban database
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = 2592000

Create monitoring script

Create a script to monitor fail2ban status and generate reports of blocked attacks.

#!/bin/bash

echo "=== Fail2ban Status Report ==="
echo "Generated: $(date)"
echo ""

echo "Active Jails:"
fail2ban-client status | grep "Jail list" | sed 's/.*://'
echo ""

echo "Current Banned IPs:"
for jail in $(fail2ban-client status | grep "Jail list" | cut -d: -f2 | tr ',' '\n' | tr -d ' '); do
    banned=$(fail2ban-client status $jail | grep "Banned IP list" | cut -d: -f2 | tr -d ' ')
    if [ ! -z "$banned" ]; then
        echo "$jail: $banned"
    fi
done
echo ""

echo "Recent Ban Activity (last 24 hours):"
grep "$(date +'%Y-%m-%d')" /var/log/fail2ban.log | grep "Ban" | tail -10
echo ""

echo "Top Attacking IPs:"
grep "Ban" /var/log/fail2ban.log | awk '{print $NF}' | sort | uniq -c | sort -nr | head -10

Make monitoring script executable

Set proper permissions for the monitoring script and test its functionality.

sudo chmod 755 /usr/local/bin/fail2ban-report.sh
sudo /usr/local/bin/fail2ban-report.sh
Security Note: Never use chmod 777 on fail2ban configuration files. The correct permissions are 644 for configuration files and 755 for directories, ensuring only root can modify security settings while allowing the service to read them.

Common issues

SymptomCauseFix
Fail2ban won't startConfiguration syntax errorsudo fail2ban-client -t to check syntax
SSH jail not workingWrong log pathCheck /var/log/auth.log exists or use /var/log/secure
IPs not getting bannedFirewall backend mismatchSet correct banaction for your firewall (ufw/firewalld)
Email notifications not workingSendmail not configuredsudo systemctl status sendmail and configure SMTP
Own IP got bannedNot in whitelistAdd IP to ignoreip and sudo fail2ban-client unban YOUR_IP
Bans not persistingNo database configuredEnable dbfile in jail.local configuration
High false positivesToo low maxretryIncrease maxretry value and adjust findtime window

Next steps

Automated install script

Run this to automate the entire setup

#fail2ban #ssh security #brute force protection #linux firewall #intrusion prevention

Need help?

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.

Talk to an engineer