Rate limiting
Token bucket per IP z bounded memory, rozproszony limiting przez Redis (atomowy Lua) i fail-open. Konfiguracja i trusted proxies.
Token bucket per IP#
Każdy klient (IP) ma własny token bucket:
requests_per_minute— sustained rate (domyślnie 100),burst— maksymalny jednorazowy wystrzał (domyślnie 50).
rate_limit:
requests_per_minute: 100
burst: 50
trusted_proxies: []
Bucket napełnia się ze stałą prędkością; każdy request zabiera token. Brak tokenu → 429 rate_limit_exceeded.
Bounded memory#
Limiter memory ma ograniczoną pamięć: wpisy bezczynnych klientów są usuwane (idle-entry eviction), więc atak z milionów unikalnych IP nie zjada RAM-u.
Trusted proxies#
Za load balancerem/CDN socket address to adres pośrednika, nie klienta. trusted_proxies mówi, którym pośrednikom ufać przy czytaniu X-Forwarded-For:
rate_limit:
trusted_proxies:
- 10.0.0.0/8
- 192.168.1.1
Bez trusted proxies nagłówki są ignorowane
Jeśli trusted_proxies jest puste, client-supplied X-Forwarded-For jest ignorowany, a limit liczony po socket address. Inaczej każdy klient mógłby spoofować IP i omijać limity.
Rozproszony rate limiting (Redis)#
Wiele instancji proxy może współdzielić jeden budżet limitów przez Redis:
redis:
addr: redis:6379
export REDIS_PASSWORD=secret
docker compose --profile redis up -d
Limity są egzekwowane atomowo w Redis skryptem Lua (token bucket), więc wszystkie instancje widzą ten sam stan licznika.
Fail-open#
Jeśli Redis jest nieosiągalny, ruch płynie dalej (availability over strict limiting), a błędy są liczone:
rate_limit_errors_total 3
To świadoma decyzja: lepiej przepuścić ruch niż zabić produkcję przy awarii Redis.
Przykłady konfiguracji#
Publiczne API#
rate_limit:
requests_per_minute: 60
burst: 20
trusted_proxies:
- 172.16.0.0/12
Wewnętrzny mikroserwis (hojny limit)#
rate_limit:
requests_per_minute: 6000
burst: 1000
Wiele instancji proxy + Redis#
redis:
addr: redis:6379
rate_limit:
requests_per_minute: 300
burst: 100
trusted_proxies:
- 10.0.0.0/8
Metryki#
| Metryka | Opis |
|---|---|
rate_limit_rejected_total | Requesty odrzucone z 429 |
rate_limit_errors_total | Błędy backendu limitingu (np. Redis down) |
curl -s http://localhost:8080/metrics | grep rate_limit
Testowanie limitów#
for i in $(seq 1 60); do
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/
done | sort | uniq -c
50 200
10 429
Pierwsze burst requestów przechodzi, kolejne dostają 429, aż bucket się napełni.