From 62134d65af6847782a527612e71d8c1977119365 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Tue, 13 Jan 2026 16:52:56 -0300 Subject: [PATCH] reverseproxy: fix error when remote address is not an IP (#7429) --- .../caddyhttp/reverseproxy/headers_test.go | 34 +++++++++++++++++++ .../caddyhttp/reverseproxy/reverseproxy.go | 17 ++++++---- 2 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 modules/caddyhttp/reverseproxy/headers_test.go diff --git a/modules/caddyhttp/reverseproxy/headers_test.go b/modules/caddyhttp/reverseproxy/headers_test.go new file mode 100644 index 000000000..22f589141 --- /dev/null +++ b/modules/caddyhttp/reverseproxy/headers_test.go @@ -0,0 +1,34 @@ +package reverseproxy + +import ( + "context" + "net/http/httptest" + "testing" + + "github.com/caddyserver/caddy/v2/modules/caddyhttp" +) + +func TestAddForwardedHeadersNonIP(t *testing.T) { + h := Handler{} + + // Simulate a request with a non-IP remote address (e.g. SCION, abstract socket, or hostname) + req := httptest.NewRequest("GET", "/", nil) + req.RemoteAddr = "my-weird-network:12345" + + // Mock the context variables required by Caddy. + // We need to inject the variable map manually since we aren't running the full server. + vars := map[string]interface{}{ + caddyhttp.TrustedProxyVarKey: false, + } + ctx := context.WithValue(req.Context(), caddyhttp.VarsCtxKey, vars) + req = req.WithContext(ctx) + + // Execute the unexported function + err := h.addForwardedHeaders(req) + + // Expectation: No error should be returned for non-IP addresses. + // The function should simply skip the trusted proxy check. + if err != nil { + t.Errorf("expected no error for non-IP address, got: %v", err) + } +} diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go index 13bbee422..2ea17046a 100644 --- a/modules/caddyhttp/reverseproxy/reverseproxy.go +++ b/modules/caddyhttp/reverseproxy/reverseproxy.go @@ -789,16 +789,19 @@ func (h Handler) addForwardedHeaders(req *http.Request) error { // to pull that out before parsing the IP clientIP, _, _ = strings.Cut(clientIP, "%") ipAddr, err := netip.ParseAddr(clientIP) - if err != nil { - return fmt.Errorf("invalid IP address: '%s': %v", clientIP, err) - } // Check if the client is a trusted proxy trusted := caddyhttp.GetVar(req.Context(), caddyhttp.TrustedProxyVarKey).(bool) - for _, ipRange := range h.trustedProxies { - if ipRange.Contains(ipAddr) { - trusted = true - break + + // If ParseAddr fails (e.g. non-IP network like SCION), we cannot check + // if it is a trusted proxy by IP range. In this case, we ignore the + // error and treat the connection as untrusted (or retain existing status). + if err == nil { + for _, ipRange := range h.trustedProxies { + if ipRange.Contains(ipAddr) { + trusted = true + break + } } }