Fail2ban is an intrusion prevention software framework that protects servers from brute-force attacks by monitoring log files and banning IP addresses that show malicious patterns. This comprehensive guide will walk you through setting up, configuring, and optimizing Fail2ban on AlmaLinux to create a robust automated defense system against various attack vectors.
Understanding Fail2ban
What is Fail2ban?
Fail2ban is a log-parsing application that monitors system logs for suspicious activity and automatically bans IP addresses that exhibit malicious behavior. It works by:
- Monitoring log files for failed authentication attempts
- Detecting patterns that indicate malicious activity
- Taking action by updating firewall rules to block offending IPs
- Unbanning IPs after a specified time period
Key Components
- Filters: Regular expressions that identify malicious patterns in logs
- Jails: Service-specific configurations that apply filters to log files
- Actions: Commands executed when a filter matches (typically firewall bans)
- Backend: The log monitoring system (polling, gamin, systemd)
How Fail2ban Works
Log File → Filter (Regex) → Detection → Action (Ban) → Unban (After Time)
↑ ↓
└───────────────────── Continuous Monitoring ←─────────────┘
Installing Fail2ban on AlmaLinux
Prerequisites
# Update system
sudo dnf update -y
# Install EPEL repository
sudo dnf install -y epel-release
# Ensure firewalld is installed and running
sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
Installation
# Install fail2ban and dependencies
sudo dnf install -y fail2ban fail2ban-systemd
# Additional tools
sudo dnf install -y fail2ban-firewalld fail2ban-sendmail
# Verify installation
fail2ban-client --version
# Check package contents
rpm -ql fail2ban | head -20
Initial Setup
# Enable and start fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Check status
sudo systemctl status fail2ban
# View fail2ban process
ps aux | grep fail2ban
Directory Structure
/etc/fail2ban/
├── action.d/ # Action configurations
├── fail2ban.conf # Main configuration
├── filter.d/ # Filter definitions
├── jail.conf # Default jail configurations
├── jail.d/ # Custom jail configurations
├── jail.local # Local jail overrides
└── paths-*.conf # Log file path definitions
/var/log/fail2ban.log # Fail2ban log file
/var/lib/fail2ban/ # Fail2ban database
Basic Configuration
Main Configuration File
# Create local configuration (never edit .conf files directly)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# Edit local configuration
sudo nano /etc/fail2ban/jail.local
Essential Global Settings
[DEFAULT]
# Ban duration (seconds)
bantime = 3600
# Time window for maxretry
findtime = 600
# Number of failures before ban
maxretry = 5
# Ignore IP addresses
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24
# Email notifications
destemail = [email protected]
sender = [email protected]
mta = sendmail
# Action to take
action = %(action_mwl)s
# Backend for log monitoring
backend = systemd
Understanding Configuration Options
# Ban time options
bantime = 1h # 1 hour
bantime = 1d # 1 day
bantime = 1w # 1 week
bantime = -1 # Permanent ban
# Find time options
findtime = 10m # 10 minutes
findtime = 1h # 1 hour
findtime = 1d # 1 day
# Action options
action = %(action_)s # Ban only
action = %(action_mw)s # Ban and email
action = %(action_mwl)s # Ban, email with logs
action = %(action_xarf)s # Ban with X-ARF report
Backend Configuration
# Available backends
backend = auto # Auto-detect
backend = systemd # For systemd journal
backend = polling # Traditional log files
backend = gamin # File alteration monitor
# Backend-specific options
[DEFAULT]
# For systemd backend
backend = systemd
journalmatch = _SYSTEMD_UNIT=sshd.service
# For polling backend
backend = polling
logencoding = utf-8
Working with Jails
Understanding Jails
Jails are the core concept in Fail2ban. Each jail:
- Monitors specific log files
- Uses specific filters
- Takes specific actions
- Has individual settings
Enabling Basic Jails
# SSH jail
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
maxretry = 3
bantime = 1h
# HTTP authentication
[apache-auth]
enabled = true
port = http,https
filter = apache-auth
logpath = %(apache_error_log)s
maxretry = 5
# Nginx
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = %(nginx_error_log)s
maxretry = 3
Creating Custom Jails
Create /etc/fail2ban/jail.d/custom.local
:
# Custom application jail
[myapp]
enabled = true
filter = myapp
port = 8080
logpath = /var/log/myapp/access.log
maxretry = 5
findtime = 10m
bantime = 30m
action = %(action_mwl)s
# Multiple ports
[multi-port-app]
enabled = true
filter = multi-port
port = 8080,8081,8082
logpath = /var/log/app/*.log
maxretry = 10
# Custom ban action
[aggressive-ssh]
enabled = true
filter = sshd
port = ssh
logpath = %(sshd_log)s
maxretry = 2
findtime = 1d
bantime = 1w
Jail Status Management
# View all jails
sudo fail2ban-client status
# View specific jail status
sudo fail2ban-client status sshd
# Start/stop jail
sudo fail2ban-client start sshd
sudo fail2ban-client stop sshd
# Reload jail
sudo fail2ban-client reload sshd
# Add IP to jail manually
sudo fail2ban-client set sshd banip 192.168.1.100
# Unban IP
sudo fail2ban-client set sshd unbanip 192.168.1.100
Creating Custom Filters
Filter Basics
Create /etc/fail2ban/filter.d/myapp.conf
:
[Definition]
# Failure regex
failregex = ^<HOST> .* "POST /login HTTP/.*" 401
^Failed login from <HOST>
^Authentication failed for .* from <HOST>
# Ignore regex
ignoreregex =
# Date pattern
datepattern = %%Y-%%m-%%d %%H:%%M:%%S
# Log prefix pattern
prefregex = ^\s*\[<F-TIMESTAMP>%%Y-%%m-%%d %%H:%%M:%%S</F-TIMESTAMP>\]
Advanced Filter Examples
WordPress Filter
# /etc/fail2ban/filter.d/wordpress.conf
[Definition]
failregex = ^<HOST> .* "POST .*wp-login\.php HTTP/.*" 200
^<HOST> .* "POST .*xmlrpc\.php HTTP/.*" 200
Authentication failure for .* from <HOST>
Blocked authentication attempt for .* from <HOST>
Pingback error .* generated from <HOST>
ignoreregex =
# Multiline support
maxlines = 2
API Rate Limiting Filter
# /etc/fail2ban/filter.d/api-rate-limit.conf
[Definition]
# Match API calls exceeding rate limits
failregex = ^<HOST> .* "(?:GET|POST) /api/.* HTTP/.*" 429
^Rate limit exceeded for <HOST>
^<HOST>.*Too many requests
ignoreregex = ^<HOST> .* "(?:GET|POST) /api/health.* HTTP/.*"
datepattern = %%Y-%%m-%%d %%H:%%M:%%S
Custom Application Filter
# /etc/fail2ban/filter.d/custom-app.conf
[INCLUDES]
before = common.conf
[Definition]
_daemon = myapp
# Multiple failure patterns
failregex = ^%(__prefix_line)sAuthentication failed for .* from <HOST>$
^%(__prefix_line)sInvalid credentials from <HOST>$
^%(__prefix_line)sFailed (?:password|login) for .* from <HOST>$
^%(__prefix_line)sConnection refused from <HOST>$
# Date extraction
datepattern = ^[^\[]*\[({DATE})
{^LN-BEG}
# Additional options
journalmatch = _SYSTEMD_UNIT=myapp.service
Testing Filters
# Test filter against log file
sudo fail2ban-regex /var/log/secure sshd
# Test custom filter
sudo fail2ban-regex /var/log/myapp/access.log /etc/fail2ban/filter.d/myapp.conf
# Verbose testing
sudo fail2ban-regex -v /var/log/nginx/access.log nginx-noscript
# Test with specific date pattern
sudo fail2ban-regex --datepattern "%%Y-%%m-%%d %%H:%%M:%%S" /var/log/custom.log myfilter
Configuring Actions
Understanding Actions
Actions define what happens when an IP is banned. Common actions include:
- Firewall rules (iptables, firewalld, nftables)
- Email notifications
- Custom scripts
- External service integration
Basic Action Configuration
# /etc/fail2ban/action.d/custom-firewall.conf
[Definition]
actionstart = echo "Starting fail2ban jail %(name)s" >> /var/log/fail2ban-actions.log
actionstop = echo "Stopping fail2ban jail %(name)s" >> /var/log/fail2ban-actions.log
actioncheck = firewall-cmd --state
actionban = firewall-cmd --add-rich-rule='rule family="ipv4" source address="<ip>" drop'
echo "Banned <ip> in jail %(name)s" >> /var/log/fail2ban-actions.log
actionunban = firewall-cmd --remove-rich-rule='rule family="ipv4" source address="<ip>" drop'
echo "Unbanned <ip> from jail %(name)s" >> /var/log/fail2ban-actions.log
Email Notification Actions
# Enhanced email notifications
[Definition]
actionstart =
actionstop =
actioncheck =
actionban = printf %%b "Subject: [Fail2ban] %(name)s: banned <ip>
From: %(sender)s
To: %(destemail)s
Hi,
The IP <ip> has just been banned by Fail2ban after
<failures> attempts against %(name)s.
Additional Info:
Jail: %(name)s
Failures: <failures>
Log Messages:
<matches>
Regards,
Fail2ban" | /usr/sbin/sendmail -f %(sender)s %(destemail)s
actionunban =
Custom Script Actions
# /etc/fail2ban/action.d/custom-script.conf
[Definition]
actionban = /usr/local/bin/ban-notify.sh <ip> <name> <failures>
actionunban = /usr/local/bin/unban-notify.sh <ip> <name>
# Create the script
sudo nano /usr/local/bin/ban-notify.sh
#!/bin/bash
# /usr/local/bin/ban-notify.sh
IP=$1
JAIL=$2
FAILURES=$3
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
# Log to database
mysql -u fail2ban -p'password' fail2ban_db << EOF
INSERT INTO bans (ip, jail, failures, timestamp)
VALUES ('$IP', '$JAIL', $FAILURES, '$TIMESTAMP');
EOF
# Send to monitoring system
curl -X POST https://monitoring.example.com/api/ban \
-H "Content-Type: application/json" \
-d "{\"ip\":\"$IP\",\"jail\":\"$JAIL\",\"failures\":$FAILURES}"
# Update GeoIP database
geoiplookup $IP >> /var/log/fail2ban-geoip.log
Cloudflare Integration Action
# /etc/fail2ban/action.d/cloudflare.conf
[Definition]
actionstart =
actionstop =
actioncheck =
actionban = curl -X POST "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules" \
-H "X-Auth-Email: <cfuser>" \
-H "X-Auth-Key: <cftoken>" \
-H "Content-Type: application/json" \
--data '{"mode":"block","configuration":{"target":"ip","value":"<ip>"},"notes":"Fail2ban %(name)s"}'
actionunban = ID=$(curl -X GET "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules?mode=block&configuration.target=ip&configuration.value=<ip>" \
-H "X-Auth-Email: <cfuser>" \
-H "X-Auth-Key: <cftoken>" \
-H "Content-Type: application/json" | jq -r '.result[0].id')
curl -X DELETE "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules/$ID" \
-H "X-Auth-Email: <cfuser>" \
-H "X-Auth-Key: <cftoken>"
[Init]
cfuser = [email protected]
cftoken = your-api-token
Service-Specific Configurations
SSH Protection
# /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
findtime = 10m
bantime = 1h
# Aggressive SSH protection
[sshd-aggressive]
enabled = true
port = ssh
filter = sshd[mode=aggressive]
logpath = %(sshd_log)s
maxretry = 2
findtime = 1d
bantime = 1w
# SSH DDoS protection
[sshd-ddos]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
maxretry = 10
findtime = 60
bantime = 1h
Web Server Protection
# Apache protection
[apache-auth]
enabled = true
port = http,https
filter = apache-auth
logpath = %(apache_error_log)s
[apache-badbots]
enabled = true
port = http,https
filter = apache-badbots
logpath = %(apache_access_log)s
maxretry = 1
[apache-noscript]
enabled = true
port = http,https
filter = apache-noscript
logpath = %(apache_error_log)s
[apache-overflows]
enabled = true
port = http,https
filter = apache-overflows
logpath = %(apache_error_log)s
maxretry = 2
# Nginx protection
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = %(nginx_error_log)s
[nginx-noscript]
enabled = true
port = http,https
filter = nginx-noscript
logpath = %(nginx_access_log)s
[nginx-badbots]
enabled = true
port = http,https
filter = nginx-badbots
logpath = %(nginx_access_log)s
maxretry = 2
[nginx-noproxy]
enabled = true
port = http,https
filter = nginx-noproxy
logpath = %(nginx_access_log)s
maxretry = 2
Mail Server Protection
# Postfix
[postfix]
enabled = true
port = smtp,submission
filter = postfix
logpath = %(postfix_log)s
[postfix-sasl]
enabled = true
port = smtp,submission,imap,imaps,pop3,pop3s
filter = postfix[mode=auth]
logpath = %(postfix_log)s
# Dovecot
[dovecot]
enabled = true
port = pop3,pop3s,imap,imaps,submission,sieve
filter = dovecot
logpath = %(dovecot_log)s
Database Protection
# MySQL/MariaDB
[mysqld-auth]
enabled = true
port = 3306
filter = mysqld-auth
logpath = /var/log/mariadb/mariadb.log
maxretry = 5
# PostgreSQL
[postgresql]
enabled = true
port = 5432
filter = postgresql
logpath = /var/log/postgresql/*.log
maxretry = 5
# MongoDB
[mongodb-auth]
enabled = true
port = 27017
filter = mongodb-auth
logpath = /var/log/mongodb/mongod.log
maxretry = 3
Custom Application Protection
# Node.js Application
[nodejs-app]
enabled = true
port = 3000
filter = nodejs-app
logpath = /var/log/nodejs/app.log
maxretry = 10
findtime = 5m
bantime = 30m
# Python Flask/Django
[python-app]
enabled = true
port = 5000,8000
filter = python-app
logpath = /var/log/python-app/*.log
maxretry = 5
# Docker containers
[docker-app]
enabled = true
filter = docker-app
logpath = /var/lib/docker/containers/*/*-json.log
maxretry = 5
action = docker-firewall
Advanced Fail2ban Features
Persistent Banning
# Install sqlite support
sudo dnf install -y python3-sqlite
# Configure database
sudo nano /etc/fail2ban/fail2ban.local
[Definition]
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = 1d
Recidive Jail (Repeat Offenders)
# /etc/fail2ban/jail.d/recidive.local
[recidive]
enabled = true
filter = recidive
logpath = /var/log/fail2ban.log
action = %(action_mwl)s
%(action_badips)s[category="recidive"]
bantime = 1w
findtime = 1d
maxretry = 3
# Super recidive for persistent attackers
[recidive-superban]
enabled = true
filter = recidive
logpath = /var/log/fail2ban.log
action = iptables-allports[blocktype=DROP]
bantime = -1 # Permanent ban
findtime = 1w
maxretry = 5
GeoIP Integration
# Install GeoIP
sudo dnf install -y GeoIP GeoIP-data
# Create GeoIP filter
sudo nano /etc/fail2ban/filter.d/geoip-ban.conf
[Definition]
failregex = <HOST> .* Country: (?:CN|RU|KP)
ignoreregex =
Rate Limiting
# HTTP rate limiting
[http-rate-limit]
enabled = true
filter = http-rate-limit
action = iptables-multiport[name=HttpRateLimit, port="80,443"]
logpath = /var/log/nginx/access.log
findtime = 60
maxretry = 100
bantime = 600
# API rate limiting
[api-rate-limit]
enabled = true
filter = api-rate
action = iptables-multiport[name=ApiLimit, port="8080"]
logpath = /var/log/api/access.log
findtime = 300
maxretry = 50
bantime = 3600
Whitelist Management
# Global whitelist
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1
192.168.0.0/16
10.0.0.0/8
172.16.0.0/12
# Dynamic whitelist script
sudo nano /usr/local/bin/fail2ban-whitelist.sh
#!/bin/bash
# Update whitelist from external source
curl -s https://example.com/whitelist.txt > /tmp/whitelist.txt
fail2ban-client set sshd addignoreip $(cat /tmp/whitelist.txt | tr '\n' ' ')
Monitoring and Management
Log Analysis
# View fail2ban logs
sudo tail -f /var/log/fail2ban.log
# Search for specific IP
sudo grep "192.168.1.100" /var/log/fail2ban.log
# Count bans per jail
sudo grep "Ban" /var/log/fail2ban.log | awk '{print $7}' | sort | uniq -c
# Recent bans
sudo grep "Ban" /var/log/fail2ban.log | tail -20
Status Monitoring
# Overall status
sudo fail2ban-client status
# Detailed jail status
sudo fail2ban-client status sshd
# Get all banned IPs
for jail in $(sudo fail2ban-client status | grep "Jail list" | sed 's/.*://;s/,//g'); do
echo "=== $jail ==="
sudo fail2ban-client status $jail | grep -A 100 "Banned IP"
done
# Monitor in real-time
watch -n 1 'sudo fail2ban-client status | grep -A 3 "Jail list"'
Creating Monitoring Scripts
#!/bin/bash
# /usr/local/bin/fail2ban-monitor.sh
# Generate HTML report
cat > /var/www/html/fail2ban-status.html << EOF
<!DOCTYPE html>
<html>
<head>
<title>Fail2ban Status</title>
<meta http-equiv="refresh" content="60">
</head>
<body>
<h1>Fail2ban Status Report</h1>
<p>Generated: $(date)</p>
<h2>Active Jails</h2>
<pre>$(sudo fail2ban-client status)</pre>
<h2>Recent Bans</h2>
<pre>$(sudo grep "Ban" /var/log/fail2ban.log | tail -50)</pre>
<h2>Statistics</h2>
<pre>
Total Bans Today: $(sudo grep "Ban" /var/log/fail2ban.log | grep "$(date +%Y-%m-%d)" | wc -l)
Total Unbans Today: $(sudo grep "Unban" /var/log/fail2ban.log | grep "$(date +%Y-%m-%d)" | wc -l)
</pre>
<h2>Top Banned IPs</h2>
<pre>$(sudo grep "Ban" /var/log/fail2ban.log | awk '{print $8}' | sort | uniq -c | sort -rn | head -20)</pre>
</body>
</html>
EOF
Database Queries
# Query SQLite database
sudo sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 "SELECT * FROM bans ORDER BY timeofban DESC LIMIT 10;"
# Count bans per jail
sudo sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 "SELECT jail, COUNT(*) as count FROM bans GROUP BY jail ORDER BY count DESC;"
# Find repeat offenders
sudo sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 "SELECT ip, COUNT(*) as count FROM bans GROUP BY ip HAVING count > 5 ORDER BY count DESC;"
Integration with Firewall
FirewallD Integration
# /etc/fail2ban/jail.local
[DEFAULT]
banaction = firewallcmd-ipset
banaction_allports = firewallcmd-allports
# Rich rules action
[sshd]
action = %(action_)s
firewallcmd-rich-rules[name=SSH]
Custom FirewallD Actions
# /etc/fail2ban/action.d/firewallcmd-custom.conf
[Definition]
actionstart = firewall-cmd --permanent --new-ipset=<name> --type=hash:ip
firewall-cmd --permanent --zone=drop --add-source=ipset:<name>
firewall-cmd --reload
actionstop = firewall-cmd --permanent --zone=drop --remove-source=ipset:<name>
firewall-cmd --permanent --delete-ipset=<name>
firewall-cmd --reload
actionban = firewall-cmd --ipset=<name> --add-entry=<ip>
actionunban = firewall-cmd --ipset=<name> --remove-entry=<ip>
IPTables Integration
# Direct iptables action
[sshd-iptables]
enabled = true
filter = sshd
action = iptables[name=SSH, port=ssh, protocol=tcp]
logpath = %(sshd_log)s
maxretry = 3
NFTables Integration
# /etc/fail2ban/action.d/nftables-multiport.conf
[Definition]
actionstart = nft add table inet fail2ban
nft add chain inet fail2ban input { type filter hook input priority 100 \; }
nft add set inet fail2ban <name> { type ipv4_addr \; }
nft add rule inet fail2ban input ip saddr @<name> drop
actionstop = nft delete table inet fail2ban
actionban = nft add element inet fail2ban <name> { <ip> }
actionunban = nft delete element inet fail2ban <name> { <ip> }
Performance Optimization
Backend Optimization
# Use systemd for better performance
[DEFAULT]
backend = systemd
# Optimize polling backend
[DEFAULT]
backend = polling
polltimer = 10 # Check every 10 seconds
Memory Management
# Monitor memory usage
ps aux | grep fail2ban
pmap -x $(pgrep fail2ban-server)
# Optimize database
sudo sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 "VACUUM;"
# Limit log file size
sudo nano /etc/logrotate.d/fail2ban
/var/log/fail2ban.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 644 root root
postrotate
/usr/bin/fail2ban-client flushlogs 1>/dev/null || true
endscript
}
Filter Optimization
# Optimize regex patterns
# Bad: .* (greedy)
# Good: [^:]* (specific)
# Use anchors
failregex = ^<HOST> - - \[.*\] ".*" 401
# Limit maxlines for multiline
maxlines = 5
# Use specific date patterns
datepattern = %%Y-%%m-%%d %%H:%%M:%%S
Troubleshooting
Common Issues
Service Won’t Start
# Check configuration
sudo fail2ban-client -d
# Test configuration
sudo fail2ban-client -t
# Check for syntax errors
sudo fail2ban-server -f
# View detailed errors
sudo journalctl -u fail2ban -n 50
Filter Not Matching
# Test filter manually
sudo fail2ban-regex /var/log/secure "Failed password for .* from <HOST>"
# Debug filter
sudo fail2ban-regex -D /var/log/secure sshd
# Check date format
sudo fail2ban-regex --print-date-format /var/log/secure
Bans Not Working
# Check if firewall is running
sudo firewall-cmd --state
# Verify action is correct
sudo fail2ban-client get sshd actionban
# Test action manually
sudo fail2ban-client set sshd banip 192.168.1.100
# Check firewall rules
sudo firewall-cmd --list-all
sudo iptables -L -n
Debug Mode
# Run in foreground with debug
sudo fail2ban-server -f -vvv
# Enable debug logging
sudo nano /etc/fail2ban/fail2ban.local
[Definition]
loglevel = DEBUG
logtarget = /var/log/fail2ban.log
Log Analysis for Troubleshooting
# Check for errors
sudo grep ERROR /var/log/fail2ban.log
# Find configuration issues
sudo grep "Unable to" /var/log/fail2ban.log
# Monitor specific jail
sudo tail -f /var/log/fail2ban.log | grep sshd
Best Practices
Security Best Practices
- Never disable fail2ban on production servers
- Keep ban times reasonable - balance security with usability
- Monitor logs regularly for false positives
- Maintain whitelist of trusted IPs
- Use recidive jail for repeat offenders
- Regular updates of fail2ban and filters
Configuration Best Practices
# Production-ready configuration
[DEFAULT]
# Whitelisting
ignoreip = 127.0.0.1/8 ::1
# Add your management IPs
# Reasonable defaults
bantime = 1h
findtime = 10m
maxretry = 5
# Enable important jails
[sshd]
enabled = true
maxretry = 3
[recidive]
enabled = true
bantime = 1w
# Use systemd backend
backend = systemd
Maintenance Schedule
# Weekly tasks
# - Review banned IPs
# - Check for false positives
# - Update whitelists
# Monthly tasks
# - Analyze attack patterns
# - Update filters
# - Review jail effectiveness
# Quarterly tasks
# - Update fail2ban
# - Review and optimize rules
# - Performance assessment
Real-World Scenarios
Scenario 1: Protecting WordPress
# /etc/fail2ban/filter.d/wordpress-hard.conf
[Definition]
failregex = ^<HOST> .* "POST .*wp-login\.php
^<HOST> .* "POST .*xmlrpc\.php
^<HOST> .* "GET .*wp-admin.* 403
^<HOST> .* "GET .*\.php HTTP.* 404
# /etc/fail2ban/jail.d/wordpress.local
[wordpress-hard]
enabled = true
port = http,https
filter = wordpress-hard
logpath = /var/log/nginx/*access.log
maxretry = 3
findtime = 5m
bantime = 1d
Scenario 2: API Rate Limiting
# /etc/fail2ban/filter.d/api-bruteforce.conf
[Definition]
failregex = ^<HOST> .* "(?:GET|POST) /api/v1/auth.* 401
^<HOST> .* "(?:GET|POST) /api/.* 429
# /etc/fail2ban/jail.d/api.local
[api-bruteforce]
enabled = true
port = 8080,8443
filter = api-bruteforce
logpath = /var/log/api/access.log
maxretry = 20
findtime = 1m
bantime = 1h
Scenario 3: Multi-Service Protection
#!/bin/bash
# Setup comprehensive protection
# Enable essential jails
jails="sshd apache-auth apache-badbots apache-noscript
nginx-http-auth nginx-badbots dovecot postfix-sasl"
for jail in $jails; do
sudo fail2ban-client set $jail enabled true
done
# Configure aggressive protection for critical services
cat > /etc/fail2ban/jail.d/critical.local << EOF
[sshd]
maxretry = 2
bantime = 1d
[dovecot]
maxretry = 3
bantime = 1d
[postfix-sasl]
maxretry = 3
bantime = 1d
EOF
sudo fail2ban-client reload
Scenario 4: Geographic Restrictions
# Block specific countries
cat > /etc/fail2ban/filter.d/geoblock.conf << 'EOF'
[Definition]
failregex = <HOST> .* Country: (CN|RU|KP|IR)
ignoreregex =
EOF
cat > /etc/fail2ban/jail.d/geoblock.local << 'EOF'
[geoblock]
enabled = true
filter = geoblock
action = iptables-allports[blocktype=DROP]
logpath = /var/log/nginx/access.log
maxretry = 1
findtime = 1m
bantime = 1w
EOF
Conclusion
Fail2ban is an essential security tool for protecting AlmaLinux servers from brute-force attacks and malicious activities. When properly configured, it provides automated, real-time protection against various attack vectors while maintaining system accessibility for legitimate users.
Key takeaways:
- Start with basic configurations and expand based on needs
- Monitor logs regularly to avoid false positives
- Keep filters and rules updated
- Use appropriate ban times and retry limits
- Integrate with existing security infrastructure
- Regular maintenance ensures optimal protection
Remember that Fail2ban is just one layer of defense. Combine it with other security measures like firewalls, SELinux, regular updates, and good security practices for comprehensive protection.