fail2ban brute-force protection
high on-server
What it inspects
`systemctl is-active fail2ban` and `fail2ban-client status` (as root), reading the `Number of jail` line. It fails if fail2ban is not active, warns if it is active with zero jails or an unhealthy daemon, and passes with one or more jails. When SSH is not publicly reachable, a missing fail2ban is only an informational warning.
Why it matters
fail2ban watches authentication logs and temporarily firewalls addresses that repeatedly fail to log in. That blunts password guessing and reduces log noise. It complements key-only SSH rather than replacing it, and it only helps if a jail such as sshd is actually enabled.
How to verify
systemctl is-active fail2ban; sudo fail2ban-client status
How to fix
Install fail2ban, enable the sshd jail through a jail.d file, restart the service, and confirm with fail2ban-client status sshd.
Install fail2ban and enable the sshd jail
Debian / Ubuntu
# If sshd listens on a port other than 22, also add: port = <that port> under [sshd] below.
apt-get install -y fail2ban python3-systemd
install -m 0644 /dev/stdin /etc/fail2ban/jail.d/umbermark-sshd.local <<'EOF'
[DEFAULT]
# Add your own admin IP(s) here so you cannot ban yourself, e.g. ignoreip = 127.0.0.1/8 ::1 203.0.113.7
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
backend = systemd
EOF
systemctl enable fail2ban
systemctl restart fail2ban
# The server needs a moment to open its socket after a restart; wait for it (at most about 15 seconds).
for _ in $(seq 1 15); do fail2ban-client ping >/dev/null 2>&1 && break; sleep 1; done
fail2ban-client status sshd
RHEL / Rocky / Alma / Fedora
# epel-release exists on Rocky/Alma/CentOS Stream; on RHEL itself enable EPEL first (see the Fedora EPEL docs).
# If sshd listens on a port other than 22, also add: port = <that port> under [sshd] below.
. /etc/os-release; [ "$ID" = fedora ] || dnf install -y epel-release
dnf install -y fail2ban python3-systemd
install -m 0644 /dev/stdin /etc/fail2ban/jail.d/umbermark-sshd.local <<'EOF'
[DEFAULT]
# Add your own admin IP(s) here so you cannot ban yourself, e.g. ignoreip = 127.0.0.1/8 ::1 203.0.113.7
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
backend = systemd
EOF
systemctl enable fail2ban
systemctl restart fail2ban
# The server needs a moment to open its socket after a restart; wait for it (at most about 15 seconds).
for _ in $(seq 1 15); do fail2ban-client ping >/dev/null 2>&1 && break; sleep 1; done
fail2ban-client status sshd