Files
dotfiles/fail2ban
..
2026-03-24 22:53:51 -04:00

fail2ban — SSH DDoS Protection (Arch Linux)

Overview

fail2ban monitors authentication logs and bans IPs that show malicious signs (too many failed logins, DDoS patterns). This setup uses the ddos filter mode for SSH, incremental banning, and systemd as the log backend (correct for Arch).

Installation

sudo pacman -S fail2ban
sudo systemctl enable --now fail2ban

Configuration

Create /etc/fail2ban/jail.d/sshd.local:

[sshd]
enabled   = true
mode      = ddos
backend   = systemd
port      = ssh
maxretry  = 5
findtime  = 60
bantime   = 3600
bantime.increment = true
bantime.factor    = 2
bantime.maxtime   = 604800
ignoreip  = 127.0.0.1/8 ::1

If SSH runs on a non-standard port, replace port = ssh with the actual port number.

Settings explained

Setting Value Meaning
mode ddos Stricter filter that catches flood/DDoS patterns in addition to brute-force
backend systemd Reads from journald — correct for Arch (no log files needed)
maxretry 5 Ban after 5 failed attempts
findtime 60s Failure window — counts failures within this period
bantime 3600s Initial ban duration (1 hour)
bantime.increment true Ban duration increases for repeat offenders
bantime.factor 2 Each repeat offense doubles the ban time
bantime.maxtime 604800s Maximum ban duration (7 days)
ignoreip loopback Never ban localhost

Applying changes

After editing any config:

sudo fail2ban-client -t          # test config validity
sudo systemctl restart fail2ban  # apply

Usage

# Check jail status and current bans
sudo fail2ban-client status sshd

# Manually ban an IP
sudo fail2ban-client set sshd banip 1.2.3.4

# Unban an IP
sudo fail2ban-client set sshd unbanip 1.2.3.4

# Watch live fail2ban logs
sudo journalctl -u fail2ban -f

How incremental banning works

Each time an IP is banned and re-offends, the ban duration multiplies:

Offense Ban duration
1st 1 hour
2nd 2 hours
3rd 4 hours
... doubles each time
max 7 days

Verifying it works

sudo fail2ban-client status sshd

Expected output shows Currently failed, Total failed, and Banned IP list. The journal backend line should read:

Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd