diff --git a/common/protocol/http/headers.go b/common/protocol/http/headers.go index baf98ebf1..ca9c6277e 100644 --- a/common/protocol/http/headers.go +++ b/common/protocol/http/headers.go @@ -20,21 +20,29 @@ func parseXForwardedFor(xff string) []net.Address { return addrs } -// ParseTrustedXForwardedFor parses X-Forwarded-For only when a configured trusted header is present. +// ParseTrustedXForwardedFor parses forwarding headers only when a configured trusted header is present. func ParseTrustedXForwardedFor(header http.Header, trusted []string, clientAddr gonet.Addr) []net.Address { - xff := header.Get("X-Forwarded-For") - if xff == "" { + key := "X-Real-IP" + value := header.Get(key) + if value == "" { + key = "X-Forwarded-For" + value = header.Get(key) + } + if value == "" { return nil } - for _, key := range trusted { - if len(header.Values(key)) > 0 { - return parseXForwardedFor(xff) + for _, t := range trusted { + if len(header.Values(t)) > 0 { + if key == "X-Real-IP" { + return []net.Address{net.ParseAddress(value)} + } + return parseXForwardedFor(value) } } if len(trusted) == 0 { - errors.LogWarning(context.Background(), `received "X-Forwarded-For" from `, clientAddr, ` but "sockopt.trustedXForwardedFor" is not configured; ignoring it and using the real remote address`) + errors.LogWarning(context.Background(), `received "`, key, `" from `, clientAddr, ` but "sockopt.trustedXForwardedFor" is not configured; ignoring it and using the real remote address`) } else { - errors.LogError(context.Background(), `ignored potentially forged "X-Forwarded-For" from `, clientAddr, `: `, xff) + errors.LogError(context.Background(), `ignored potentially forged "`, key, `" from `, clientAddr, `: `, value) } return nil } diff --git a/common/protocol/http/headers_test.go b/common/protocol/http/headers_test.go index 3d56e44d3..81b456de5 100644 --- a/common/protocol/http/headers_test.go +++ b/common/protocol/http/headers_test.go @@ -14,19 +14,50 @@ import ( ) func TestParseTrustedXForwardedFor(t *testing.T) { - header := http.Header{} - header.Add("X-Forwarded-For", "129.78.138.66, 129.78.64.103") clientAddr := &gonet.TCPAddr{IP: gonet.ParseIP("127.0.0.1"), Port: 12345} - if addrs := ParseTrustedXForwardedFor(header, nil, clientAddr); addrs != nil { - t.Fatalf("unexpected trusted X-Forwarded-For: %v", addrs) - } + t.Run("ignore X-Forwarded-For without trusted header", func(t *testing.T) { + header := http.Header{} + header.Add("X-Forwarded-For", "129.78.138.66, 129.78.64.103") - header.Add("X-Trusted-CDN", "") - addrs := ParseTrustedXForwardedFor(header, []string{"X-Trusted-CDN"}, clientAddr) - if r := cmp.Diff(addrs, []net.Address{net.ParseAddress("129.78.138.66"), net.ParseAddress("129.78.64.103")}); r != "" { - t.Error(r) - } + if addrs := ParseTrustedXForwardedFor(header, nil, clientAddr); addrs != nil { + t.Fatalf("unexpected trusted X-Forwarded-For: %v", addrs) + } + }) + + t.Run("trust X-Forwarded-For", func(t *testing.T) { + header := http.Header{} + header.Add("X-Forwarded-For", "129.78.138.66, 129.78.64.103") + header.Add("X-Trusted-CDN", "") + + addrs := ParseTrustedXForwardedFor(header, []string{"X-Trusted-CDN"}, clientAddr) + if r := cmp.Diff(addrs, []net.Address{net.ParseAddress("129.78.138.66"), net.ParseAddress("129.78.64.103")}); r != "" { + t.Error(r) + } + }) + + t.Run("prefer X-Real-IP over X-Forwarded-For", func(t *testing.T) { + header := http.Header{} + header.Add("X-Real-IP", "10.0.0.1") + header.Add("X-Forwarded-For", "129.78.138.66, 129.78.64.103") + header.Add("X-Trusted-CDN", "") + + addrs := ParseTrustedXForwardedFor(header, []string{"X-Trusted-CDN"}, clientAddr) + if r := cmp.Diff(addrs, []net.Address{net.ParseAddress("10.0.0.1")}); r != "" { + t.Error(r) + } + }) + + t.Run("do not parse X-Real-IP as a list", func(t *testing.T) { + header := http.Header{} + header.Add("X-Real-IP", "8.8.8.8, 9.9.9.9") + header.Add("X-Trusted-CDN", "") + + addrs := ParseTrustedXForwardedFor(header, []string{"X-Trusted-CDN"}, clientAddr) + if r := cmp.Diff(addrs, []net.Address{net.ParseAddress("8.8.8.8, 9.9.9.9")}); r != "" { + t.Error(r) + } + }) } func TestHopByHopHeadersRemoving(t *testing.T) {