Fail2ban, Building Beyond the First Rule

In Februray, I wrote a post about the power of one fail2ban rule. That post has become one of, if not, the most popular post I have ever written. I figured it was well deserving of a sequel!

Let's provide an update and look at three addition rules that could be implemented.

An Update on the "One Rule" 10 months later

In short the goal of that one rule was to ban an IP where someone was trying to login using an invalid user. It looked for logins via ssh and would ban an IP for one month.

The numbers as they look today:

There were 750 banned IP addresses currently on the list.

The IP addresses were fed into Claude Sonnet 4.0 for analysis - these are the generated findings

🇨🇳 CHINA - 392 IPs (52.3%)
Primary Ranges: 1.x, 14.x, 27.x, 36.x, 39.x, 42.x, 43.x, 49.x, 58.x, 59.x, 61.x, 101.x, 106.x, 110.x, 112-125.x, 171.x, 175.x, 180-183.x, 210-223.x

Notable concentrations:

103.x range: 67 IPs
101.x range: 35 IPs
43.x range: 23 IPs
27.x range: 21 IPs
14.x range: 18 IPs

🇺🇸 UNITED STATES - 140 IPs (18.7%)
Primary Ranges: 104.x, 107.x, 128.x, 129.x, 134.x, 136-139.x, 142-143.x, 146-147.x, 149.x, 152.x, 157.x, 159-167.x, 172.x, 198-199.x, 204-209.x

Major cloud providers:

DigitalOcean: ~34 IPs
AWS/Google Cloud: ~24 IPs
Various hosting providers: ~82 IPs

🌍 AFRICA - 47 IPs (6.3%)
Countries breakdown:

South Africa: 23 IPs (102.x range)
Nigeria: 14 IPs (197.x range)
Egypt: 6 IPs (196.x range)
Other African nations: 4 IPs

🇮🇳 INDIA - 41 IPs (5.5%)
Primary Ranges: 103.x (mixed with other Asian countries)

Concentrated in 103.x addressing space
Mix of ISPs and mobile networks

🇧🇷 BRAZIL - 23 IPs (3.1%)
Primary Ranges: 177.x, 179.x, 181.x, 186-191.x, 200-201.x

Distributed across major Brazilian ISPs
Mix of residential and commercial ranges

🇪🇺 EUROPE - 67 IPs (8.9%)
Country breakdown:

Germany: 17 IPs
Netherlands: 14 IPs
France: 11 IPs
United Kingdom: 9 IPs
Russia: 8 IPs
Other EU countries: 8 IPs

🌏 OTHER ASIA-PACIFIC - 40 IPs (5.3%)
Countries:

Vietnam: 12 IPs
Thailand: 9 IPs
Indonesia: 8 IPs
Philippines: 6 IPs
South Korea: 3 IPs
Japan: 2 IPs

It is interesting to see the high level details around the IP addresses. I am curious how they would differ if my IP address was in a different block; maybe it wouldn't be so high if it was hosted by a different provider or in a different region.

Three More Rules!

Three more rules to help limit traffic, improve security and hopefully provide hosters will better usage data. Thinking logically about who you are trying to attract, what you are hosting and the tech stack you are using can all be helpful when defining rules.

Ban Random Scans

Seeing a flurry of these logs:

xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:09 +0000] "GET /hk.php HTTP/1.1" 301 178 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:09 +0000] "GET /hk.php HTTP/1.1" 404 196 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:09 +0000] "GET /hook.php HTTP/1.1" 301 178 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:09 +0000] "GET /hook.php HTTP/1.1" 404 196 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:09 +0000] "GET /atomlib.php HTTP/1.1" 301 178 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:09 +0000] "GET /atomlib.php HTTP/1.1" 404 196 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:09 +0000] "GET /geck.php HTTP/1.1" 301 178 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:10 +0000] "GET /geck.php HTTP/1.1" 404 196 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:10 +0000] "GET /file88.php HTTP/1.1" 301 178 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:10 +0000] "GET /file88.php HTTP/1.1" 404 196 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:10 +0000] "GET /gold.php HTTP/1.1" 301 178 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:10 +0000] "GET /gold.php HTTP/1.1" 404 196 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:10 +0000] "GET /moo.php HTTP/1.1" 301 178 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:10 +0000] "GET /moo.php HTTP/1.1" 404 196 "-" "-"
xxx.xxx.xxx.xxx - - [23/Oct/2025:11:09:10 +0000] "GET /file2.php HTTP/1.1" 301 178 "-" "
xxx.xxx.xxx.xxx are the redacted IP address.

This is an easy rule for me to make a ban around. I run a static html site. I do not have any PHP. If someone is looking for PHP, that is an instant red flag for me. So, let's make a rule banning a get request, ending in a ".php" that is a 301 or 404.

Create the filter.

# /etc/fail2ban/filter.d/php-scan.conf
[Definition]
failregex = ^ .* "GET .*\.php HTTP/.*" (301|404) .*$
ignoreregex =

Create the jail and add the following.

# /etc/fail2ban/jail.d/jail.local
[php-scan]
enabled = true
port = http,https
filter = php-scan
logpath = /var/log/nginx/access.log
maxretry = 3
findtime = 300
bantime = 86400
action = iptables-multiport[name=php-scan, port="http,https", protocol=tcp]

If you did run a [custom] PHP site, you could modify this filter to look for folders exclusive to Wordpress sites as an example. Spoiler Alert, there are a lot of scans for Wordpress specific paths.

Ban Bots

This rule could go either way, some people want to ban bots, others will want to have them. Either way, I am sure both parties want to have control over what the bots access and which bots scrape their site. One relevent, present day use case, is banning bots from AI companies as an example.

 # Using AI to block AI!
failregex = ^ .* ".*" \d+ \d+ ".*" ".*(ChatGPT-User|GPTBot|ChatGPT|OpenAI|openai\.com|CCBot.*OpenAI).*"$
ignoreregex =

(You should be able to use the previous jail and make the appropriate modifications - yes, you can do it!)

This is simple a rule based on the bot metadata. This is not guarenteed to work perfect, as there have been reports of companies ignoring general bot practices when it comes to AI companies. You may want to enchance rules based on activity or IP blocks... speaking of...

Ban IP Ranges

Lastly, create rules based on IP addresses. This is a method of limiting access to geo regions or even avoiding blocks from certain cloud providers. There are two things to call out, 1. IP addresses aren't perfect when it comes to specific regions and 2. VPNs are a fairly simple way to mitigate this rule. This rule should still be considered low hanging fruit, low effort and high reward.

 # Blocking the 103.0.0.0/8 block!
failregex = ^103\.\d+\.\d+\.\d+ .*
ignoreregex =

(Same goes for the jail on this one. You are doing great! Try again!)

Remember!

Just a few reminders, and tips to help when creating rules:

Also, this is a way to assist other tools and help augment your configuration. Ensure that all configurations are set up in layers. IE. Disable PHP even though you don't use it on the server. Block access to IP address at the firewall or network level, before the proxy. Use a robots.txt.

I hope this helps you on your journey to hosting a more secure and efficient site!