Fail2Ban
From Log Noise to Active Defense
When I deployed my Speech Coach bot, I expected a quiet side-project setup: NGINX, webhook, basic frontend.
Instead, the first log review looked like a low-grade attack stream:
- requests to
.env,.git,wp-login.php,admin.php - repeated probing from rotating IPs
- constant retries against endpoints that did not exist
Nothing was compromised, but the message was clear: the internet found the server immediately.
What I Changed
I built the protection in two layers.
Layer 1: Tight NGINX boundaries
First, I reduced what was even reachable.
- The webhook endpoint was restricted to Telegram IP ranges and local testing.
- Static hosting was limited to build artifacts only, not the project root.
- Hidden and temporary files were blocked at the proxy level.
location /webhook/ {
proxy_pass http://host.docker.internal:8000/webhook/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
allow 149.154.160.0/20;
allow 91.108.4.0/22;
allow 192.168.1.0/22;
deny all;
}
location ~ /\.(?!well-known).* { deny all; }
location ~* \.(bak|swp|tmp|log|sql|old|orig)$ { deny all; }
Layer 2: Fail2Ban automation
Blocking is good, but manual blocking does not scale.
I added Fail2Ban to watch NGINX access logs and ban repeat scanners automatically.
[Definition]
failregex = ^<HOST> - - \[.*\] "(GET|POST|HEAD) .*(\.env|\.git|admin|login).* HTTP/.*" (403|404|405)
[nginx-badbots]
enabled = true
filter = nginx-badbots
action = iptables[name=BadBots, port=http, protocol=tcp]
logpath = /var/log/nginx/access.log
findtime = 600
bantime = 3600
maxretry = 3
Outcome
Within about 30 minutes, dozens of hostile IPs were automatically banned.
After that, logs became cleaner and easier to reason about, and the bot infrastructure became noticeably more stable under background internet noise.
Why This Case Matters
This was a small project, but it reflects how I approach production reliability:
- treat “small” services as real public systems
- put clear boundaries at the edge
- automate repetitive defense actions
- optimize for observability, not only for blocking