Public: anyone can read this; the crew can change it.
Limits for strangers see each outside client, from trusted proxy headers only #726
Check · Review · Land · Live done — Landed and live in production.
Closes #697
shipped production is serving it for issue #697 — its issue is already done deploy 4183511
Sign in to join this conversation.
new above
offline
Closes #697
Limits for strangers, and the signup audit, now see each outside client instead of the ingress in front of the forge.
What changed
src/http/peer.tsresolves the client once, when the listener notes a request. Forwarded headers are read only when the socket peer is a trusted proxy: this machine (as before) or a range inFORGE_TRUSTED_PROXIES. In that case:- a header the edge sets,
FORGE_CLIENT_IP_HEADER, is read first when it holds an address; - otherwiseX-Forwarded-Foris walked from the right, past each trusted hop, to the first address no trusted proxy vouches for. The left end, which the client wrote, cannot choose its bucket. With no setting, a public peer is the client and headers are ignored, as today.src/kernel/cidr.ts: strict IPv4 and IPv6 ranges, including IPv4-mapped IPv6 addresses. A malformed range inFORGE_TRUSTED_PROXIESfails the boot; nothing is skipped silently.clientAddress, so they inherit this. Their whole-forge caps are unchanged.What to set for production (documented in the README). The forge sits behind cloudflared → Traefik, and Traefik has no
forwardedHeaders.trustedIPs, so it rewritesX-Forwarded-Forto cloudflared's pod address. The real client is in Cloudflare'sCF-Connecting-IP, which Cloudflare overwrites at its edge. Set:(10.42.0.0/16 is the k3s pod range; Traefik is 10.42.0.166 today.) An alternative is giving Traefik
forwardedHeaders.trustedIPs=10.42.0.0/16, after which the right-to-leftX-Forwarded-Forwalk needs no named header. That is an edge change, so I left it to the owner.Two outside clients seen apart:
test/client-address.test.tsruns the forge behind a trusted proxy range. One forwarded client spends the OAuth registration minute (429), and a second client behind the same proxy is still served (201). Other tests: a trusted proxy's header separates two clients; the same header from an untrusted peer is ignored; a client-written leading hop is ignored; a chain of trusted proxies is walked; a named header is read only from a trusted proxy; CIDR parsing, and malformed entries refused. 10 tests, in the gate. The reports, report-discovery and OAuth suites still pass.🤖 Generated with Claude Code