ADR-0019 — Rate limiting, quotas e WAF
- Status: Aceito
- Data: 2026-07-18 · Aceito em: 2026-07-18
Contexto
A RNF-06 e a Segurança citam rate limiting, mas sem limites, camadas ou responsáveis definidos. Precisamos proteger a API pública contra abuso/scraping e o fluxo de login OIDC (ADR-0013), sem afetar a ingestão interna.
Decisão
Defesa em camadas:
- Borda (Cloudflare): WAF gerenciado + regras contra bots e proteção de DDoS.
- Traefik (ingress):
rateLimitmiddleware por IP (burst + média) para toda a API pública. - Spring (aplicação): limites por usuário/rota com
bucket4j(respostas429+Retry-After), especialmente em rotas caras/sensíveis.
Quotas de partida (calibráveis):
| Superfície | Limite default |
|---|---|
| API pública anônima | 60 req/min por IP |
| API pública autenticada | 300 req/min por usuário |
Rotas de auth (/oauth2/**, /auth/refresh) | 10 req/min por IP |
Ingestão interna (/internal/**) | sem rate limit de borda — protegida por X-Internal-Token, não exposta no ingress |
429segue Problem Details (RFC 7807) comRetry-After.- Métrica de taxa de 429 e top-talkers na observabilidade (detectar abuso).
Arquitetura, robustez e escala
- Contadores distribuídos:
bucket4jcom Redis (ADR-0007) para os limites da aplicação, de modo que o limite valha entre todas as réplicas do backend (não por instância). Algoritmo token bucket (permite rajada controlada). - Chaves de limite: por IP (anônimo), por usuário (autenticado) e por rota; a chave correta evita punir usuários legítimos atrás de NAT compartilhado.
- Headers padrão: responder
RateLimit-Limit,RateLimit-Remaining,RateLimit-Resete, no429,Retry-After— clientes bem-comportados se auto-regulam. - Proteção de auth: limite estrito nas rotas OIDC/refresh + backoff/lockout progressivo e, se necessário, CAPTCHA como fallback contra brute-force de callback (ADR-0013).
- Borda: Cloudflare (WAF gerenciado, regras de bot, mitigação de DDoS volumétrico) + Traefik
rateLimitpor IP como segunda linha; a ingestão interna nunca é exposta na borda. - Degradação graciosa: se o Redis dos contadores cair, aplicar fail-open com limite local conservador por instância (não derrubar a API) e alertar.
- Configuração como dado: limites por rota em config versionada, ajustáveis sem redeploy; processo de tuning guiado por métricas.
- Observabilidade: taxa de
429por rota, top IPs/usuários, latência adicionada pelo middleware, hits de regra de WAF (ver pilares).
Consequências
- + Abuso/scraping contido em várias camadas; login protegido contra brute-force de callback.
- + Limites explícitos e observáveis; tuning por rota.
- − Risco de bloquear usuários legítimos atrás de NAT/corporativo → limites autenticados por usuário mitigam.
- − Estado de contadores (Redis) para limites distribuídos entre réplicas do backend.
Alternativas consideradas
- Só Cloudflare: não conhece usuário/rota nem regra de negócio — insuficiente sozinho.
- Só aplicação: deixa a borda exposta a floods antes de chegar ao Spring — rejeitado.