You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(security): mask IPv6 rate-limit keys to the routed /64
A single IPv6 client is delegated a whole /64 — the standard residential and
cloud allocation — so it can legitimately source every request from a different
address. Keying a per-IP throttle on the full /128 therefore left the exact
bypass this fix exists to close wide open over IPv6, with no header spoofing at
all: the proxy itself writes the varying value and nothing looks wrong.
Mask IPv6 to its routed prefix when producing a key, so one subscriber is one
bucket. Matches Better Auth's `ipv6Subnet` default, so session and throttle keys
agree. IPv4 is untouched — a v4 address is already a single host.
Masking happens only where a key is produced, never before the trusted-proxy
comparison, which must see the full address. `getAssertedOriginIp` stays
unmasked: the webhook allowlist needs the exact sender.
Also corrects what the docs claim about Better Auth. With no trusted proxies
configured it does not walk the chain — `getIPFromHeader` returns null for any
multi-value header — so the previous wording (and the env.ts line this replaces,
which had been accurate) overstated the agreement between the two. Each surface
now states where they align and where they deliberately differ, warns against a
trusted range broad enough to cover clients, and notes that none of it helps an
app exposed without a proxy.
- values.schema.json carried the same stale claim as values.yaml
- profound.ts compares against UNKNOWN_CLIENT_IP instead of a bare literal
- cover the env -> parseTrustedProxies wiring, which was globally mocked and so
never executed in CI, and de-vacuum the IPv6/IPv4 kind-mismatch test
# INTERNAL_API_BASE_URL=http://sim-app.default.svc.cluster.local:3000 # Optional: internal URL for server-side /api self-calls; defaults to NEXT_PUBLIC_APP_URL
21
21
# TRUSTED_ORIGINS=https://www.example.com,https://app.example.com # Optional: comma-separated additional public origins to trust for auth (apex+www, alias domains). Merged into Better Auth trustedOrigins.
22
-
# AUTH_TRUSTED_PROXIES=10.0.0.0/24,192.0.2.10 # Optional: reverse-proxy IPs/CIDRs in front of the app. Better Auth and Sim's own per-IP throttles walk x-forwarded-for right to left, skip these hops, and use the first untrusted address as the client IP (the leftmost entry is caller-supplied and would otherwise let anyone mint a fresh rate-limit bucket per request). Unsettrusts no hop and keys on the rightmostentry — safe, but a multi-hop chain collapses callers onto the edge addresses. Use your proxies' actual addresses, not broad private ranges that also cover clients.
22
+
# AUTH_TRUSTED_PROXIES=10.0.0.0/24,192.0.2.10 # Optional: reverse-proxy IPs/CIDRs in front of the app. When set, Better Auth and Sim's own per-IP throttles both walk x-forwarded-for right to left, skip these hops, and use the first untrusted address as the client IP (the leftmost entry is caller-supplied and would otherwise let anyone mint a fresh rate-limit bucket per request). Unset, the two differ: Better Auth trusts only single-value headers, while Sim's throttles key on the rightmost, proxy-written entry — never spoofable, but a multi-hop chain collapses callers onto the edge addresses. Use your proxies' actual addresses, NOT broad private ranges that also cover clients: a caller whose own address falls inside a trusted range makes the whole chain trusted.
23
23
24
24
# Chat (Optional)
25
25
# COPILOT_API_KEY= # Mint one at https://sim.ai. Without it the Sim Chat block, prompt jobs, and Inbox cannot run
REACT_SCAN_ENABLED: z.boolean().optional(),// Enable React Scan for performance debugging (dev only)
508
508
509
509
// Network / proxy trust
510
-
AUTH_TRUSTED_PROXIES: z.string().optional(),// Comma-separated reverse-proxy IPs or CIDR ranges. Better Auth and getClientIp (per-IP rate-limit keys, audit rows) walk the forwarded-IP chain right to left, skip these trusted hops, and use the first untrusted address as the client IP. Unset trusts no hop and keys on the rightmost, proxy-written entry — never the caller-supplied leftmost one.
510
+
AUTH_TRUSTED_PROXIES: z.string().optional(),// Comma-separated reverse-proxy IPs or CIDR ranges. When set, Better Auth and getClientIp (per-IP rate-limit keys, audit rows) both walk the forwarded-IP chain right to left, skip these trusted hops, and use the first untrusted address as the client IP. Leave unset and the two differ: Better Auth trusts only single-value IP headers (recording no IP for a multi-hop chain), while getClientIp keys on the rightmost, proxy-written entry — never the caller-supplied leftmost one.
Copy file name to clipboardExpand all lines: helm/sim/values.schema.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -157,7 +157,7 @@
157
157
},
158
158
"AUTH_TRUSTED_PROXIES": {
159
159
"type": "string",
160
-
"description": "Comma-separated reverse-proxy IPs or CIDR ranges in front of the app (e.g. '10.0.0.0/16'). Better Auth walks x-forwarded-for right to left, skips these hops, and uses the first untrusted address as the client IP."
160
+
"description": "Comma-separated reverse-proxy IPs or CIDR ranges in front of the app (e.g. the ingress pods, '10.42.0.0/24'). When set, Better Auth and Sim's per-IP rate limits both walk x-forwarded-for right to left, skip these hops, and use the first untrusted address as the client IP. Leave empty and the two differ: Better Auth trusts only a single-value header, while Sim's throttles key on the rightmost, proxy-written entry. Do not use a range broad enough to also cover client traffic — a caller inside a trusted range makes the whole chain trusted."
0 commit comments