The moment you spin up a brand new Linux server on AWS, DigitalOcean, Hetzner, or Linode, a timer starts ticking. Within ten to fifteen minutes of your public IP address appearing on the internet, automated botnets will find your machine and begin attempting to log in.
They do not need to know who you are. Massive global scanning networks constantly sweep the entire IPv4 address space, probing TCP port 22. When they find an open SSH listener, an automated dictionary attack kicks off: testing hundreds of common usernames and passwords every minute.
In this guide, we look at the raw logs of an active SSH brute-force attack, examine why weak passwords fail against dictionary scripts, and configure Fail2ban to automatically detect and block malicious IP addresses before they guess correctly.
1. The Raw Firehose: What an SSH Attack Looks Like
Every SSH connection attempt is recorded by your Linux operating system. On Debian and Ubuntu servers, SSH logs to /var/log/auth.log. On RHEL, Rocky, and Fedora systems, it logs to /var/log/secure or systemd journal.
If you run a live stream on your authentication log on any fresh cloud server, this is what you will witness:
$ sudo tail -f /var/log/auth.log | grep "Failed password"
Nov 10 03:14:12 server sshd[18420]: Failed password for invalid user admin from 185.220.101.5 port 41232 ssh2
Nov 10 03:14:15 server sshd[18422]: Failed password for invalid user ubuntu from 185.220.101.5 port 41238 ssh2
Nov 10 03:14:18 server sshd[18425]: Failed password for root from 185.220.101.5 port 41244 ssh2
Nov 10 03:14:21 server sshd[18429]: Failed password for invalid user test from 185.220.101.5 port 41250 ssh2
Nov 10 03:14:24 server sshd[18433]: Failed password for invalid user oracle from 185.220.101.5 port 41256 ssh2
Notice the cadence: every three seconds, the same remote IP address (185.220.101.5) attempts a new credential pair. The script cycles through common default system accounts: admin, ubuntu, root, test, oracle, git, and postgres.
If your server allows password authentication and has a common password like password123 or admin2026, a dictionary attack will eventually succeed. Even if your password is strong, hundreds of thousands of failed attempts waste server memory, flood log files, and consume CPU cycles.
2. Enter Fail2ban: The Automated Digital Bouncer
A firewall like UFW or iptables is static: it either opens port 22 to everyone or blocks port 22 completely. If you block port 22, you cannot administer your server remotely. If you open port 22, botnets hammer it.
Fail2ban bridges this gap by making your firewall dynamic:
- It monitors system log files in real time (using Python regex filters).
- It counts failed authentication attempts from each unique IP address within a configurable time window.
- When an IP exceeds the threshold (e.g. 3 failed logins within 10 minutes), Fail2ban dynamically injects a temporary drop rule into your firewall (iptables or nftables).
- Any further packets from that attacking IP are silently discarded before they even reach the SSH daemon.
- Once the ban duration expires (e.g. 24 hours), Fail2ban removes the firewall rule automatically.
[Attacker IP: 185.220.101.5]
|
v
[Port 22 SSH Attempt 1] -> Fails -> auth.log (Fail count: 1)
[Port 22 SSH Attempt 2] -> Fails -> auth.log (Fail count: 2)
[Port 22 SSH Attempt 3] -> Fails -> auth.log (Fail count: 3 -> THRESHOLD REACHED!)
|
v
[Fail2ban Action Triggered]
|--> iptables -I INPUT -s 185.220.101.5 -j DROP
|
v
[Attempt 4+] -> Packets dropped at kernel level. Zero CPU/SSH overhead.
3. Installing and Configuring Fail2ban on Ubuntu / Debian
Setting up Fail2ban takes less than three minutes. First, update your package repository and install the daemon:
sudo apt update
sudo apt install -y fail2ban
The Golden Rule: Never Edit jail.conf Directly
Fail2ban ships with a default configuration file located at /etc/fail2ban/jail.conf. Whenever the package updates through apt upgrade, this file can get overwritten.
Always create a local override file called jail.local. Any setting you specify in jail.local takes precedence over jail.conf:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
4. Recommended Production Jail Settings
Inside /etc/fail2ban/jail.local, find the [DEFAULT] and [sshd] sections. Configure these essential parameters:
[DEFAULT]
# Never ban your own home or office IP address (whitelist)
ignoreip = 127.0.0.1/8 ::1 203.0.113.45
# How long the attacker is banned (e.g. 1d = 24 hours, 1h = 1 hour)
bantime = 1d
# Time window in which failed attempts are tracked
findtime = 10m
# Number of failed attempts before ban triggers
maxretry = 3
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 1d
Make sure to add your own public IP address to the
ignoreip directive. If you accidentally misplace an SSH key or mistype a passphrase three times, you do not want your own server locking you out for 24 hours.
Save the file and restart the Fail2ban service to activate your new rules:
sudo systemctl restart fail2ban
sudo systemctl enable fail2ban
sudo fail2ban-client status
5. Watching Fail2ban Jail Attackers in Real Time
Once Fail2ban is running, you can monitor active jails using the fail2ban-client command-line utility. Check the specific status of the SSH jail:
$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 2
| |- Total failed: 418
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 3
|- Total banned: 37
`- Banned IP list: 185.220.101.5 194.26.29.112 45.154.255.89
In this output, Fail2ban detected 418 total failed login attempts, identified repeat offenders, and actively jailed 3 IP addresses right now. Those machines are completely blocked at the kernel network layer.
How to Manually Unban an IP Address
If a colleague or friend gets accidentally banned after entering the wrong password, you can unban their IP address immediately from your terminal:
sudo fail2ban-client set sshd unbanip 185.220.101.5
6. The 3 Ultimate Rules for Bulletproof SSH Defense
Fail2ban is your active line of defense, but true server hardening requires combining it with these three fundamental habits:
- Disable Password Authentication Completely: Passwords can be guessed; 4096-bit RSA or Ed25519 cryptographic keys cannot. Set
PasswordAuthentication noin/etc/ssh/sshd_config. - Disable Direct Root Login: Bots target
rootby default because it is universally present on all Unix machines. Create a standard sudo user and setPermitRootLogin no. - Move Off Port 22: Changing your SSH listening port to a non-standard port (such as
Port 2222orPort 48921) eliminates 98% of dumb, automated mass-scanning scripts before they even touch your firewall.
Summary Checklist
- Automated botnets constantly sweep IPv4 for open SSH port 22 listeners.
- Inspect live attack attempts with
tail -f /var/log/auth.log. - Fail2ban dynamically monitors log files and injects firewall drop rules.
- Always edit
jail.local, never the defaultjail.conf. - Whitelist your home/office IP in
ignoreipto prevent accidental self-lockout. - Verify active jails with
sudo fail2ban-client status sshd.
Setting up Fail2ban gives your Linux servers automated resilience against internet noise. Once configured, you can sleep soundly knowing brute-force bots get stopped in their tracks.