Name every rate limit that shares the client key
check / check (push) Waiting to run

"Trusted proxies" now says a block covering clients makes every rate
limit bypassable, not "all three"; Rate Limiting lists delivery replay
and event resubmit among the limiters sharing the key. The
Configuration table and the TrustedProxies comment say a private
client behind a trusted proxy chooses its key, not behind any proxy.

Model: opus-5-5
This commit is contained in:
2026-10-01 20:01:45 +00:00
parent 70e708ee4f
commit 8b0854ccb7
2 changed files with 12 additions and 12 deletions
+9 -9
View File
@@ -147,7 +147,7 @@ TTY detection, and security headers are always applied.
| `RETENTION_SWEEP_INTERVAL` | How often the retention reaper and archive sweeper run (Go duration, must be positive) | `1h` | | `RETENTION_SWEEP_INTERVAL` | How often the retention reaper and archive sweeper run (Go duration, must be positive) | `1h` |
| `SESSION_IDLE_TIMEOUT` | Idle session timeout (Go duration) | `24h` | | `SESSION_IDLE_TIMEOUT` | Idle session timeout (Go duration) | `24h` |
| `RECEIVER_RATE_LIMIT` | Receiver requests/minute per IP per entrypoint (10x that per IP across the route) | `120` | | `RECEIVER_RATE_LIMIT` | Receiver requests/minute per IP per entrypoint (10x that per IP across the route) | `120` |
| `TRUSTED_PROXIES` | CIDRs whose forwarded headers are trusted. A set value replaces the default. Under the default, any client with a private address, whether it connects directly or through the proxy, can choose its own rate-limit key by sending its own `X-Forwarded-For`; if any clients have private addresses, set it to the proxy's address alone. See [Trusted proxies](#trusted-proxies) | `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16` (RFC 1918) | | `TRUSTED_PROXIES` | CIDRs whose forwarded headers are trusted. A set value replaces the default. Under the default, any client with a private address, whether it connects directly or through a trusted proxy, can choose its own rate-limit key by sending its own `X-Forwarded-For`; if any clients have private addresses, set it to the proxy's address alone. See [Trusted proxies](#trusted-proxies) | `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16` (RFC 1918) |
| `ALLOWED_EGRESS_CIDRS` | CIDRs that delivery targets may reach despite the SSRF blocklist. Read [Allowing egress to your own network](#allowing-egress-to-your-own-network) before setting it | `""` (none) | | `ALLOWED_EGRESS_CIDRS` | CIDRs that delivery targets may reach despite the SSRF blocklist. Read [Allowing egress to your own network](#allowing-egress-to-your-own-network) before setting it | `""` (none) |
#### Allowing egress to your own network #### Allowing egress to your own network
@@ -447,8 +447,8 @@ Two operator requirements follow:
it can name a different address on every request to get a fresh it can name a different address on every request to get a fresh
bucket each time, or name another client's address to drain that bucket each time, or name another client's address to drain that
client's bucket. A block that also covers clients — the default, on client's bucket. A block that also covers clients — the default, on
a network where clients have private addresses — makes all three a network where clients have private addresses — makes every rate
limits, including the unauthenticated webhook receiver, silently limit, including the unauthenticated webhook receiver's, silently
bypassable by every client in the block. bypassable by every client in the block.
#### Sessions #### Sessions
@@ -2552,12 +2552,12 @@ the tree is checked out: four checkouts have reported 3,959, 3,961,
client-supplied field was cut, and that the shipped chain's stack client-supplied field was cut, and that the shipped chain's stack
arrived uncut — never the numbers. arrived uncut — never the numbers.
Every limiter here — receiver, login, and password change — identifies Every limiter here — receiver, login, password change, delivery replay
the client the same way, through one shared key function: the and event resubmit — identifies the client the same way, through one
connection's own address, unless the peer is inside shared key function: the connection's own address, unless the peer is
`TRUSTED_PROXIES`, in which case the forwarded client address is used inside `TRUSTED_PROXIES`, in which case the forwarded client address is
instead. That address becomes a bucket by family: IPv4 keys on the full used instead. That address becomes a bucket by family: IPv4 keys on
address, IPv6 on its `/64` prefix. A routed `/64` is the normal the full address, IPv6 on its `/64` prefix. A routed `/64` is the normal
residential and mobile IPv6 allocation, so keying IPv6 per address would residential and mobile IPv6 allocation, so keying IPv6 per address would
let one subscriber rotate source addresses and mint a fresh bucket per let one subscriber rotate source addresses and mint a fresh bucket per
request, evading these limits at the network layer without spoofing request, evading these limits at the network layer without spoofing
+3 -3
View File
@@ -182,9 +182,9 @@ type Config struct {
// Other peers' forwarded headers are ignored and they are // Other peers' forwarded headers are ignored and they are
// identified by the connection's own address. Under the // identified by the connection's own address. Under the
// default any client with a private address, directly or // default any client with a private address, directly or
// through a proxy, can choose its own rate-limit key, so // through a trusted proxy, can choose its own rate-limit
// where any clients have private addresses this must be set // key, so where any clients have private addresses this must
// to the proxy hosts alone. // be set to the proxy hosts alone.
TrustedProxies []netip.Prefix TrustedProxies []netip.Prefix
// AllowedEgressCIDRs is the set of networks a delivery target // AllowedEgressCIDRs is the set of networks a delivery target