This commit is contained in:
Meo597
2026-06-19 00:11:07 +08:00
parent ce3b4fd175
commit 4ad42e6e28
7 changed files with 45 additions and 118 deletions
+7 -21
View File
@@ -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
}
+6 -30
View File
@@ -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)
}
})
}
+23 -35
View File
@@ -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
}
@@ -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 {
+3 -3
View File
@@ -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),
}
}
+3 -3
View File
@@ -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,
}
}
+3 -3
View File
@@ -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),
}
}