
Homelab 18: ban the scanners with fail2ban, and the Docker trap that makes bans useless
As soon as ports 80 and 443 are open, scanners arrive: thousands of requests for admin pages, old exploits, passwords guessed in a loop. fail2ban reads your proxy's logs and bans an address for a while after a few errors. It is simple and effective, with one condition that a Docker setup quietly breaks.
A bit of history
fail2ban dates from 2004. Its idea has not aged: read the logs, count failures per address, and when an address fails too often, add a firewall rule against it for a while. It knows nothing about attacks; it only knows patterns in log lines, which is why a pattern that does not match your log format leaves it silently blind.
1. A filter for the proxy's logs
For Nginx Proxy Manager, a filter that catches errors and refused requests:
# filter.d/npm-docker.conf
[Definition]
failregex = ^.* \[error\] \d+#\d+: \*\d+ .*client: <HOST>,.*$
^<HOST> .* "(GET|POST|HEAD).*" (401|403|404|429) .*$
Test it against a real log before trusting it: fail2ban-regex /path/to/proxy-host-1_access.log filter.d/npm-docker.conf shows how many lines it would catch.
2. A jail, in the right chain
# jail.d/npm.conf
[npm-docker]
enabled = true
filter = npm-docker
logpath = /var/log/npm/proxy-host-*_access.log
/var/log/npm/proxy-host-*_error.log
maxretry = 5
findtime = 300
bantime = 3600
chain = DOCKER-USER
action = iptables-multiport[name=npm, port="80,443", protocol=tcp, chain="DOCKER-USER"]
Five errors in five minutes, one hour of ban.
The Docker trap
By default, fail2ban writes its bans in the INPUT chain of the firewall. Traffic to a port published by Docker is forwarded to the container and never crosses INPUT: the ban exists, the counters rise, and the attacker keeps going. Docker provides a chain for your own rules on that path, DOCKER-USER: put the ban there.
Check where the traffic really goes, not only that a rule exists: the packet counters tell you.
sudo nft list ruleset | grep -A3 f2b # or: sudo iptables -L DOCKER-USER -v -n
Then ban an address you control, from your phone on mobile data, and check that the site stops answering it.
Traps
Do not ban your own network. Add your local range and your private network to ignoreip, or a misbehaving script at home locks you out of your own services.
A 404 alone is not an attack: a broken link on your site produces them too. Five in five minutes from the same address is a reasonable threshold; ban on your own logs for a week in "count only" mode first, and look at what would have been banned.
Sources
Found this useful? Tell us what to measure next.