From 9ecd987416320a8ac505fe0859e643b6b51503c9 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Tue, 3 Nov 2026 00:59:16 +0000 Subject: [PATCH] fix(bearer): key the failure tracker by IPv6 /64 The bearer failure tracker keyed entries by the full source address. One IPv6 host could create a new entry per request and grow the map for window plus penalty seconds. It now shares one entry per /64, like the login limiter. IPv4 keys do not change. --- bearer_auth.go | 5 +++-- bearer_failure_ipv6_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 bearer_failure_ipv6_test.go diff --git a/bearer_auth.go b/bearer_auth.go index 591799c..08f7ed4 100644 --- a/bearer_auth.go +++ b/bearer_auth.go @@ -558,9 +558,10 @@ func clientIPForBearer(req *http.Request) string { } host, _, err := net.SplitHostPort(req.RemoteAddr) if err != nil { - return req.RemoteAddr + host = req.RemoteAddr } - return host + // One IPv6 /64 shares a key, so a single host cannot mint 2^64 entries. + return limiterKey(host) } // --- Bearer auth entrypoint --- diff --git a/bearer_failure_ipv6_test.go b/bearer_failure_ipv6_test.go new file mode 100644 index 0000000..131280c --- /dev/null +++ b/bearer_failure_ipv6_test.go @@ -0,0 +1,24 @@ +package traefikoidc + +import ( + "net/http/httptest" + "testing" +) + +// Addresses in one IPv6 /64 must share one failure-tracker key. +func TestClientIPForBearerCollapsesIPv6Prefix(t *testing.T) { + key := func(addr string) string { + r := httptest.NewRequest("GET", "/", nil) + r.RemoteAddr = addr + return clientIPForBearer(r) + } + if a, b := key("[2001:db8:1:2::1]:1"), key("[2001:db8:1:2:ffff::9]:2"); a != b { + t.Fatalf("same /64 got different keys: %q vs %q", a, b) + } + if a, b := key("[2001:db8:1:2::1]:1"), key("[2001:db8:1:3::1]:1"); a == b { + t.Fatalf("different /64 shared key %q", a) + } + if got := key("192.0.2.7:80"); got != "192.0.2.7" { + t.Fatalf("ipv4 key = %q", got) + } +}