Practical Guide to Defending Against Brute‑Force Attacks with fail2ban
This step‑by‑step guide explains how fail2ban monitors logs, matches brute‑force patterns, and automatically bans malicious IPs for SSH, HTTP Basic Auth, MySQL, and custom services on Ubuntu 24.04 or Rocky Linux 9.5, including installation, configuration, testing, best practices, and troubleshooting.
Overview
Brute‑force attacks target services such as SSH, web logins, and databases by repeatedly trying passwords until a correct credential is found. Public honeypot data shows a newly exposed Linux server receives its first attempt within five minutes and thousands of scans daily.
fail2ban Working Principle
fail2ban is an intrusion‑prevention framework that watches log files, applies regular‑expression filters, counts matches, and triggers firewall actions (iptables or nftables) when a threshold is exceeded. The core concepts are:
Filter : defines regex patterns that identify failed authentication attempts.
Jail : a monitoring unit that ties a filter to an action and parameters such as bantime, findtime, and maxretry.
Action : the command executed on a match, typically adding a DROP rule to the firewall.
Comparison with Other Tools
fail2ban : works on any log format, highly configurable, but depends on real‑time log writes.
DenyHosts : lightweight, SSH‑only, no longer maintained.
SSHGuard : multi‑protocol, low resource usage, but less flexible filter syntax.
CrowdSec : adds community threat intelligence, requires network sync.
Firewall rate‑limiting : does not distinguish legitimate from malicious traffic.
Environment Requirements
OS: Ubuntu 24.04 LTS or Rocky Linux 9.5 (kernel 6.12+).
fail2ban version 1.1.x (Python 3.12+).
Python 3.12+, iptables/nftables (built‑in), optional firewalld (Rocky 9.5).
Log sources: auth.log, /var/log/nginx/*, /var/log/mysql/error.log, etc.
Installation
# Ubuntu 24.04
sudo apt update
sudo apt install -y fail2ban
# Rocky Linux 9.5 (EPEL required)
sudo dnf install -y epel-release
sudo dnf install -y fail2ban fail2ban-firewalld
# Verify version
fail2ban-client versionConfiguration File Structure
/etc/fail2ban/
├── fail2ban.conf # global defaults (read‑only)
├── fail2ban.local # overrides for global defaults
├── jail.conf # default jail definitions (read‑only)
├── jail.local # all custom jail settings
├── jail.d/ # optional fragment files
├── filter.d/ # regex filter definitions
├── action.d/ # action scripts
└── paths-*.conf # distro‑specific pathsCore principle: never edit .conf files; place all custom settings in the corresponding .local files.
Basic Configuration (jail.local)
[DEFAULT]
# Global defaults
bantime = 3600 # 1 hour
findtime = 600 # 10‑minute window
maxretry = 5
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 604800
backend = systemd
banaction = iptables-multiport
banaction_allports = iptables-allportsSSH Protection
[sshd]
enabled = true
port = ssh
filter = sshd[mode=aggressive]
maxretry = 3
findtime = 300
bantime = 3600The built‑in sshd filter in aggressive mode matches password failures, public‑key failures, invalid users, and session timeouts.
Nginx HTTP Basic Auth Protection
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
findtime = 300
bantime = 3600Custom Nginx Bad‑Bot Filter
# /etc/fail2ban/filter.d/nginx-badbots.conf
[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD).*(\.php|\.asp|\.aspx|\.jsp|\.cgi|\.env|wp-login|wp-admin|phpmyadmin|\.git|\.svn|config\.|\.bak|\.sql|shell|eval|base64).*" (400|403|404|444)
^<HOST> .* "(GET|POST) /" [0-9]+ [0-9]+ "-" ".*(masscan|zgrab|python-requests|Go-http-client|Scrapy|curl/|wget/).*"
ignoreregex = [nginx-badbots]
enabled = true
port = http,https
filter = nginx-badbots
logpath = /var/log/nginx/access.log
maxretry = 3
findtime = 60
bantime = 86400Custom Nginx CC (Rate‑Limiting) Filter
# /etc/fail2ban/filter.d/nginx-cc.conf
[Definition]
failregex = ^<HOST> .* "(GET|POST|PUT|DELETE) .*" (429|503) .*$
limiting requests, excess: .* by zone .*, client: <HOST>
ignoreregex = [nginx-cc]
enabled = true
port = http,https
filter = nginx-cc
logpath = /var/log/nginx/error.log /var/log/nginx/access.log
maxretry = 30
findtime = 60
bantime = 600Custom Application Log Filter (API Auth)
# /etc/fail2ban/filter.d/api-auth.conf
[Definition]
failregex = ^\s*WARN\s+\[auth-service\]\s+-\s+Authentication failed:.*ip=<HOST>.*$
ignoreregex =
datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S [api-auth]
enabled = true
port = 8080,8443
filter = api-auth
logpath = /var/log/myapp/auth.log
maxretry = 10
findtime = 300
bantime = 1800Firewalld / nftables Integration
# For Rocky Linux (firewalld)
[DEFAULT]
banaction = firewallcmd-rich-rules
banaction_allports = firewallcmd-rich-rules
# For nftables
[DEFAULT]
banaction = nftables-multiport
banaction_allports = nftables-allportsService Management and Verification
# Test configuration syntax
sudo fail2ban-client -t
# Reload after changes
sudo fail2ban-client reload
# Check overall status
sudo fail2ban-client status
# Check specific jail (e.g., sshd)
sudo fail2ban-client status sshd
# View active iptables chain
sudo iptables -L f2b-sshd -n -vBest Practices and Pitfalls
Maintain a comprehensive ignoreip whitelist that includes localhost, internal networks, bastion hosts, and monitoring IPs to avoid accidental lock‑out of legitimate users.
Use incremental bans ( bantime.increment = true) so repeated offenders receive exponentially longer bans (1 h → 2 h → 4 h … up to 7 days).
Test every new filter with fail2ban-regex against real log samples before deployment.
Rotate large log files with logrotate and keep findtime as small as practical to reduce memory usage.
When running Docker, monitor host logs rather than container logs, or mount container logs into the host for fail2ban to read.
Common Errors and Solutions
fail2ban fails to start : syntax error in jail.local. Run fail2ban-client -t to locate the problem.
Filter does not match logs : regex mistake or log format mismatch. Use fail2ban-regex to debug.
IP remains reachable after ban : iptables rule order places ACCEPT rules before the fail2ban chain. Adjust chain priority.
Whitelist missing entries : legitimate admin IPs get banned. Add them to ignoreip using CIDR notation.
Ban data lost on reboot : ensure dbpurgeage is set appropriately or increase bantime.
Monitoring and Metrics
fail2ban exposes metrics such as fail2ban_currently_banned, fail2ban_total_failed, and fail2ban_total_banned. A simple Bash exporter can write these metrics to a Prometheus textfile collector, enabling alerts for unusually high ban rates or missing metrics.
Backup and Recovery
# Backup configuration
BACKUP_DIR="/opt/backup/fail2ban"
DATE=$(date +%Y%m%d)
mkdir -p "$BACKUP_DIR"
tar czf "$BACKUP_DIR/fail2ban_config_${DATE}.tar.gz" \
/etc/fail2ban/jail.local \
/etc/fail2ban/jail.d/ \
/etc/fail2ban/filter.d/*local* \
/etc/fail2ban/action.d/*local*
# Backup SQLite database
cp /var/lib/fail2ban/fail2ban.sqlite3 "$BACKUP_DIR/fail2ban_db_${DATE}.sqlite3"
# Keep 30 days
find "$BACKUP_DIR" -mtime +30 -delete # Restore
sudo tar xzf /opt/backup/fail2ban/fail2ban_config_20260313.tar.gz -C /
sudo systemctl stop fail2ban
sudo cp /opt/backup/fail2ban/fail2ban_db_20260313.sqlite3 /var/lib/fail2ban/fail2ban.sqlite3
sudo systemctl start fail2ban
sudo fail2ban-client statusConclusion
fail2ban provides a lightweight, flexible, and well‑documented solution for mitigating brute‑force attacks across SSH, web services, databases, and custom applications. By following the installation steps, structuring configuration files correctly, testing filters, and applying incremental ban policies, operators can significantly reduce exposure to credential‑guessing attacks while maintaining visibility through logs and metrics.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Raymond Ops
Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
