Fail2ban SSH brute-force defense: first line of server security
Fail2ban tutorial: install, configure, and dynamically ban SSH brute-force IPs. Includes iptables verification and manual unban. Five steps to automated defense.
Scope
This tutorial is for Linux admins who expose SSH to the public internet. SSH brute-force attacks are among the most common automated attacks. Attackers try username/password combinations via dictionaries or enumeration. Fail2ban watches failed-login patterns in logs, then dynamically adds iptables or nftables rules to block malicious IPs. It is a practical first line of defense.
Prerequisites
- Linux server (Debian/Ubuntu or CentOS/RHEL) with root or sudo
- SSH enabled (port 22 or custom)
- Python 3 installed (Fail2ban dependency)
- iptables or nftables enabled
- Prefer SSH key login first. Fail2ban is complementary.
How it works
Fail2ban is a closed loop: log watch → match rules → dynamic ban.
- Log watch (Filter): Fail2ban tails a log (e.g. `/var/log/auth.log`) and matches failed-login patterns with regex.
- Rule match: You set a threshold—max failures (e.g. 5) in a time window (e.g. 10 minutes). Exceed it, get banned.
- Dynamic ban (Action): Fail2ban inserts iptables rules. The IP is denied all SSH for a set time (e.g. 1 hour).
- Unban: When the ban expires, the rule is removed. Access resumes.
Steps
Step 1: Install Fail2ban
`# Debian/Ubuntu
sudo apt update
sudo apt install fail2ban -y
# CentOS/RHEL
sudo yum install epel-release -y
sudo yum install fail2ban -y`
Step 2: Confirm SSH log path
`# Debian/Ubuntu default path
ls -la /var/log/auth.log
# CentOS/RHEL default path
ls -la /var/log/secure`
Step 3: Create local config
Do not edit `jail.conf` directly (updates overwrite it). Put custom config in `jail.local`:
`sudo tee /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
# Ban time: 3600 seconds (1 hour)
bantime = 3600
# Time window: 600 seconds (10 minutes)
findtime = 600
# Threshold: max 5 failures within 10 minutes
maxretry = 5
# Send email notifications (optional, requires sendmail)
# destemail = admin@example.com
# action = %(action_mwl)s
[sshd]
enabled = true
# Log path auto-detected
logpath = %(sshd_log)s
# SSH port
port = ssh
# Whitelist (IPs that are never banned)
# ignoreip = 127.0.0.1/8 192.168.1.0/24
EOF`
Step 4: Start and enable Fail2ban
`# Restart to load the new configuration
sudo systemctl restart fail2ban
# Enable start on boot
sudo systemctl enable fail2ban
# Check running status
sudo systemctl status fail2ban`
Step 5: Check ban status and stats
`# View all jail statuses
sudo fail2ban-client status
# View SSH jail details (banned IP list)
sudo fail2ban-client status sshd
# View recent logs
sudo tail -f /var/log/fail2ban.log`
Verify
Verify 1: Simulate brute force
From another host, test with a wrong password (do not test from the IP you are using):
`# From the test machine, repeatedly log in with a wrong password
ssh testuser@your-serverIP
# After 6 attempts, legitimate SSH should be rejected
# Watch fail2ban logs
sudo tail -20 /var/log/fail2ban.log
# Should see something like: INFO [sshd] Ban xx.xx.xx.xx`
Verify 2: Check iptables rules
`# Confirm Fail2ban has added the rule chain
sudo iptables -L -n | grep -A 20 \"f2b-sshd\"
# Example output:
# Chain f2b-sshd (1 references)
# target prot opt source destination
# REJECT all -- xx.xx.xx.xx 0.0.0.0/0
# RETURN all -- 0.0.0.0/0 0.0.0.0/0`
Verify 3: Manually unban a test IP
`# Unban the specified IP
sudo fail2ban-client set sshd unbanip xx.xx.xx.xx
# View the updated ban list
sudo fail2ban-client status sshd`
FAQ
Q1: Fail2ban banned my own IP. What now?
Unban immediately with `fail2ban-client set sshd unbanip your-IP`. Then add a whitelist in `[DEFAULT]` of `jail.local`: `ignoreip = 127.0.0.1/8 your-static-public-IP`. Restart Fail2ban. If you have no static IP, raise maxretry to 10-15 to cut false bans.
Q2: Fail2ban does nothing. No ban records in the log?
Common causes and checks: ① Confirm the service is up: `systemctl status fail2ban`; ② Check the SSH log path: `fail2ban-client get sshd logpath`. If it returns `/var/log/secure` but your system uses `auth.log`, set `logpath = /var/log/auth.log` in jail.local; ③ Confirm the fail2ban user can read the log; ④ In `fail2ban.conf`, change `loglevel = INFO` to `DEBUG` for verbose logs.
Takeaway
Fail2ban is a lightweight SSH brute-force defense. It bans malicious IPs from log analysis. This article covered install, config, and verification. Combine it with SSH key auth (disable password login), a non-default SSH port, and Port Knocking for defense in depth. Check fail2ban ban stats weekly. Watch for abnormal spikes.