How to Configure Fail2Ban to Protect SSH on Fedora

Install Fail2Ban on Fedora, configure the SSH jail in /etc/fail2ban/jail.d/ssh.conf, and enable the service to block brute-force attacks.

The brute-force problem on a fresh Fedora install

You just finished setting up a new Fedora Server instance. You opened port 22, connected from your laptop, and went to grab coffee. When you return, journalctl -u sshd is scrolling with hundreds of Failed password for root lines. Automated scanners are trying every password in a dictionary. You need a system that watches those logs and blocks the offending IPs before they guess correctly.

What Fail2Ban actually does

Fail2Ban is not a firewall. It does not sit between your network interface and the internet. It is a log parser. It reads authentication logs, matches lines against regular expressions, and counts failures per source IP. When the count crosses a threshold, it executes an action. The default action on Fedora tells firewalld to insert a temporary drop rule for that IP.

Think of it like a bouncer at a club. The bouncer does not stop everyone from entering. The bouncer watches the door, counts how many times someone tries to get in without a ticket, and calls security to block them after three attempts. The security team handles the actual blocking. Fail2Ban is the bouncer. firewalld is the security team.

This separation matters. If you change your firewall backend, Fail2Ban adapts. If you change your SSH log format, Fail2Ban adapts. You only configure the rules that matter to your service. The daemon ships with filter files in /etc/fail2ban/filter.d/ that contain the regex patterns for dozens of services. The [sshd] filter matches OpenSSH log lines. The action files in /etc/fail2ban/action.d/ contain the shell commands that talk to firewalld, iptables, or nftables. You never edit those directories directly. You only point to them by name in your jail configuration.

Read the actual log lines before guessing at patterns. Run journalctl -xeu sshd to see exactly what the daemon is logging. Match your filter to that output.

Installing and configuring the SSH jail

Fedora ships Fail2Ban in the default repositories. The package includes a comprehensive default configuration in /etc/fail2ban/jail.conf. You should never edit that file directly. Package updates will overwrite your changes. Instead, create override files in /etc/fail2ban/jail.d/. The daemon merges these files on startup, giving your local settings priority. This follows the standard Linux convention: user modifications live in /etc/, package defaults live in /usr/lib/ or /etc/ base files, and drop-in directories override the base.

Here is how to install the package and prepare the configuration directory.

sudo dnf install fail2ban -y # Pulls the daemon and Python dependencies from the default repos
sudo mkdir -p /etc/fail2ban/jail.d # Creates the override directory if it does not exist
sudo touch /etc/fail2ban/jail.d/ssh.conf # Creates a dedicated file for SSH rules

Fail2Ban uses INI-style configuration. Each service you want to protect gets its own section. The [sshd] section targets the OpenSSH daemon. You will define which log to watch, how many failures trigger a ban, and how long the ban lasts. The three time-based parameters work together as a sliding window. findtime defines the window. maxretry defines the threshold inside that window. bantime defines the punishment.

Here is the configuration file that enables SSH protection with sensible defaults.

[sshd]
enabled = true # Activates this jail. The default config has it disabled.
port = ssh # Tells the firewall action which port to protect. Resolves to 22.
filter = sshd # Uses the built-in regex patterns for OpenSSH log lines.
logpath = /var/log/secure # Points to the Fedora authentication log file.
maxretry = 3 # Bans the IP after three failed authentication attempts.
bantime = 3600 # Keeps the IP blocked for one hour.
findtime = 600 # Resets the failure counter if no attempts occur in 10 minutes.

Save that block to /etc/fail2ban/jail.d/ssh.conf. The findtime parameter is critical. Without it, a single failed attempt stays in the counter forever. Adding it ensures the ban only triggers for rapid, automated attacks rather than a user who mistypes their password once a week. The logpath must match your system. Fedora uses /var/log/secure. Debian derivatives use /var/log/auth.log. Mixing them up is the most common reason jails stay empty.

Trust the package manager. Manual file edits drift, drop-in overrides stay.

Starting the service and checking the backend

Fail2Ban needs to know which firewall tool to use. Fedora defaults to firewalld. The package includes an action file that translates Fail2Ban commands into firewall-cmd rich rules. You do not need to configure this manually unless you run nftables directly or use iptables. The daemon detects the available backend automatically.

Here is how to start the daemon and enable it for future boots.

sudo systemctl enable --now fail2ban # Starts the service immediately and registers it for boot
sudo systemctl status fail2ban # Confirms the process is running and shows the active backend

Check the status output. Look for the line that says Backend: systemd or Backend: polling. The systemd backend is faster and uses journal events. The polling backend falls back to reading files on disk. Both work. The systemd backend is preferred on modern Fedora releases. Always check status before restarting. It shows recent log lines and the current state in one view.

Verifying the jail is active

The daemon does not give you a visual dashboard. You query it through the command-line client. The client reads the current state and reports which jails are loaded, how many IPs are banned, and which log files are being monitored.

Here is how to query the SSH jail status.

sudo fail2ban-client status sshd # Requests the runtime state of the sshd jail

The output will list the filter name, the log file path, the ban action, and a list of currently banned IPs. If the list is empty, the jail is idle and waiting for failures. That is normal. To test it safely, open a second terminal and attempt to SSH into the server with a wrong password three times. Wait thirty seconds. Run the status command again. You should see your local IP in the Currently failed or Banned IP list section.

If you see your own IP banned, you can unban it immediately.

sudo fail2ban-client set sshd unbanip <YOUR_IP> # Removes the temporary firewall rule for that address

Verify the firewall rule disappeared by checking the runtime zone.

sudo firewall-cmd --list-rich-rules # Shows active rich rules including Fail2Ban bans

Run journalctl -xeu fail2ban after every configuration change. Read the actual error before guessing.

Common pitfalls and what the error looks like

Fail2Ban fails silently more often than it crashes. The daemon starts, but the jail never triggers. The most common cause is a mismatched log path. Fedora stores authentication logs in /var/log/secure. Some guides assume /var/log/auth.log, which is the Debian default. If the path is wrong, Fail2Ban cannot read the log and the jail stays empty.

Another frequent issue is SELinux. The daemon runs in a confined domain. If you point logpath to a custom directory or a non-standard location, SELinux will block the read access. You will see denials in the audit log.

type=AVC msg=audit(1698765432.123:456): avc:  denied  { read } for  pid=1234 comm="fail2ban-server" name="secure" dev="sda1" ino=789 scontext=system_u:system_r:fail2ban_t:s0 tcontext=system_u:object_r:var_log_t:s0 tclass=file

Run journalctl -t setroubleshoot to get a human-readable summary. The fix is usually to restore the default context or use the standard /var/log/secure path. Never disable SELinux to make Fail2Ban work. Adjust the path or the policy instead.

Firewall backend conflicts also cause headaches. If you manually add firewall-cmd rules while Fail2Ban is active, the runtime configuration and the persistent configuration can diverge. Always run firewall-cmd --reload after making manual changes. Fail2Ban inserts rules into the runtime zone. A reload flushes and rebuilds from persistent rules, which can temporarily drop Fail2Ban bans until the daemon re-applies them. The daemon handles this gracefully, but it is good to know why your ban list might briefly disappear.

Snapshot the system before the upgrade. Future-you will thank you.

When to use Fail2Ban versus alternatives

Use Fail2Ban when you want automated, log-driven IP blocking across multiple services like SSH, Apache, or Dovecot. Use SSHGuard when you prefer a lighter daemon that reads logs via journalctl and uses iptables or nftables directly without a configuration file. Use firewalld rich rules when you only need static whitelisting for a few known IPs and do not want background monitoring. Use denyhosts when you are maintaining a legacy RHEL 6 system that predates modern systemd integration.

Where to go next