Add per-IP rate limiting to prevent finger-daemon abuse

The app sits behind nginx, which caches 200s for 30s — so repeated lookups
of the same user are cheap. What bypasses the cache is enumeration of distinct
usernames: each is a unique cache key -> miss -> a fresh finger call to the
mammut daemon, all attributed to admin's single IP (so the daemon cannot ban
the real source). The app only ever receives cache misses, so a per-IP limit
here throttles exactly that uncached path without touching the cached hot path.

- Flask-Limiter keyed per client IP: 30/min on the finger lookup endpoints,
  10/min on /api/upload (auth brute-force), 120/min global default. Index and
  the container healthcheck are exempt. All limits env-tunable (RATELIMIT_*).
- ProxyFix(x_for=1): trust nginx's X-Forwarded-For so the real client IP is
  used for keying and logging. Without it the app only saw the Docker bridge
  gateway (172.20.0.1) and every client shared one bucket.
- 429 handler (JSON for /api, HTML 429.html otherwise) and WARNING logging of
  failed/invalid lookups and limit hits, so enumeration is observable.
This commit is contained in:
pmb
2026-06-17 10:23:58 -07:00
parent ad4b9b9af8
commit 3115ac64b2
4 changed files with 109 additions and 6 deletions
+1
View File
@@ -6,3 +6,4 @@ itsdangerous==2.1.2
click==8.1.7
blinker==1.6.3
Flask-HTTPAuth==4.8.0
Flask-Limiter==3.5.1