Rate-limit the public webhook receiver endpoint (closes #64)
All checks were successful
check / check (push) Successful in 5s
All checks were successful
check / check (push) Successful in 5s
The receiver was the one unauthenticated, internet-facing endpoint with no rate limit, so a misbehaving or hostile sender could flood a webhook without bound. RECEIVER_RATE_LIMIT (default 120/min) now caps it, keyed on client IP plus entrypoint path so one entrypoint cannot exhaust another's budget. Over-limit requests get 429 with Retry-After. The limiter deliberately does not reuse postRateLimit: that helper is POST-only and keys on IP alone, whereas the receiver must count every method. A test locks that property in. Config parsing follows the fail-loudly idiom: a set-but-unparseable or non-positive value aborts startup rather than falling back to the default. Known limitation, tracked in #88: the key still trusts forwarded headers unconditionally, so the limit is evadable by rotating X-Forwarded-For until trusted-proxy gating lands.
This commit was merged in pull request #87.
This commit is contained in:
31
README.md
31
README.md
@@ -94,6 +94,7 @@ TTY detection, and security headers are always applied.
|
||||
| `METRICS_PASSWORD` | Basic auth password for `/metrics` | `""` |
|
||||
| `SENTRY_DSN` | Sentry error reporting DSN | `""` |
|
||||
| `SESSION_IDLE_TIMEOUT` | Idle session timeout (Go duration) | `24h` |
|
||||
| `RECEIVER_RATE_LIMIT` | Receiver requests/minute per IP per entrypoint | `120` |
|
||||
|
||||
Sessions are bounded by two independent clocks, and end at whichever
|
||||
one runs out first:
|
||||
@@ -123,7 +124,8 @@ fatal configuration error: webhooker logs the offending variable and
|
||||
its value and refuses to start, rather than silently running with a
|
||||
substituted default. `PORT=eighty`, `DEBUG=ture`, and
|
||||
`RETENTION_SWEEP_INTERVAL=1 hour` all abort startup. `PORT` must
|
||||
additionally be a number in the range 1–65535.
|
||||
additionally be a number in the range 1–65535, and
|
||||
`RECEIVER_RATE_LIMIT` must be at least 1.
|
||||
|
||||
Boolean variables (`DEBUG`, `MAINTENANCE_MODE`) accept exactly the
|
||||
spellings Go's `strconv.ParseBool` accepts — `1`, `t`, `T`, `TRUE`,
|
||||
@@ -785,17 +787,24 @@ just delayed until the target is healthy again.
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
Global rate limiting middleware (e.g., per-IP throttling applied at the
|
||||
router level) **must not** apply to webhook receiver endpoints. Webhook
|
||||
endpoints receive automated traffic from external services at
|
||||
unpredictable rates, and blanket rate limits would cause legitimate
|
||||
deliveries to be dropped.
|
||||
Global blanket rate limiting middleware (e.g., a per-IP throttle shared
|
||||
with the web UI) **must not** apply to webhook receiver endpoints.
|
||||
Webhook endpoints receive automated traffic from external services at
|
||||
unpredictable rates, and blanket limits shared with other routes would
|
||||
cause legitimate deliveries to be dropped.
|
||||
|
||||
Instead, each webhook has its own individually configurable rate limit,
|
||||
applied within the webhook handler itself. By default, no rate limit is
|
||||
applied — webhook endpoints accept traffic as fast as it arrives. Rate
|
||||
limits can be configured per-webhook when needed (e.g., to protect
|
||||
against a misbehaving sender).
|
||||
The receiver instead has its own dedicated abuse limit, scoped to the
|
||||
`/webhook/{uuid}` route only and keyed per client IP per entrypoint: one
|
||||
misbehaving sender is throttled without affecting other senders of the
|
||||
same entrypoint or the same sender's other entrypoints. The limit is
|
||||
`RECEIVER_RATE_LIMIT` requests per minute (default 120, generous for
|
||||
legitimate webhook senders). Requests over the limit receive HTTP 429
|
||||
with a `Retry-After` header. A set-but-invalid `RECEIVER_RATE_LIMIT`
|
||||
value aborts startup rather than silently falling back to the default.
|
||||
|
||||
Finer-grained per-webhook rate limits (configured in the web UI and
|
||||
enforced in the webhook handler) can layer on top of this env-level
|
||||
abuse limit later; they are tracked as future work.
|
||||
|
||||
### API Endpoints
|
||||
|
||||
|
||||
Reference in New Issue
Block a user