Installing Fail2ban on CentOS Stream 9: Battle-Tested Brute-Force Protection

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

Just rented a new CentOS Stream 9 VPS? What’s the first thing you should do? Don’t rush to install a Web Server or Database just yet. Statistics show it takes an average of only 3-5 minutes for botnets to “notice” a public IP. They execute thousands of failed login attempts (Brute-force) on port 22 every hour. Fail2ban is an incredibly lightweight yet effective tool to automatically lock out these troublemakers.

3 steps to “stop” Brute-force (Quick Start)

Fail2ban is not included in the default RHEL/CentOS repositories. You need to install the EPEL Repo first. Here is a quick 60-second DNF deployment process:

# 1. Install EPEL Repository
sudo dnf install epel-release -y

# 2. Install Fail2ban and Firewalld support package
sudo dnf install fail2ban fail2ban-firewalld -y

# 3. Enable service to start with system
sudo systemctl enable --now fail2ban

Once completed, Fail2ban is ready. However, the default configuration is still quite loose. We need to fine-tune it to be truly effective for SSH.

What’s new in the Fail2ban mechanism on CentOS Stream 9?

Many people mistake Fail2ban for a firewall. In reality, it acts like an “inspector” constantly scanning log files, augmenting your firewalld configuration. On CentOS Stream 9, the system prioritizes using systemd-journald. Instead of slowly reading the /var/log/secure text file, Fail2ban now communicates directly with the journal. This approach allows for near-instant response times and saves resources as logs grow larger.

Golden Rule: Don’t touch jail.conf

Practical experience dictates that you should absolutely never modify the /etc/fail2ban/jail.conf file. Every time the package updates, this file will be overwritten, and you will lose all your configurations. Instead, create a jail.local file to override the custom settings:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo vi /etc/fail2ban/jail.local

Battle-tested configuration to protect SSH

Navigate to the [sshd] section in the jail.local file and modify it as follows to optimize security:

[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
backend = systemd
maxretry = 3
findtime = 15m
bantime  = 24h
  • maxretry = 3: Only allows 3 failed attempts. Strike three and you’re out.
  • findtime = 15m: If 3 failures occur within a 15-minute window, the ban is triggered.
  • bantime = 24h: A 1-day “prison sentence.” This duration is usually enough to convince botnets to automatically skip your IP.

After editing, restart the service to apply the new configuration: sudo systemctl restart fail2ban.

Integrating Firewalld and handling SELinux errors

This is the most critical part. On CentOS Stream 9, SELinux and Firewalld operate under very strict policies.

When SELinux blocks Fail2ban

I once migrated 5 production servers to CentOS 9 and realized Fail2ban was often blocked by SELinux when attempting to call Firewalld. The result? The IP was added to the ban list, but the firewall remained wide open. To verify, use the command: ausearch -m avc -ts recent. If you see errors related to dbus or firewalld, that is the culprit.

Optimizing Firewalld Backend

To ensure Fail2ban coordinates as smoothly as possible, force it to use rich-rules. Add the following lines to the [DEFAULT] section in your jail.local file:

[DEFAULT]
banaction = firewallcmd-rich-rules
banaction_allports = firewallcmd-rich-rules

Using rich-rules keeps the ban list managed separately, preventing it from cluttering the main firewall ruleset.

How to check and Unban an IP

During work, you or a client might occasionally lock yourselves out. In such cases, fail2ban-client is your only lifesaver, especially when Cockpit is not available.

View active “jails”: fail2ban-client status

SSH Blacklist status: fail2ban-client status sshd

Rescue an accidentally locked IP: fail2ban-client set sshd unbanip 1.2.3.4

Best practices for a secure server

  1. Always Whitelist your personal IP: Add your static IP to the ignoreip line in jail.local. This is your insurance policy to ensure you’re never locked out of your own server.
  2. Change the default SSH port: Change from 22 to a random number like 2289. This simple step alone can reduce bot-scanning logs by up to 90% daily.
  3. Monitor logs in real-time: Try the command tail -f /var/log/fail2ban.log. There is a strange sense of security and satisfaction in watching unknown IPs being “neutralized” continuously.

Deploying Fail2ban on CentOS Stream 9 isn’t difficult. The challenge lies in knowing how to handle SELinux so the tool actually has the permission to execute. I hope these battle-tested insights help make your server impenetrable to brute-force password attacks and ensure complete server malware protection.

Share: