One Month of SSH Brute Force: What Banned IPs Taught Me About fail2ban
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
ignoreipor you will lock yourself out. backend = systemdis 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
- 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.
- 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.
- 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.
- 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.