| Tested on | RHEL 10.2 (Coughlan) — vm1.lab.example (192.168.56.116) as SSH server; vm2.lab.example (192.168.56.220, source 192.168.56.156) for failed-login testsDebian 12 (Bookworm) — podman container (public.ecr.aws/docker/library/debian:bookworm-slim) for apt install; jail used /var/log/auth.log after rsyslog in that container (package defaults vary) |
|---|---|
| Package | fail2ban-server 1.1.0-6.el10_0 and fail2ban-firewalld 1.1.0-6.el10_0 on RHELfail2ban 1.0.2-2, openssh-server 1:9.2p1-2+deb12u10, and rsyslog 8.2302.0-1+deb12u1 in the Debian container |
| Applies to | RHEL, Rocky Linux, AlmaLinux, Fedora, Ubuntu, Debian, and other Linux systems with OpenSSH |
| Privilege | sudo or root on the SSH server for package install, jail files, and firewall rules |
| Scope | Install Fail2ban, enable the sshd jail, tune bantime / findtime / maxretry / ignoreip, verify bans after failed SSH, and unban addresses. Does not replace full SSH hardening or every method in prevent brute force SSH attacks. |
| Related guides | ssh command OpenSSH authentication methods Prevent brute force SSH attacks firewalld cheat sheet Test SSH connection |
Fail2ban watches authentication failures and adds a temporary firewall block when one IP crosses your threshold. For SSH, enable the sshd jail when you want repeated failed login attempts to trigger temporary source-IP blocks — after basic OpenSSH hardening.
The walkthrough uses short lab values (bantime = 2m, findtime = 1m, maxretry = 3) so the ban is easy to verify. Choose production values according to your login pattern and lockout policy.
Quick answer
| Step | RHEL family | Debian / Ubuntu |
|---|---|---|
| Install | sudo dnf install epel-release fail2ban |
sudo apt install fail2ban |
| Enable sshd jail | Create /etc/fail2ban/jail.d/00-sshd.local with enabled = true under [sshd] |
Same file path and syntax |
| Start service | sudo systemctl enable --now fail2ban |
Same on systemd hosts |
| Check jail | sudo fail2ban-client status sshd |
Same command |
| Unban one IP | sudo fail2ban-client set sshd unbanip 203.0.113.10 |
Same command |
After install on any distro, run sudo fail2ban-client status sshd and note whether the jail reports Journal matches or a File list — that tells you which backend is active on your host.
How Fail2ban protects SSH
Fail2ban runs fail2ban-server as a daemon. It applies filter rules to log lines (or journal entries) that match failed SSH authentication, counts failures per source IP inside findtime, and calls a ban action when maxretry is reached.
On a typical setup:
- The filter reads
sshdfailure messages (Invalid user,Failed password, and similar). - The action inserts a firewall rule that drops or rejects traffic from the offender for
bantime. - When
bantimeexpires, Fail2ban removes the rule automatically.
Fail2ban complements — it does not replace — disabling password auth, using keys, and limiting PermitRootLogin.
Install Fail2ban
RHEL, Rocky Linux, AlmaLinux, and Fedora
Fail2ban ships in EPEL on RHEL-family systems. Install the repository, then the package:
sudo dnf install epel-release fail2ban -yOn RHEL 10 the search shows the expected package name:
======================== Name Exactly Matched: fail2ban ========================
fail2ban.noarch : Daemon to ban hosts that cause multiple authentication errorsThe fail2ban-firewalld subpackage is pulled in on hosts that use firewalld. It sets firewallcmd-rich-rules as the default ban action.
Confirm the version you installed:
rpm -q fail2ban-serverSample output:
fail2ban-server-1.1.0-6.el10_0.noarchDebian and Ubuntu
On Debian-family systems Fail2ban is in the main archive:
sudo apt update
sudo apt install fail2ban -yIn a Debian 12 container used for this refresh, the installed versions were:
fail2ban 1.0.2-2
openssh-server 1:9.2p1-2+deb12u10Start Fail2ban and run sudo fail2ban-client status sshd. If the jail reports a File list pointing at /var/log/auth.log but that file does not exist, install and start rsyslog so authentication events are written there:
sudo apt install rsyslog -y
sudo systemctl enable --now rsyslogIf the status output shows Journal matches instead, the jail reads the systemd journal and you do not need auth.log for SSH monitoring on that host.
Enable the sshd jail
Shipped defaults keep jails disabled until you opt in. Add a drop-in under jail.d instead of editing jail.conf (package updates can overwrite the main file).
Create /etc/fail2ban/jail.d/00-sshd.local with short lab values you can replace for production:
sudo tee /etc/fail2ban/jail.d/00-sshd.local <<'EOF'
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1
bantime = 2m
findtime = 1m
maxretry = 3
[sshd]
enabled = true
EOFThe settings mean:
ignoreip— never ban these addresses (add your admin VPN or office subnet in production).findtime— window in which failures are counted together.maxretry— failures insidefindtimethat trigger a ban.bantime— how long the firewall block lasts.
Add trusted admin networks to ignoreip before you test with deliberate failed logins. Locking yourself out of SSH is a common first-time mistake.
Start Fail2ban and verify the jail
Enable and start the service:
sudo systemctl enable --now fail2banCheck that the unit is running:
systemctl is-active fail2banSample output:
activeList active jails — you should see sshd:
sudo fail2ban-client statusSample output from the RHEL lab:
Status
|- Number of jail: 1
`- Jail list: sshdInspect the jail backend and ban counters:
sudo fail2ban-client status sshdOn RHEL 10 the jail reads the journal and uses the firewalld action:
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd + _COMM=sshd-session
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:
The jail sshd has the following actions:
firewallcmd-rich-rulesIn the Debian 12 container lab — after rsyslog was added — the same command showed File list: /var/log/auth.log and iptables-multiport as the ban action. On your host, trust the status sshd output rather than assuming either backend or action.
Test the ban with failed SSH logins
Run failed attempts from a second host (not from the server console logged in as root on the same IP Fail2ban sees as ignoreself). From vm2.lab.example three invalid-user tries against vm1.lab.example produced:
for i in 1 2 3; do
ssh -o BatchMode=yes -o StrictHostKeyChecking=no bogus@192.168.56.116 exit
sleep 1
doneEach attempt returned Permission denied until the fourth try hit the ban:
bogus@192.168.56.116: Permission denied (publickey,password).
bogus@192.168.56.116: Permission denied (publickey,password).
bogus@192.168.56.116: Permission denied (publickey,password).
ssh: connect to host 192.168.56.116 port 22: Connection refusedAfter the threshold, the jail reported the client source address (192.168.56.156 from vm2):
sudo fail2ban-client status sshdStatus for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 3
| `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd + _COMM=sshd-session
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 192.168.56.156On RHEL with firewalld, list rich rules to see the reject rule Fail2ban added:
sudo firewall-cmd --list-rich-rulesSample output:
rule family="ipv4" source address="192.168.56.156" port port="ssh" protocol="tcp" reject type="icmp-port-unreachable"The Debian 12 container test through published port 2222 banned the container gateway address 10.88.0.1 after the same three-failure pattern — confirming a file-based jail works outside the RHEL journal path used above.
Unban an address and tune ignoreip
When you lock out a lab client, remove the ban immediately:
sudo fail2ban-client set sshd unbanip 192.168.56.156Sample output:
1Recheck the jail — the banned list should be empty:
sudo fail2ban-client status sshdThe Banned IP list line should show no addresses. To prevent future bans for a subnet, add it to ignoreip in 00-sshd.local and reload:
sudo fail2ban-client reload sshdYou can also add a single address at runtime (lost on service restart unless it is in the file):
sudo fail2ban-client set sshd addignoreip 192.168.56.0/24Where the sshd jail reads failures
| Distro family | Common backend |
|---|---|
| RHEL, Rocky, AlmaLinux, Fedora | systemd journal |
| Debian, Ubuntu | systemd journal or /var/log/auth.log, depending on Fail2ban and package configuration |
Do not assume one path from the distro name alone. After you start Fail2ban, run:
sudo fail2ban-client status sshdIf the filter section shows Journal matches, the jail reads sshd events from the journal (typical on RHEL 10 in this lab). If it shows File list: /var/log/auth.log, the jail tails that file — which is what the Debian 12 container used once rsyslog was running.
If Total failed never increments, check the backend your status output reports first, then rule out testing from an address in ignoreip or matched by ignoreself, or too few failures inside findtime.
Useful fail2ban-client commands
| Task | Command |
|---|---|
| Ban an IP manually | sudo fail2ban-client set sshd banip 203.0.113.55 |
| Unban an IP | sudo fail2ban-client set sshd unbanip 203.0.113.55 |
Reload jail after editing jail.d |
sudo fail2ban-client reload sshd |
| Stop monitoring SSH temporarily | sudo fail2ban-client stop sshd |
| Start the sshd jail again | sudo fail2ban-client start sshd |
Runtime banip / unbanip changes apply immediately; persistent settings belong in /etc/fail2ban/jail.d/*.local.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Have not found any log file for sshd jail |
Jail expects a file backend but the log path is missing | Check fail2ban-client status sshd for File list; create the log (often via rsyslog and auth.log) or switch the jail to backend = systemd if you intend to use the journal |
Total failed stuck at zero on Debian or Ubuntu |
Wrong backend for your logging setup | Compare status sshd (Journal matches vs File list) with where sshd actually logs (journalctl -u ssh vs /var/log/auth.log) |
| Failures logged but no ban | Testing from the server own IP | Use a remote client or adjust ignoreself / ignoreip for lab work |
| You banned yourself | ignoreip missing your admin subnet |
Console in through out-of-band access; fail2ban-client set sshd unbanip …; add subnet to ignoreip |
| Jail empty after install | [sshd] enabled = true missing |
Add 00-sshd.local as shown above; systemctl restart fail2ban |
| SSH shows connection refused right after failures | Ban action active | Expected during bantime; wait or unban; on RHEL check firewall-cmd --list-rich-rules |
Total failed stuck at zero on RHEL |
Journal not matching filter | Confirm sshd logs failures: journalctl -u sshd --since today; ensure fail2ban service is running |
References
Summary
Fail2ban gives you an automatic SSH safety net: enable the sshd jail in /etc/fail2ban/jail.d/00-sshd.local, set ignoreip for networks you never want blocked, and tune bantime, findtime, and maxretry to match your lockout policy.
On RHEL-family servers the jail commonly reads sshd events from the systemd journal and, with fail2ban-firewalld installed, adds firewalld rich rules. On Debian and Ubuntu the backend may be the journal or /var/log/auth.log depending on package defaults — use fail2ban-client status sshd to see Journal matches or File list on your system. In the tested Debian 12 package the ban action was iptables-multiport; other releases or local configs may differ. Test bans from a remote client so ignoreself does not hide your failures, and keep fail2ban-client set sshd unbanip handy during the first rollout.
After a successful lab test, adjust thresholds for production and add your admin sources to ignoreip before you rely on the jail on production SSH. Pair Fail2ban with key-based login and the broader checklist in prevent brute force SSH attacks rather than treating it as the only control.

