One Month of SSH Brute Force: What Banned IPs Taught Me About fail2ban

Share

If you run a public server (even a small one hosting a personal site like chrisyoull.com ), you already know the reality: the moment you expose SSH to the internet, the scanners find you.

Since mid-October 2026 I’ve been running fail2ban with a deliberately aggressive policy — any IP that trips the SSH jail gets banned for a full year. The goal wasn’t just defense; it was to collect data. After roughly only a month I had 356 unique IP addresses banned. This post covers what fail2ban is, how I run it, and what the data showed.

What is fail2ban?

fail2ban is an intrusion-prevention framework that watches log files (or the systemd journal) for repeated failed authentication attempts and then bans the offending IP addresses by updating your firewall rules.

It doesn’t stop the first few attempts, it reacts after a threshold is crossed. Once an IP hits maxretry failures inside a findtime window, fail2ban adds a temporary (or long-term) firewall rule that drops traffic from that address.

It is commonly used to protect:

  • SSH
  • nginx / Apache (auth failures, excessive 404s, bot scans)
  • Mail services
  • Anything that writes clear failure messages to a log

I use two factor authentication (MFA), using am authenticator application on my phone, for SSH access to my public facing server. This is short-lived numeric code (almost always 6 digits) that the authenticator app (Google Authenticator, Microsoft Authenticator, Authy, 1Password, Aegis, etc.) generates. The code changes every 30 seconds.

Also, I wrote about using Cloudflare to secuere the HTTP traffic and not having to open any ports to access the website. The document can be found here

fail2ban-client: the control tool

fail2ban-client is the command-line interface used to talk to the running fail2ban server. You use it to check status, list banned IPs, unban addresses, and inspect configuration at runtime.

Common commands I use regularly:

# Overall status + list of active jails
sudo fail2ban-client status

# Status of the SSH jail (includes currently banned IPs)
sudo fail2ban-client status sshd

# List banned IPs with ban start time and calculated unban time
sudo fail2ban-client get sshd banip --with-time

# Which jails currently have a specific IP banned
sudo fail2ban-client banned 1.2.3.4

# Unban an IP from a specific jail
sudo fail2ban-client set sshd unbanip 1.2.3.4

# Unban an IP from every jail
sudo fail2ban-client unban 1.2.3.4

Installation

On Debian/Ubuntu-based systems:

sudo apt update
sudo apt install fail2ban
sudo systemctl enable --now fail2ban

Basic configuration

Never edit the packaged /etc/fail2ban/jail.conf, package upgrades will overwrite it. Instead create a local override:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# or, cleaner, put small files in /etc/fail2ban/jail.d/

Here’s the relevant part of my jail.local for the SSH analysis experiment:

[DEFAULT]
bantime  = 1y          # 1 year — intentionally long for data collection
findtime = 10m
maxretry = 3           # If a bad actor attepmts 3 times in 10 minutes the IP will be jailed
ignoreip = 127.0.0.1/8 ::1 <my-home-ip>

[sshd]
enabled  = true
backend  = systemd     # or auto
port     = ssh
maxretry = 3
bantime  = 1y

After any config change:

sudo systemctl restart fail2ban
# or
sudo fail2ban-client reload

Important notes:

  • Always put your own IP (and any jump hosts) in ignoreip or you will lock yourself out.
  • backend = systemd is usually the best choice on modern systems because it reads the journal directly.
  • A one-year ban is extreme for normal use. Most people run 1 hour to 1 week. I did the long ban specifically so I could study the population of attacking IPs.

The data: 356 banned IPs

From mid-October 2026 through early November 2026, the SSH jail banned 356 unique IP addresses.

Country breakdown (top 15)

Rank Country Count Percentage
1 United States 65 18.3%
2 China 33 9.3%
3 India 22 6.2%
4 Hong Kong 20 5.6%
5 South Korea 19 5.3%
6 Vietnam 18 5.1%
7 Brazil 17 4.8%
8 Indonesia 13 3.7%
9 The Netherlands 12 3.4%
10 Germany 11 3.1%
11 France 11 3.1%
12 Russia 10 2.8%
13 Singapore 9 2.5%
14 Iran 8 2.2%
15 United Kingdom 7 2.0%

Top ISPs / cloud providers

Rank ISP / Provider Count
1 Oracle Corporation 25
2 Shenzhen Tencent Computer Systems Company Limited 15
3 OVH SAS 15
4 Unmanaged LTD 14
5 UCLOUD INFORMATION TECHNOLOGY (HK) LIMITED 12
6 DigitalOcean, LLC 12
7 Microsoft Corporation 10
8 Korea Telecom 9
9 Byteplus Pte. Ltd. 6
10 Chinanet 5

Observations

  1. Cloud and VPS providers dominate. Oracle, Tencent, OVH, DigitalOcean, and Microsoft alone account for a large share of the bans. This is consistent with the common pattern of attackers using cheap or compromised cloud instances rather than residential connections.
  2. Geographic concentration is real but not absolute. The United States leads, followed by China and India, but the long tail is truly global. Traffic came from Burkina Faso, Senegal, Ethiopia, Uzbekistan, Andorra, and many other places.
  3. A long ban is useful for analysis, less so for operations. Banning for a year made the dataset clean (no re-bans of the same IP), but for a normal production server I would recommend something in the 1-hour to 7-day range plus a recidive jail that escalates repeat offenders.
  4. fail2ban is not a complete solution. It reduces noise and stops the dumb scanners. Determined attackers with large botnets simply rotate IPs. Pair it with key-only SSH, fail2ban, multi-factor authentication, and (ideally) network-level controls or a bastion host.

Full dataset

The complete cleaned list of 356 IPs (IP, country, city, ISP) is available as a CSV if you want to do your own analysis.

Quick reference commands

Task Command
List all jails sudo fail2ban-client status
Show banned IPs in a jail sudo fail2ban-client status sshd
Show ban times remaining sudo fail2ban-client get sshd banip --with-time
Unban an IP sudo fail2ban-client set sshd unbanip <IP>
Reload config sudo fail2ban-client reload

Final thoughts

fail2ban is still one of the highest-signal, lowest-effort tools you can put in front of a public SSH service. It won’t stop a sophisticated attacker, but it dramatically cuts the volume of opportunistic noise and gives you a clear log of who has been trying.

If you run it, take a few minutes to look at the banip --with-time output and the country/ISP breakdown once in a while. The patterns are surprisingly consistent across small personal servers and large fleets.


Data collected on the host serving chrisyoull.com between approximately 15 October 2026 and 2 November 2026.