Trust the RFC 1918 ranges as proxies when TRUSTED_PROXIES is unset (closes #333)
check / check (push) Successful in 4m39s

Unset or empty, TRUSTED_PROXIES now defaults to 10.0.0.0/8,
172.16.0.0/12 and 192.168.0.0/16, so a reverse proxy reaching the app
over a Docker network or a private LAN gets per-client rate-limit
buckets without configuration. A set value replaces the default; an
unparseable one still fails startup.

The startup warning for an empty list goes, with its test hook and
test, since the default is no longer empty. The README's
configuration table, Trusted proxies, upaas and reverse-proxy sections
describe the new default and when to narrow it to the proxy alone.

Model: opus-5-5
This commit is contained in:
2026-09-29 07:29:26 +00:00
parent 4a724130ca
commit fc651abe46
10 changed files with 126 additions and 257 deletions
+4 -4
View File
@@ -108,10 +108,10 @@ type failureWindow struct {
//
// A limiter that spends budget on arrival cannot protect a
// single-admin product: behind the reverse proxy the deployment
// requires, with TRUSTED_PROXIES unset, every client keys on the
// proxy, so a stranger trickling five POSTs a minute keeps the one
// bucket full and the operator's own correct password is answered 429
// forever. There is no second administrative path.
// requires, when TRUSTED_PROXIES does not cover it, every client
// keys on the proxy, so a stranger trickling five POSTs a minute
// keeps the one bucket full and the operator's own correct password
// is answered 429 forever. There is no second administrative path.
//
// So budget is spent only by a FAILED verification. A correct
// password is never throttled, whatever the counters say, which is
+2 -3
View File
@@ -123,9 +123,8 @@ func bucketKey(addr netip.Addr) string {
return prefix.String()
}
// isTrustedProxy reports whether addr belongs to a network the
// operator listed in TRUSTED_PROXIES. The list is empty by default,
// so by default nothing is trusted.
// isTrustedProxy reports whether addr belongs to a network in
// TRUSTED_PROXIES, which by default is the RFC 1918 private ranges.
func (m *Middleware) isTrustedProxy(addr netip.Addr) bool {
for _, prefix := range m.params.Config.TrustedProxies {
if prefix.Contains(addr) {
+2 -2
View File
@@ -426,8 +426,8 @@ func assertSharedBucket(
}
// TestRateLimitKey_SpoofedForwardedFromUntrustedPeer is the test
// this gating exists for: with no trusted proxies configured (the
// default), a client that rotates a forwarded header on every
// this gating exists for: from a peer that is not a trusted
// proxy, a client that rotates a forwarded header on every
// request must stay in one bucket. If forwarded headers were
// trusted unconditionally, each spoofed value would mint a fresh
// bucket and the limit would stop no one.