From 1eb3c738b17a854dc7cc10e1cf7c4be5845037e5 Mon Sep 17 00:00:00 2001 From: micbar Date: Fri, 6 Nov 2026 14:34:28 +0100 Subject: [PATCH] fix: filter health checks from the pod ip The health check calls the address the server listens on. For 0.0.0.0 that is the IP of the pod, not the loopback address. Compare the peer with the local address of the connection instead. --- pkg/checks/checkhttp.go | 28 +++++++++++++++++++++++----- pkg/checks/checkhttp_test.go | 19 ++++++++++++------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/pkg/checks/checkhttp.go b/pkg/checks/checkhttp.go index 9293a2c108..e8971d4458 100644 --- a/pkg/checks/checkhttp.go +++ b/pkg/checks/checkhttp.go @@ -48,15 +48,33 @@ func NewHTTPCheck(url string) func(context.Context) error { // IsLocalHealthCheck reports whether r is a request of NewHTTPCheck, so an // access log can leave it out. The user agent alone would let any client hide -// its requests, so the request must also come from the loopback address. +// its requests, so the connection must also come from the server's own +// address: the check calls the address the server listens on, for 0.0.0.0 +// that is the IP of the host or pod, not the loopback address. func IsLocalHealthCheck(r *http.Request) bool { if r.UserAgent() != HealthCheckUserAgent { return false } - host, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { + local, ok := r.Context().Value(http.LocalAddrContextKey).(net.Addr) + if !ok { return false } - a, err := netip.ParseAddr(host) - return err == nil && a.Unmap().IsLoopback() + peer, ok := addrIP(r.RemoteAddr) + if !ok { + return false + } + own, ok := addrIP(local.String()) + return ok && peer == own +} + +func addrIP(hostport string) (netip.Addr, bool) { + host, _, err := net.SplitHostPort(hostport) + if err != nil { + return netip.Addr{}, false + } + a, err := netip.ParseAddr(host) + if err != nil { + return netip.Addr{}, false + } + return a.Unmap().WithZone(""), true } diff --git a/pkg/checks/checkhttp_test.go b/pkg/checks/checkhttp_test.go index 735d7ddb56..bc35961e22 100644 --- a/pkg/checks/checkhttp_test.go +++ b/pkg/checks/checkhttp_test.go @@ -2,6 +2,7 @@ package checks import ( "context" + "net" "net/http" "net/http/httptest" "testing" @@ -25,16 +26,20 @@ func TestNewHTTPCheck(t *testing.T) { func TestIsLocalHealthCheck(t *testing.T) { for name, tc := range map[string]struct { - remoteAddr, userAgent string - want bool + localAddr, remoteAddr, userAgent string + want bool }{ - "check from ipv4 loopback": {"127.0.0.1:51234", HealthCheckUserAgent, true}, - "check from ipv6 loopback": {"[::1]:51234", HealthCheckUserAgent, true}, - "client sending the agent": {"203.0.113.7:51234", HealthCheckUserAgent, false}, - "proxy in front": {"10.0.0.5:51234", HealthCheckUserAgent, false}, - "local browser": {"127.0.0.1:51234", "Mozilla/5.0", false}, + "check to the pod ip": {"10.21.130.74:9200", "10.21.130.74:51234", HealthCheckUserAgent, true}, + "check to the loopback": {"127.0.0.1:9200", "127.0.0.1:51234", HealthCheckUserAgent, true}, + "check to ipv6": {"[fd00::5]:9200", "[fd00::5]:51234", HealthCheckUserAgent, true}, + "ingress sending the agent": {"10.21.130.74:9200", "10.21.64.12:51234", HealthCheckUserAgent, false}, + "client sending the agent": {"10.21.130.74:9200", "203.0.113.7:51234", HealthCheckUserAgent, false}, + "local browser": {"127.0.0.1:9200", "127.0.0.1:51234", "Mozilla/5.0", false}, } { r := httptest.NewRequest(http.MethodHead, "/", nil) + local, err := net.ResolveTCPAddr("tcp", tc.localAddr) + require.NoError(t, err) + r = r.WithContext(context.WithValue(r.Context(), http.LocalAddrContextKey, local)) r.RemoteAddr = tc.remoteAddr r.Header.Set("User-Agent", tc.userAgent) assert.Equal(t, tc.want, IsLocalHealthCheck(r), name)