Files
Rendezvous/docs/security/abuse-protection.md
T
KyuubiYoru 88ef946af5
quality-gate / quality (push) Failing after 1m5s
feat(server): harden hostile input and overload behavior (#15)
2026-07-16 12:37:38 +02:00

5.4 KiB

Hostile-input and overload protection

Tracking: #15

Rendezvous treats every public HTTP request and UDP datagram as hostile. The server applies bounded fixed-window request budgets and concurrency ceilings in two stages so malformed input is discarded before expensive work while valid traffic is also isolated by its authenticated scope.

Enforcement order

  1. Kestrel and the HTTP abuse middleware cap request bodies at 16 KiB. A known oversized body receives a typed 413 response before endpoint dispatch.
  2. Every HTTP request consumes global, source-prefix, and operation budgets and acquires the corresponding concurrency leases. IPv4 sources share a /24 budget and IPv6 sources share a /56 budget; raw addresses are not retained. Non-lease operations also consume a smaller optional-work budget, leaving a configured global and source-prefix reserve for renew, update, and delete operations during shedding. Health probes use their own source-prefix budget so public API overload cannot make a healthy instance fail its orchestrator probes, while health traffic is still bounded.
  3. Once an endpoint has safely derived identities, it also acquires applicable tenant, principal or capability, and listing/attempt budgets. Secret capabilities are represented only by bounded SHA-256 fingerprints.
  4. Every UDP envelope consumes global, source-prefix, and wire-operation budgets before decoding. A structurally and cryptographically valid request then consumes capability, role, and mediation-handle budgets before state mutation or introduction.
  5. HTTP overload returns the stable RateLimited error, status 429, and a bounded Retry-After value in both the header and response contract. UDP overload and every invalid UDP input are silently dropped.

The same HTTP identity budget is computed whether or not a listing or attempt exists. Rejection therefore does not disclose resource existence. Publisher authentication also completes before any tenant/resource operation, while the pre-authentication source budget prevents invalid credentials from bypassing load shedding.

Bounded state and recovery

Rendezvous:AbuseProtection:MaxTrackedKeys is a hard combined ceiling for rate and active-concurrency keys. General HTTP and UDP traffic cannot consume the configured CriticalTrackedKeyReserve; lease operations and health probes may use that reserve but never exceed the hard ceiling. A request that would exceed its applicable ceiling fails closed without adding state. Fixed-window rate keys are cleared at the next window boundary; concurrency keys are removed as their request leases finish. HTTP and UDP trackers have separate locks and cardinality partitions, so a UDP flood cannot block HTTP admission on a shared lock or consume HTTP key capacity. This gives deterministic burst recovery and prevents an attacker from growing a permanent high-cardinality address, credential, or resource table.

The complete default profile is checked into src/FinalFactory.Rendezvous.Server/appsettings.json. Operators may lower or tune limits for a measured deployment profile, but must preserve all dimensions and leave the tracker ceiling above the maximum simultaneous key set. A rolling deployment should use the same profile on every instance. These per-process limits are a final service boundary; an edge proxy may add stricter distributed limits but is not a substitute for them.

When an HTTP reverse proxy is used, every immediate proxy address must be allowlisted in Rendezvous:AbuseProtection:TrustedProxyAddresses (or indexed environment variables such as Rendezvous__AbuseProtection__TrustedProxyAddresses__0). Only one forwarded hop is accepted. With an empty allowlist, forwarded headers are ignored and the direct TCP peer is the source. Never add a broad network range or accept untrusted X-Forwarded-For input: that would let a caller choose its own rate partition.

Reflection, disclosure, and logging rules

  • UDP sends nothing for malformed, oversized, unauthenticated, stale, replayed, wrong-role, or rate-limited input.
  • Introductions are emitted only after both role-scoped capabilities bind to their observed gameplay-socket sources. HTTP never supplies a public introduction target.
  • Private candidates must be same-family private unicast addresses and are used only for peers observed behind the same public address.
  • Abuse keys, exceptions, and responses never include bearer credentials, capabilities, tickets, raw endpoints, metadata values, or hostile markup.
  • Endpoint and capability values are not used as metric labels or log fields.

Verification

The deterministic test corpora use the recorded seeds 0x152026, 0x154A50, and 0x1557A7E. They exercise 10,000 arbitrary UDP envelopes through the production decoder, 5,000 arbitrary HTTP/credential parser inputs, and 1,000 mutated state transitions, including the oversized and configured-capacity boundaries. Focused tests cover IPv4 and IPv6 prefix partitioning, tenant/principal/resource concurrency, tracker exhaustion, window recovery, wire-operation isolation, a steady-state allocation ceiling, typed 429/413 responses, secret fingerprint redaction, and silent authenticated UDP shedding. The existing state, contract, HTTP, client, and mediator suites continue to cover cross-tenant access, replay, role swaps, credential rotation, bounded metadata, endpoint validation, and one-shot amplification behavior.