Say the proxy is covered by default or by a set value
check / check (push) Successful in 3m56s

The rate-limit test comments on trustedProxyCIDR and forwardedKeyFor
said production needs TRUSTED_PROXIES set; another said only
operator-listed addresses are trusted. The README's Rate Limiting
paragraph said "listed in", and the login endpoint section assumed
every proxied deployment shares one bucket. All now match the
RFC 1918 default.

Model: opus-5-5
This commit is contained in:
2026-10-01 18:47:25 +00:00
parent 5892416aa4
commit 2de0a79ffd
2 changed files with 11 additions and 9 deletions
+3 -2
View File
@@ -2554,7 +2554,7 @@ arrived uncut — never the numbers.
Every limiter here — receiver, login, and password change — identifies Every limiter here — receiver, login, and password change — identifies
the client the same way, through one shared key function: the the client the same way, through one shared key function: the
connection's own address, unless the peer is listed in connection's own address, unless the peer is inside
`TRUSTED_PROXIES`, in which case the forwarded client address is used `TRUSTED_PROXIES`, in which case the forwarded client address is used
instead. That address becomes a bucket by family: IPv4 keys on the full instead. That address becomes a bucket by family: IPv4 keys on the full
address, IPv6 on its `/64` prefix. A routed `/64` is the normal address, IPv6 on its `/64` prefix. A routed `/64` is the normal
@@ -2592,7 +2592,8 @@ opposite directions:
The login `POST` is the one endpoint with no pre-emptive limiter in The login `POST` is the one endpoint with no pre-emptive limiter in
front of it, and that is deliberate. A limiter that spends budget on front of it, and that is deliberate. A limiter that spends budget on
arrival is a lockout in this deployment shape: sharing one bucket, a arrival is a lockout wherever clients share one bucket, as they do
behind a reverse proxy that `TRUSTED_PROXIES` does not cover: a
stranger sending five POSTs a minute — about 0.08 requests per second, stranger sending five POSTs a minute — about 0.08 requests per second,
from anywhere — keeps it permanently full, and the operator has no from anywhere — keeps it permanently full, and the operator has no
second administrative path. So the handler inverts the order: second administrative path. So the handler inverts the order:
+8 -7
View File
@@ -384,8 +384,8 @@ const (
// trustedProxyCIDR is the proxy network the forwarded-path // trustedProxyCIDR is the proxy network the forwarded-path
// tests configure, and trustedPeer an address inside it. A // tests configure, and trustedPeer an address inside it. A
// production deployment is required to run behind a reverse // production deployment is required to run behind a reverse
// proxy with TRUSTED_PROXIES set, so this is the shape the // proxy that TRUSTED_PROXIES covers, either by the default or by
// bucketing has to hold in. // a set value, so this is the shape the bucketing has to hold in.
trustedProxyCIDR = "10.0.0.0/8" trustedProxyCIDR = "10.0.0.0/8"
trustedPeer = "10.0.0.1:44444" trustedPeer = "10.0.0.1:44444"
) )
@@ -1097,8 +1097,9 @@ func TestPostRateLimit_IPv4IndependentPerAddress(t *testing.T) {
// that arrives from trustedPeer — a configured trusted proxy — and // that arrives from trustedPeer — a configured trusted proxy — and
// names forwarded as its client in X-Forwarded-For. That is the // names forwarded as its client in X-Forwarded-For. That is the
// production path: a deployment is required to run behind a reverse // production path: a deployment is required to run behind a reverse
// proxy with TRUSTED_PROXIES set, so the forwarded address, not the // proxy that TRUSTED_PROXIES covers, either by the default or by a
// peer, is what the limiters bucket on there. // set value, so the forwarded address, not the peer, is what the
// limiters bucket on there.
func forwardedKeyFor( func forwardedKeyFor(
t *testing.T, m *middleware.Middleware, forwarded string, t *testing.T, m *middleware.Middleware, forwarded string,
) string { ) string {
@@ -1178,9 +1179,9 @@ func TestRateLimitKey_ForwardedIPv6BucketsByPrefix(t *testing.T) {
// //
// Every existing test of this fallback uses an IPv4 proxy, where // Every existing test of this fallback uses an IPv4 proxy, where
// bucketKey is the identity function, so replacing the call with // bucketKey is the identity function, so replacing the call with
// peer.String() leaves the whole suite green. Only operator-listed // peer.String() leaves the whole suite green. Only addresses inside
// addresses reach this line and the fallback is fail-closed, so this // TRUSTED_PROXIES reach this line and the fallback is fail-closed, so
// pins behaviour rather than fixing a defect. // this pins behaviour rather than fixing a defect.
func TestRateLimitKey_TrustedPeerUnusableForwardedMasksPeer( func TestRateLimitKey_TrustedPeerUnusableForwardedMasksPeer(
t *testing.T, t *testing.T,
) { ) {