diff --git a/common/protocol/http/headers.go b/common/protocol/http/headers.go index ca9c6277e..e28bbebba 100644 --- a/common/protocol/http/headers.go +++ b/common/protocol/http/headers.go @@ -11,38 +11,24 @@ import ( "github.com/xtls/xray-core/common/net" ) -func parseXForwardedFor(xff string) []net.Address { - list := strings.Split(xff, ",") - addrs := make([]net.Address, 0, len(list)) - for _, proxy := range list { - addrs = append(addrs, net.ParseAddress(proxy)) - } - return addrs -} - // ParseTrustedXForwardedFor parses forwarding headers only when a configured trusted header is present. -func ParseTrustedXForwardedFor(header http.Header, trusted []string, clientAddr gonet.Addr) []net.Address { - key := "X-Real-IP" - value := header.Get(key) - if value == "" { - key = "X-Forwarded-For" - value = header.Get(key) - } +func ParseTrustedXForwardedFor(header http.Header, trusted []string, remoteAddr gonet.Addr) net.Address { + value := header.Get("X-Forwarded-For") if value == "" { return nil } for _, t := range trusted { if len(header.Values(t)) > 0 { - if key == "X-Real-IP" { - return []net.Address{net.ParseAddress(value)} + if idx := strings.IndexByte(value, ','); idx >= 0 { + value = value[:idx] } - return parseXForwardedFor(value) + return net.ParseAddress(value) } } if len(trusted) == 0 { - errors.LogWarning(context.Background(), `received "`, key, `" from `, clientAddr, ` but "sockopt.trustedXForwardedFor" is not configured; ignoring it and using the real remote address`) + errors.LogWarning(context.Background(), `received "X-Forwarded-For" from `, remoteAddr, ` but "sockopt.trustedXForwardedFor" is not configured; ignoring it and using the real remote address`) } else { - errors.LogError(context.Background(), `ignored potentially forged "`, key, `" from `, clientAddr, `: `, value) + errors.LogError(context.Background(), `ignored potentially forged "X-Forwarded-For" from `, remoteAddr, `: `, value) } return nil } diff --git a/common/protocol/http/headers_test.go b/common/protocol/http/headers_test.go index 81b456de5..21f1d1e62 100644 --- a/common/protocol/http/headers_test.go +++ b/common/protocol/http/headers_test.go @@ -7,21 +7,20 @@ import ( "strings" "testing" - "github.com/google/go-cmp/cmp" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" . "github.com/xtls/xray-core/common/protocol/http" ) func TestParseTrustedXForwardedFor(t *testing.T) { - clientAddr := &gonet.TCPAddr{IP: gonet.ParseIP("127.0.0.1"), Port: 12345} + remoteAddr := &gonet.TCPAddr{IP: gonet.ParseIP("127.0.0.1"), Port: 12345} 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") - if addrs := ParseTrustedXForwardedFor(header, nil, clientAddr); addrs != nil { - t.Fatalf("unexpected trusted X-Forwarded-For: %v", addrs) + if addr := ParseTrustedXForwardedFor(header, nil, remoteAddr); addr != nil { + t.Fatalf("unexpected trusted X-Forwarded-For: %v", addr) } }) @@ -30,32 +29,9 @@ func TestParseTrustedXForwardedFor(t *testing.T) { 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) + addr := ParseTrustedXForwardedFor(header, []string{"X-Trusted-CDN"}, remoteAddr) + if addr != net.ParseAddress("129.78.138.66") { + t.Fatalf("unexpected trusted X-Forwarded-For: %v", addr) } }) } diff --git a/transport/internet/grpc/encoding/remoteaddr.go b/transport/internet/grpc/encoding/remoteaddr.go index adb5ac6e6..a28d91933 100644 --- a/transport/internet/grpc/encoding/remoteaddr.go +++ b/transport/internet/grpc/encoding/remoteaddr.go @@ -26,41 +26,7 @@ func remoteAddrFromContext(ctx context.Context, trusted []string) net.Addr { return remoteAddr } - key := "X-Real-IP" - values := md.Get(key) - if len(values) == 0 || values[0] == "" { - key = "X-Forwarded-For" - values = md.Get(key) - } - if len(values) == 0 || values[0] == "" { - return remoteAddr - } - - isTrusted := false - for _, t := range trusted { - if len(md.Get(t)) > 0 { - isTrusted = true - break - } - } - if !isTrusted { - if len(trusted) == 0 { - errors.LogWarning(context.Background(), `received "`, key, `" from `, remoteAddr, ` but "sockopt.trustedXForwardedFor" is not configured; ignoring it and using the real remote address`) - } else { - errors.LogError(context.Background(), `ignored potentially forged "`, key, `" from `, remoteAddr, `: `, values[0]) - } - return remoteAddr - } - - forwardedIP := values[0] - if key == "X-Forwarded-For" { - if idx := strings.IndexByte(forwardedIP, ','); idx >= 0 { - forwardedIP = forwardedIP[:idx] - } - } - - forwardedAddr := net.ParseAddress(forwardedIP) - if forwardedAddr.Family().IsIP() { + if forwardedAddr := parseTrustedXForwardedFor(md, trusted, remoteAddr); forwardedAddr != nil && forwardedAddr.Family().IsIP() { remoteAddr = &net.TCPAddr{ IP: forwardedAddr.IP(), Port: 0, @@ -68,3 +34,25 @@ func remoteAddrFromContext(ctx context.Context, trusted []string) net.Addr { } return remoteAddr } + +func parseTrustedXForwardedFor(md metadata.MD, trusted []string, remoteAddr net.Addr) net.Address { + values := md.Get("X-Forwarded-For") + if len(values) == 0 || values[0] == "" { + return nil + } + value := values[0] + for _, t := range trusted { + if len(md.Get(t)) > 0 { + if idx := strings.IndexByte(value, ','); idx >= 0 { + value = value[:idx] + } + return net.ParseAddress(value) + } + } + if len(trusted) == 0 { + errors.LogWarning(context.Background(), `received "X-Forwarded-For" from `, remoteAddr, ` 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 `, remoteAddr, `: `, value) + } + return nil +} diff --git a/transport/internet/grpc/encoding/remoteaddr_test.go b/transport/internet/grpc/encoding/remoteaddr_test.go index b89d84feb..81e40aa96 100644 --- a/transport/internet/grpc/encoding/remoteaddr_test.go +++ b/transport/internet/grpc/encoding/remoteaddr_test.go @@ -16,17 +16,6 @@ func TestRemoteAddrFromContext(t *testing.T) { trustedXForwardedFor []string expectedRemoteAddress string }{ - { - name: "ignore X-Real-IP without trusted header", - metadata: metadata.Pairs("X-Real-IP", "1.1.1.1"), - expectedRemoteAddress: "127.0.0.1:12345", - }, - { - name: "trust X-Real-IP when configured", - metadata: metadata.Pairs("X-Real-IP", "1.1.1.1"), - trustedXForwardedFor: []string{"X-Real-IP"}, - expectedRemoteAddress: "1.1.1.1:0", - }, { name: "trust X-Forwarded-For when configured", metadata: metadata.Pairs("X-Forwarded-For", "2.2.2.2, 3.3.3.3"), @@ -45,18 +34,6 @@ func TestRemoteAddrFromContext(t *testing.T) { trustedXForwardedFor: []string{"X-Trusted-CDN"}, expectedRemoteAddress: "127.0.0.1:12345", }, - { - name: "prefer X-Real-IP over X-Forwarded-For", - metadata: metadata.Pairs("X-Real-IP", "6.6.6.6", "X-Forwarded-For", "7.7.7.7", "X-Trusted-CDN", "1"), - trustedXForwardedFor: []string{"X-Trusted-CDN"}, - expectedRemoteAddress: "6.6.6.6:0", - }, - { - name: "do not parse X-Real-IP as a list", - metadata: metadata.Pairs("X-Real-IP", "8.8.8.8, 9.9.9.9", "X-Trusted-CDN", "1"), - trustedXForwardedFor: []string{"X-Trusted-CDN"}, - expectedRemoteAddress: "127.0.0.1:12345", - }, } for _, test := range tests { diff --git a/transport/internet/httpupgrade/hub.go b/transport/internet/httpupgrade/hub.go index bc7378e8b..720fc14a6 100644 --- a/transport/internet/httpupgrade/hub.go +++ b/transport/internet/httpupgrade/hub.go @@ -85,10 +85,10 @@ func (s *server) upgrade(conn net.Conn) (stat.Connection, error) { if s.socketSettings != nil { trustedXFF = s.socketSettings.TrustedXForwardedFor } - forwardedAddrs := http_proto.ParseTrustedXForwardedFor(req.Header, trustedXFF, remoteAddr) - if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() { + forwardedAddr := http_proto.ParseTrustedXForwardedFor(req.Header, trustedXFF, remoteAddr) + if forwardedAddr != nil && forwardedAddr.Family().IsIP() { remoteAddr = &net.TCPAddr{ - IP: forwardedAddrs[0].IP(), + IP: forwardedAddr.IP(), Port: int(0), } } diff --git a/transport/internet/splithttp/hub.go b/transport/internet/splithttp/hub.go index 9e654791e..eb89c7d83 100644 --- a/transport/internet/splithttp/hub.go +++ b/transport/internet/splithttp/hub.go @@ -174,10 +174,10 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req if h.socketSettings != nil { trustedXFF = h.socketSettings.TrustedXForwardedFor } - forwardedAddrs := http_proto.ParseTrustedXForwardedFor(request.Header, trustedXFF, remoteAddr) - if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() { + forwardedAddr := http_proto.ParseTrustedXForwardedFor(request.Header, trustedXFF, remoteAddr) + if forwardedAddr != nil && forwardedAddr.Family().IsIP() { remoteAddr = &net.TCPAddr{ - IP: forwardedAddrs[0].IP(), + IP: forwardedAddr.IP(), Port: 0, } } diff --git a/transport/internet/websocket/hub.go b/transport/internet/websocket/hub.go index da34e6e5c..c4c81d6cd 100644 --- a/transport/internet/websocket/hub.go +++ b/transport/internet/websocket/hub.go @@ -70,10 +70,10 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req if h.socketSettings != nil { trustedXFF = h.socketSettings.TrustedXForwardedFor } - forwardedAddrs := http_proto.ParseTrustedXForwardedFor(request.Header, trustedXFF, remoteAddr) - if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() { + forwardedAddr := http_proto.ParseTrustedXForwardedFor(request.Header, trustedXFF, remoteAddr) + if forwardedAddr != nil && forwardedAddr.Family().IsIP() { remoteAddr = &net.TCPAddr{ - IP: forwardedAddrs[0].IP(), + IP: forwardedAddr.IP(), Port: int(0), } }