diff --git a/go.mod b/go.mod index d0417d9a8..38590fa13 100644 --- a/go.mod +++ b/go.mod @@ -171,3 +171,5 @@ replace github.com/getlantern/systray => github.com/netbirdio/systray v0.0.0-202 replace golang.zx2c4.com/wireguard => github.com/netbirdio/wireguard-go v0.0.0-20240105182236-6c340dd55aed replace github.com/cloudflare/circl => github.com/cunicu/circl v0.0.0-20230801113412-fec58fc7b5f6 + +replace github.com/grpc-ecosystem/go-grpc-middleware/v2 => github.com/surik/go-grpc-middleware/v2 v2.0.0-20240206110057-98a38fc1f86f diff --git a/go.sum b/go.sum index a379d018e..ac404d216 100644 --- a/go.sum +++ b/go.sum @@ -286,8 +286,6 @@ github.com/gopherjs/gopherjs v0.0.0-20220410123724-9e86199038b0 h1:fWY+zXdWhvWnd github.com/gopherjs/gopherjs v0.0.0-20220410123724-9e86199038b0/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240202184442-37827591b26c h1:Kvw2BIua5WGDnknpnODn9K74PYWLhhqt8G3l0chyzEI= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240202184442-37827591b26c/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng= github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= @@ -519,6 +517,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/surik/go-grpc-middleware/v2 v2.0.0-20240206110057-98a38fc1f86f h1:J+egXEDkpg/vOYYzPO5IwF8OufGb7g+KcwEF1AWIzhQ= +github.com/surik/go-grpc-middleware/v2 v2.0.0-20240206110057-98a38fc1f86f/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4= github.com/things-go/go-socks5 v0.0.4 h1:jMQjIc+qhD4z9cITOMnBiwo9dDmpGuXmBlkRFrl/qD0= github.com/things-go/go-socks5 v0.0.4/go.mod h1:sh4K6WHrmHZpjxLTCHyYtXYH8OUuD+yZun41NomR1IQ= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= diff --git a/management/cmd/management.go b/management/cmd/management.go index 9afddc0f7..2f06037db 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -15,6 +15,7 @@ import ( "net/url" "os" "path" + "slices" "strings" "time" @@ -170,21 +171,31 @@ var ( turnManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig) - trustedPeers := config.TrustedHTTPProxies - if len(trustedPeers) == 0 { - trustedPeers = []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")} + trustedPeers := config.ReverseProxy.TrustedPeers + defaultTrustedPeers := []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0")} + if len(trustedPeers) == 0 || slices.Equal[[]netip.Prefix](trustedPeers, defaultTrustedPeers) { + log.Warn("TrustedPeers are configured to default value '0.0.0.0/0', '::/0'. This allows connection IP spoofing.") + trustedPeers = defaultTrustedPeers + } + trustedHTTPProxies := config.ReverseProxy.TrustedHTTPProxies + trustedProxiesCount := config.ReverseProxy.TrustedHTTPProxiesCount + if len(trustedHTTPProxies) > 0 && trustedProxiesCount > 0 { + log.Warn("TrustedHTTPProxies and TrustedHTTPProxiesCount both are configured. " + + "This is not recommended way to extract X-Forwarded-For. Consider using one of these options.") + } + realipOpts := realip.Opts{ + TrustedPeers: trustedPeers, + TrustedProxies: trustedHTTPProxies, + TrustedProxiesCount: trustedProxiesCount, + Headers: []string{realip.XForwardedFor, realip.XRealIp}, } - headers := []string{realip.XForwardedFor, realip.XRealIp} gRPCOpts := []grpc.ServerOption{ grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp), - grpc.ChainUnaryInterceptor( - realip.UnaryServerInterceptor(trustedPeers, headers), - ), - grpc.ChainStreamInterceptor( - realip.StreamServerInterceptor(trustedPeers, headers), - ), + grpc.ChainUnaryInterceptor(realip.UnaryServerInterceptorOpts(realipOpts)), + grpc.ChainStreamInterceptor(realip.StreamServerInterceptorOpts(realipOpts)), } + var certManager *autocert.Manager var tlsConfig *tls.Config tlsEnabled := false diff --git a/management/server/config.go b/management/server/config.go index c8b6606ea..3e5ff1eaf 100644 --- a/management/server/config.go +++ b/management/server/config.go @@ -41,8 +41,6 @@ type Config struct { HttpConfig *HttpServerConfig - TrustedHTTPProxies []netip.Prefix - IdpManagerConfig *idp.Config DeviceAuthorizationFlow *DeviceAuthorizationFlow @@ -50,6 +48,8 @@ type Config struct { PKCEAuthorizationFlow *PKCEAuthorizationFlow StoreConfig StoreConfig + + ReverseProxy ReverseProxy } // GetAuthAudiences returns the audience from the http config and device authorization flow config @@ -146,6 +146,27 @@ type StoreConfig struct { Engine StoreEngine } +// ReverseProxy contains reverse proxy configuration in front of management. +type ReverseProxy struct { + // TrustedHTTPProxies represents a list of trusted HTTP proxies by their IP prefixes. + // When extracting the real IP address from request headers, the middleware will verify + // if the peer's address falls within one of these trusted IP prefixes. + TrustedHTTPProxies []netip.Prefix + + // TrustedHTTPProxiesCount specifies the count of trusted HTTP proxies between the internet + // and the server. When using the trusted proxy count method to extract the real IP address, + // the middleware will search the X-Forwarded-For IP list from the rightmost by this count + // minus one. + TrustedHTTPProxiesCount uint + + // TrustedPeers represents a list of trusted peers by their IP prefixes. + // These peers are considered trustworthy by the gRPC server operator, + // and the middleware will attempt to extract the real IP address from + // request headers if the peer's address falls within one of these + // trusted IP prefixes. + TrustedPeers []netip.Prefix +} + // validateURL validates input http url func validateURL(httpURL string) bool { _, err := url.ParseRequestURI(httpURL)