fix: harden LDAP STS rate-limit source bucketing

Remove the per-username LDAP STS throttle bucket and keep the limiter keyed only by source IP. A username bucket is shared across all clients and lets one source keep a known account's bucket drained with bad-password attempts, locking the legitimate user out before LDAP bind.

For trusted proxies, stop trusting the left-most forwarded address. Resolve X-Forwarded-For right-to-left, skip trusted proxy hops, reject catch-all trusted-proxy CIDRs, and intentionally ignore RFC 7239 Forwarded for this security-sensitive bucket. Document that X-Real-IP is trusted verbatim and must be overwritten by the trusted proxy, not passed through from clients.

Update focused limiter, source-IP, trusted-proxy, and LDAP config tests to cover source-only buckets, spoofed appended XFF, multi-hop trusted proxies, Forwarded fallback, catch-all rejection, and the X-Real-IP deployment contract.

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude Code <claude-code@anthropic.com>
This commit is contained in:
Feng Ruohang
2026-06-12 17:09:24 +08:00
parent f48dbe777d
commit 5e40665acd
5 changed files with 170 additions and 120 deletions
@@ -74,3 +74,12 @@ func TestSetSTSTrustedProxiesRejectsInvalidEntries(t *testing.T) {
t.Fatal("expected invalid trusted proxy list to fail")
}
}
func TestSetSTSTrustedProxiesRejectsCatchAll(t *testing.T) {
for _, value := range []string{"0.0.0.0/0", "::/0", "192.0.2.0/24,0.0.0.0/0"} {
var cfg Config
if err := cfg.SetSTSTrustedProxies(value); err == nil {
t.Fatalf("expected catch-all trusted proxy %q to be rejected", value)
}
}
}