diff --git a/internal/handlers/proxy.go b/internal/handlers/proxy.go index 4e5dc966b..f095b1fdb 100644 --- a/internal/handlers/proxy.go +++ b/internal/handlers/proxy.go @@ -26,6 +26,9 @@ import ( "net/http" "regexp" "strings" + + "github.com/minio/minio/internal/config" + "github.com/minio/pkg/v3/env" ) var ( @@ -51,6 +54,9 @@ var ( protoRegex = regexp.MustCompile(`(?i)^(;|,| )+(?:proto=)(https|http)`) ) +// Used to disable all processing of the X-Forwarded-For header in source IP discovery. +var enableXFFHeader = env.Get("_MINIO_API_XFF_HEADER", config.EnableOn) == config.EnableOn + // GetSourceScheme retrieves the scheme from the X-Forwarded-Proto and RFC7239 // Forwarded headers (in that order). func GetSourceScheme(r *http.Request) string { @@ -84,29 +90,35 @@ func GetSourceScheme(r *http.Request) string { func GetSourceIPFromHeaders(r *http.Request) string { var addr string - if fwd := r.Header.Get(xForwardedFor); fwd != "" { - // Only grab the first (client) address. Note that '192.168.0.1, - // 10.1.1.1' is a valid key for X-Forwarded-For where addresses after - // the first may represent forwarding proxies earlier in the chain. - s := strings.Index(fwd, ", ") - if s == -1 { - s = len(fwd) + if enableXFFHeader { + if fwd := r.Header.Get(xForwardedFor); fwd != "" { + // Only grab the first (client) address. Note that '192.168.0.1, + // 10.1.1.1' is a valid key for X-Forwarded-For where addresses after + // the first may represent forwarding proxies earlier in the chain. + s := strings.Index(fwd, ", ") + if s == -1 { + s = len(fwd) + } + addr = fwd[:s] } - addr = fwd[:s] - } else if fwd := r.Header.Get(xRealIP); fwd != "" { - // X-Real-IP should only contain one IP address (the client making the - // request). - addr = fwd - } else if fwd := r.Header.Get(forwarded); fwd != "" { - // match should contain at least two elements if the protocol was - // specified in the Forwarded header. The first element will always be - // the 'for=' capture, which we ignore. In the case of multiple IP - // addresses (for=8.8.8.8, 8.8.4.4, 172.16.1.20 is valid) we only - // extract the first, which should be the client IP. - if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 { - // IPv6 addresses in Forwarded headers are quoted-strings. We strip - // these quotes. - addr = strings.Trim(match[1], `"`) + } + + if addr == "" { + if fwd := r.Header.Get(xRealIP); fwd != "" { + // X-Real-IP should only contain one IP address (the client making the + // request). + addr = fwd + } else if fwd := r.Header.Get(forwarded); fwd != "" { + // match should contain at least two elements if the protocol was + // specified in the Forwarded header. The first element will always be + // the 'for=' capture, which we ignore. In the case of multiple IP + // addresses (for=8.8.8.8, 8.8.4.4, 172.16.1.20 is valid) we only + // extract the first, which should be the client IP. + if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 { + // IPv6 addresses in Forwarded headers are quoted-strings. We strip + // these quotes. + addr = strings.Trim(match[1], `"`) + } } } diff --git a/internal/handlers/proxy_test.go b/internal/handlers/proxy_test.go index ccd415df2..952e75745 100644 --- a/internal/handlers/proxy_test.go +++ b/internal/handlers/proxy_test.go @@ -84,3 +84,27 @@ func TestGetSourceIP(t *testing.T) { } } } + +func TestXFFDisabled(t *testing.T) { + req := &http.Request{ + Header: http.Header{ + xForwardedFor: []string{"8.8.8.8"}, + xRealIP: []string{"1.1.1.1"}, + }, + } + // When X-Forwarded-For and X-Real-IP headers are both present, X-Forwarded-For takes precedence. + res := GetSourceIP(req) + if res != "8.8.8.8" { + t.Errorf("wrong header, xff takes precedence: got %s, want: 8.8.8.8", res) + } + + // When explicitly disabled, the XFF header is ignored and X-Real-IP is used. + enableXFFHeader = false + defer func() { + enableXFFHeader = true + }() + res = GetSourceIP(req) + if res != "1.1.1.1" { + t.Errorf("wrong header, xff is disabled: got %s, want: 1.1.1.1", res) + } +}