Add fail2ban 4xx/badbots filters, jail backend fix, and review #007

- Add reverse-proxy-4xx and reverse-proxy-badbots fail2ban filters
- Set backend=auto and ignoreip on all jails (fixes silent no-match
  when defaults-debian.conf inherits systemd backend)
- Document three-jail setup and REQUEST log format in README
- Add review #007 covering connection lifecycle, logging, and deployment
  drift triggered by the 2026-07-24 FD exhaustion incident
This commit is contained in:
glm-5.2 committed 2026-07-28 10:02:19 +00:00
1 parent f6e62a37ef
commit e803817350
5 files changed
+637 -7

No files matched your search

+26 -6
View File
@@ -236,17 +236,31 @@ unit file with security hardening options.
### fail2ban
Install the filter and jail config:
Three filters and jails are provided, covering rate-limited requests, repeated
40x errors, and known-bad scanner paths:
```bash
cp deploy/fail2ban/filter.d/reverse-proxy.conf /etc/fail2ban/filter.d/
cp deploy/fail2ban/jail.d/reverse-proxy.conf /etc/fail2ban/jail.d/
cp deploy/fail2ban/filter.d/reverse-proxy.conf /etc/fail2ban/filter.d/
cp deploy/fail2ban/filter.d/reverse-proxy-4xx.conf /etc/fail2ban/filter.d/
cp deploy/fail2ban/filter.d/reverse-proxy-badbots.conf /etc/fail2ban/filter.d/
cp deploy/fail2ban/jail.d/reverse-proxy.conf /etc/fail2ban/jail.d/
systemctl restart fail2ban
```
The filter matches `RATE_LIMIT` log lines from the proxy's structured log
output. The jail bans IPs after 10 rate-limited requests within 60 seconds
(adjust `maxretry` and `findtime` to taste).
| Jail | Filter matches | Max Retry | Find Time | Ban Time |
|------|----------------|-----------|-----------|----------|
| `reverse-proxy` | `RATE_LIMIT ... status=429` | 10 | 60s | 1h |
| `reverse-proxy-4xx` | `REQUEST ... status=(401\|403)` | 5 | 10m | 1h |
| `reverse-proxy-badbots` | known-bad paths (`.env`, `.git`, `/actuator`, `/wp-login.php`, PROPFIND, binary garbage, etc.) | 5 | 10m | 1h |
**Backend note**: The jails set `backend = auto` (file tailing via pyinotify).
If the default `backend = systemd` is inherited from `defaults-debian.conf`, it
will ignore `logpath` and read journald instead — silently matching nothing for
a file-logging proxy. Always set `backend = auto` explicitly on file-backed
jails.
**ignoreip**: The jails ignore `127.0.0.1/8`, `::1`, and `10.0.0.0/8` to
prevent the proxy from banning itself or trusted VPN clients.
Rate-limited requests produce log lines like:
@@ -254,6 +268,12 @@ Rate-limited requests produce log lines like:
RATE_LIMIT client_ip=203.0.113.50 host=git.example.com path=/login status=429
```
Proxied requests produce log lines like:
```
2026-07-28T08:49:20Z INFO reverse_proxy::proxy::handler: prefix="REQUEST" client_ip=203.0.113.50 host=git.example.com method=GET path=/ status=200 upstream=127.0.0.1:3000 duration_ms=45
```
For Docker deployments, mount the log directory so fail2ban on the host can
read it: