Allow disabling of all X-Forwarded-For header processing (#20977)

This commit is contained in:
Mark Theunissen 2025-02-26 20:25:49 +01:00 committed by GitHub
parent f129fd48f2
commit 7cc0c69228
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 58 additions and 22 deletions

View File

@ -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], `"`)
}
}
}

View File

@ -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)
}
}