mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-07-05 19:28:45 +00:00
XHTTP & WS & HU & gRPC servers: Require sockopt.trustedXForwardedFor (#6309)
https://github.com/XTLS/Xray-core/pull/6258#issuecomment-4663652131 Behavior: https://github.com/XTLS/Xray-core/pull/6258#issuecomment-4746598275 Replaces https://github.com/XTLS/Xray-core/pull/6159
This commit is contained in:
@@ -62,7 +62,7 @@ func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *interne
|
||||
if err != nil {
|
||||
return nil, errors.New("Cannot dial gRPC").Base(err)
|
||||
}
|
||||
return encoding.NewMultiHunkConn(grpcService, nil), nil
|
||||
return encoding.NewMultiHunkConn(grpcService, nil, nil), nil
|
||||
}
|
||||
|
||||
errors.LogDebug(ctx, "using gRPC tun mode service name: `"+grpcSettings.getServiceName()+"` stream name: `"+grpcSettings.getTunStreamName()+"`")
|
||||
@@ -71,7 +71,7 @@ func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *interne
|
||||
return nil, errors.New("Cannot dial gRPC").Base(err)
|
||||
}
|
||||
|
||||
return encoding.NewHunkConn(grpcService, nil), nil
|
||||
return encoding.NewHunkConn(grpcService, nil, nil), nil
|
||||
}
|
||||
|
||||
func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (*grpc.ClientConn, error) {
|
||||
|
||||
@@ -9,8 +9,6 @@ import (
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/signal/done"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
)
|
||||
|
||||
type HunkConn interface {
|
||||
@@ -38,31 +36,8 @@ func NewHunkReadWriter(hc HunkConn, cancel context.CancelFunc) *HunkReaderWriter
|
||||
return &HunkReaderWriter{hc, cancel, done.New(), nil, 0}
|
||||
}
|
||||
|
||||
func NewHunkConn(hc HunkConn, cancel context.CancelFunc) net.Conn {
|
||||
var rAddr net.Addr
|
||||
pr, ok := peer.FromContext(hc.Context())
|
||||
if ok {
|
||||
rAddr = pr.Addr
|
||||
} else {
|
||||
rAddr = &net.TCPAddr{
|
||||
IP: []byte{0, 0, 0, 0},
|
||||
Port: 0,
|
||||
}
|
||||
}
|
||||
|
||||
md, ok := metadata.FromIncomingContext(hc.Context())
|
||||
if ok {
|
||||
header := md.Get("x-real-ip")
|
||||
if len(header) > 0 {
|
||||
realip := net.ParseAddress(header[0])
|
||||
if realip.Family().IsIP() {
|
||||
rAddr = &net.TCPAddr{
|
||||
IP: realip.IP(),
|
||||
Port: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func NewHunkConn(hc HunkConn, cancel context.CancelFunc, trustedXForwardedFor []string) net.Conn {
|
||||
rAddr := remoteAddrFromContext(hc.Context(), trustedXForwardedFor)
|
||||
wrc := NewHunkReadWriter(hc, cancel)
|
||||
return cnc.NewConnection(
|
||||
cnc.ConnectionInput(wrc),
|
||||
|
||||
@@ -3,15 +3,12 @@ package encoding
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
xnet "github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/signal/done"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
)
|
||||
|
||||
type MultiHunkConn interface {
|
||||
@@ -34,31 +31,8 @@ func NewMultiHunkReadWriter(hc MultiHunkConn, cancel context.CancelFunc) *MultiH
|
||||
return &MultiHunkReaderWriter{hc, cancel, done.New(), nil}
|
||||
}
|
||||
|
||||
func NewMultiHunkConn(hc MultiHunkConn, cancel context.CancelFunc) net.Conn {
|
||||
var rAddr net.Addr
|
||||
pr, ok := peer.FromContext(hc.Context())
|
||||
if ok {
|
||||
rAddr = pr.Addr
|
||||
} else {
|
||||
rAddr = &net.TCPAddr{
|
||||
IP: []byte{0, 0, 0, 0},
|
||||
Port: 0,
|
||||
}
|
||||
}
|
||||
|
||||
md, ok := metadata.FromIncomingContext(hc.Context())
|
||||
if ok {
|
||||
header := md.Get("x-real-ip")
|
||||
if len(header) > 0 {
|
||||
realip := xnet.ParseAddress(header[0])
|
||||
if realip.Family().IsIP() {
|
||||
rAddr = &net.TCPAddr{
|
||||
IP: realip.IP(),
|
||||
Port: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func NewMultiHunkConn(hc MultiHunkConn, cancel context.CancelFunc, trustedXForwardedFor []string) net.Conn {
|
||||
rAddr := remoteAddrFromContext(hc.Context(), trustedXForwardedFor)
|
||||
wrc := NewMultiHunkReadWriter(hc, cancel)
|
||||
return cnc.NewConnection(
|
||||
cnc.ConnectionInputMulti(wrc),
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
)
|
||||
|
||||
func remoteAddrFromContext(ctx context.Context, trusted []string) net.Addr {
|
||||
var remoteAddr net.Addr
|
||||
if pr, ok := peer.FromContext(ctx); ok {
|
||||
remoteAddr = pr.Addr
|
||||
} else {
|
||||
remoteAddr = &net.TCPAddr{
|
||||
IP: []byte{0, 0, 0, 0},
|
||||
Port: 0,
|
||||
}
|
||||
}
|
||||
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return remoteAddr
|
||||
}
|
||||
|
||||
if forwardedAddr := parseTrustedXForwardedFor(md, trusted, remoteAddr); forwardedAddr != nil && forwardedAddr.Family().IsIP() {
|
||||
remoteAddr = &net.TCPAddr{
|
||||
IP: forwardedAddr.IP(),
|
||||
Port: 0,
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
)
|
||||
|
||||
func TestRemoteAddrFromContext(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
metadata metadata.MD
|
||||
trustedXForwardedFor []string
|
||||
expectedRemoteAddress string
|
||||
}{
|
||||
{
|
||||
name: "trust X-Forwarded-For when configured",
|
||||
metadata: metadata.Pairs("X-Forwarded-For", "2.2.2.2, 3.3.3.3"),
|
||||
trustedXForwardedFor: []string{"X-Forwarded-For"},
|
||||
expectedRemoteAddress: "2.2.2.2:0",
|
||||
},
|
||||
{
|
||||
name: "trust X-Forwarded-For with trusted marker",
|
||||
metadata: metadata.Pairs("X-Forwarded-For", "4.4.4.4", "X-Trusted-CDN", "1"),
|
||||
trustedXForwardedFor: []string{"X-Trusted-CDN"},
|
||||
expectedRemoteAddress: "4.4.4.4:0",
|
||||
},
|
||||
{
|
||||
name: "ignore X-Forwarded-For without trusted marker",
|
||||
metadata: metadata.Pairs("X-Forwarded-For", "5.5.5.5"),
|
||||
trustedXForwardedFor: []string{"X-Trusted-CDN"},
|
||||
expectedRemoteAddress: "127.0.0.1:12345",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := peer.NewContext(metadata.NewIncomingContext(context.Background(), test.metadata), &peer.Peer{
|
||||
Addr: &net.TCPAddr{
|
||||
IP: net.ParseIP("127.0.0.1"),
|
||||
Port: 12345,
|
||||
},
|
||||
})
|
||||
remoteAddr := remoteAddrFromContext(ctx, test.trustedXForwardedFor)
|
||||
if remoteAddr.String() != test.expectedRemoteAddress {
|
||||
t.Fatalf("unexpected remote address: %s", remoteAddr.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -19,24 +19,25 @@ import (
|
||||
|
||||
type Listener struct {
|
||||
encoding.UnimplementedGRPCServiceServer
|
||||
ctx context.Context
|
||||
handler internet.ConnHandler
|
||||
local net.Addr
|
||||
config *Config
|
||||
ctx context.Context
|
||||
handler internet.ConnHandler
|
||||
local net.Addr
|
||||
config *Config
|
||||
trustedXForwardedFor []string
|
||||
|
||||
s *grpc.Server
|
||||
}
|
||||
|
||||
func (l Listener) Tun(server encoding.GRPCService_TunServer) error {
|
||||
tunCtx, cancel := context.WithCancel(l.ctx)
|
||||
l.handler(encoding.NewHunkConn(server, cancel))
|
||||
l.handler(encoding.NewHunkConn(server, cancel, l.trustedXForwardedFor))
|
||||
<-tunCtx.Done()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l Listener) TunMulti(server encoding.GRPCService_TunMultiServer) error {
|
||||
tunCtx, cancel := context.WithCancel(l.ctx)
|
||||
l.handler(encoding.NewMultiHunkConn(server, cancel))
|
||||
l.handler(encoding.NewMultiHunkConn(server, cancel, l.trustedXForwardedFor))
|
||||
<-tunCtx.Done()
|
||||
return nil
|
||||
}
|
||||
@@ -74,6 +75,9 @@ func Listen(ctx context.Context, address net.Address, port net.Port, settings *i
|
||||
}
|
||||
|
||||
listener.ctx = ctx
|
||||
if settings.SocketSettings != nil {
|
||||
listener.trustedXForwardedFor = settings.SocketSettings.TrustedXForwardedFor
|
||||
}
|
||||
|
||||
config := tls.ConfigFromStreamSettings(settings)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user