diff --git a/management/internals/controllers/network_map/controller/controller.go b/management/internals/controllers/network_map/controller/controller.go index d3a800068..590773dda 100644 --- a/management/internals/controllers/network_map/controller/controller.go +++ b/management/internals/controllers/network_map/controller/controller.go @@ -55,8 +55,6 @@ type Controller struct { proxyController port_forwarding.Controller integratedPeerValidator integrated_validator.IntegratedValidator - - configExtender grpc.ConfigExtender } type bufferUpdate struct { @@ -67,7 +65,7 @@ type bufferUpdate struct { var _ network_map.Controller = (*Controller)(nil) -func NewController(ctx context.Context, store store.Store, metrics telemetry.AppMetrics, peersUpdateManager network_map.PeersUpdateManager, requestBuffer account.RequestBuffer, integratedPeerValidator integrated_validator.IntegratedValidator, settingsManager settings.Manager, dnsDomain string, proxyController port_forwarding.Controller, ephemeralPeersManager ephemeral.Manager, config *config.Config, configExtender grpc.ConfigExtender) *Controller { +func NewController(ctx context.Context, store store.Store, metrics telemetry.AppMetrics, peersUpdateManager network_map.PeersUpdateManager, requestBuffer account.RequestBuffer, integratedPeerValidator integrated_validator.IntegratedValidator, settingsManager settings.Manager, dnsDomain string, proxyController port_forwarding.Controller, ephemeralPeersManager ephemeral.Manager, config *config.Config) *Controller { nMetrics, err := newMetrics(metrics.UpdateChannelMetrics()) if err != nil { log.Fatal(fmt.Errorf("error creating metrics: %w", err)) @@ -86,8 +84,6 @@ func NewController(ctx context.Context, store store.Store, metrics telemetry.App proxyController: proxyController, EphemeralPeersManager: ephemeralPeersManager, - - configExtender: configExtender, } } @@ -207,7 +203,7 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin peerGroups := account.GetPeerGroups(p.ID) start = time.Now() - update := grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort, c.configExtender) + update := grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort) c.metrics.CountToSyncResponseDuration(time.Since(start)) c.peersUpdateManager.SendUpdate(ctx, p.ID, &network_map.UpdateMessage{ @@ -333,7 +329,7 @@ func (c *Controller) UpdateAccountPeer(ctx context.Context, accountId string, pe peerGroups := account.GetPeerGroups(peerId) dnsFwdPort := computeForwarderPort(maps.Values(account.Peers), network_map.DnsForwarderPortMinVersion) - update := grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, peer, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSettings, maps.Keys(peerGroups), dnsFwdPort, c.configExtender) + update := grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, peer, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSettings, maps.Keys(peerGroups), dnsFwdPort) c.peersUpdateManager.SendUpdate(ctx, peer.ID, &network_map.UpdateMessage{ Update: update, MessageType: network_map.MessageTypeNetworkMap, diff --git a/management/internals/server/boot.go b/management/internals/server/boot.go index 99489f3ec..46e475143 100644 --- a/management/internals/server/boot.go +++ b/management/internals/server/boot.go @@ -155,72 +155,68 @@ func (s *BaseServer) RateLimiter() *middleware.APIRateLimiter { func (s *BaseServer) GRPCServer() *grpc.Server { return Create(s, func() *grpc.Server { - return s.BuildGRPCServer(s.ExtendNetBirdConfig) + trustedPeers := s.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.WithContext(context.Background()).Warn("TrustedPeers are configured to default value '0.0.0.0/0', '::/0'. This allows connection IP spoofing.") + trustedPeers = defaultTrustedPeers + } + trustedHTTPProxies := s.Config.ReverseProxy.TrustedHTTPProxies + trustedProxiesCount := s.Config.ReverseProxy.TrustedHTTPProxiesCount + if len(trustedHTTPProxies) > 0 && trustedProxiesCount > 0 { + log.WithContext(context.Background()).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.Option{ + realip.WithTrustedPeers(trustedPeers), + realip.WithTrustedProxies(trustedHTTPProxies), + realip.WithTrustedProxiesCount(trustedProxiesCount), + realip.WithHeaders([]string{realip.XForwardedFor, realip.XRealIp}), + } + proxyUnary, proxyStream, proxyAuthClose := nbgrpc.NewProxyAuthInterceptors(s.Store()) + s.proxyAuthClose = proxyAuthClose + gRPCOpts := []grpc.ServerOption{ + grpc.KeepaliveEnforcementPolicy(kaep), + grpc.KeepaliveParams(kasp), + grpc.ChainUnaryInterceptor(realip.UnaryServerInterceptorOpts(realipOpts...), unaryInterceptor, proxyUnary), + grpc.ChainStreamInterceptor(realip.StreamServerInterceptorOpts(realipOpts...), streamInterceptor, proxyStream), + } + + if s.Config.HttpConfig.LetsEncryptDomain != "" { + certManager, err := encryption.CreateCertManager(s.Config.Datadir, s.Config.HttpConfig.LetsEncryptDomain) + if err != nil { + log.Fatalf("failed to create certificate service: %v", err) + } + transportCredentials := credentials.NewTLS(certManager.TLSConfig()) + gRPCOpts = append(gRPCOpts, grpc.Creds(transportCredentials)) + } else if s.Config.HttpConfig.CertFile != "" && s.Config.HttpConfig.CertKey != "" { + tlsConfig, err := loadTLSConfig(s.Config.HttpConfig.CertFile, s.Config.HttpConfig.CertKey) + if err != nil { + log.Fatalf("cannot load TLS credentials: %v", err) + } + transportCredentials := credentials.NewTLS(tlsConfig) + gRPCOpts = append(gRPCOpts, grpc.Creds(transportCredentials)) + } + + gRPCAPIHandler := grpc.NewServer(gRPCOpts...) + srv, err := nbgrpc.NewServer(s.Config, s.AccountManager(), s.SettingsManager(), s.JobManager(), s.SecretsManager(), s.Metrics(), s.AuthManager(), s.IntegratedValidator(), s.NetworkMapController(), s.OAuthConfigProvider(), s.SessionStore()) + if err != nil { + log.Fatalf("failed to create management server: %v", err) + } + serviceMgr := s.ServiceManager() + srv.SetReverseProxyManager(serviceMgr) + if serviceMgr != nil { + serviceMgr.StartExposeReaper(context.Background()) + } + mgmtProto.RegisterManagementServiceServer(gRPCAPIHandler, srv) + + mgmtProto.RegisterProxyServiceServer(gRPCAPIHandler, s.ReverseProxyGRPCServer()) + log.Info("ProxyService registered on gRPC server") + + return gRPCAPIHandler }) } -func (s *BaseServer) BuildGRPCServer(configExtender nbgrpc.ConfigExtender) *grpc.Server { - trustedPeers := s.Config.ReverseProxy.TrustedPeers - defaultTrustedPeers := []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0")} - if len(trustedPeers) == 0 || slices.Equal(trustedPeers, defaultTrustedPeers) { - log.WithContext(context.Background()).Warn("TrustedPeers are configured to default value '0.0.0.0/0', '::/0'. This allows connection IP spoofing.") - trustedPeers = defaultTrustedPeers - } - trustedHTTPProxies := s.Config.ReverseProxy.TrustedHTTPProxies - trustedProxiesCount := s.Config.ReverseProxy.TrustedHTTPProxiesCount - if len(trustedHTTPProxies) > 0 && trustedProxiesCount > 0 { - log.WithContext(context.Background()).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.Option{ - realip.WithTrustedPeers(trustedPeers), - realip.WithTrustedProxies(trustedHTTPProxies), - realip.WithTrustedProxiesCount(trustedProxiesCount), - realip.WithHeaders([]string{realip.XForwardedFor, realip.XRealIp}), - } - proxyUnary, proxyStream, proxyAuthClose := nbgrpc.NewProxyAuthInterceptors(s.Store()) - s.proxyAuthClose = proxyAuthClose - gRPCOpts := []grpc.ServerOption{ - grpc.KeepaliveEnforcementPolicy(kaep), - grpc.KeepaliveParams(kasp), - grpc.ChainUnaryInterceptor(realip.UnaryServerInterceptorOpts(realipOpts...), unaryInterceptor, proxyUnary), - grpc.ChainStreamInterceptor(realip.StreamServerInterceptorOpts(realipOpts...), streamInterceptor, proxyStream), - } - - if s.Config.HttpConfig.LetsEncryptDomain != "" { - certManager, err := encryption.CreateCertManager(s.Config.Datadir, s.Config.HttpConfig.LetsEncryptDomain) - if err != nil { - log.Fatalf("failed to create certificate service: %v", err) - } - transportCredentials := credentials.NewTLS(certManager.TLSConfig()) - gRPCOpts = append(gRPCOpts, grpc.Creds(transportCredentials)) - } else if s.Config.HttpConfig.CertFile != "" && s.Config.HttpConfig.CertKey != "" { - tlsConfig, err := loadTLSConfig(s.Config.HttpConfig.CertFile, s.Config.HttpConfig.CertKey) - if err != nil { - log.Fatalf("cannot load TLS credentials: %v", err) - } - transportCredentials := credentials.NewTLS(tlsConfig) - gRPCOpts = append(gRPCOpts, grpc.Creds(transportCredentials)) - } - - gRPCAPIHandler := grpc.NewServer(gRPCOpts...) - srv, err := nbgrpc.NewServer(s.Config, s.AccountManager(), s.SettingsManager(), s.JobManager(), s.SecretsManager(), s.Metrics(), s.AuthManager(), s.IntegratedValidator(), s.NetworkMapController(), s.OAuthConfigProvider(), s.SessionStore(), configExtender) - if err != nil { - log.Fatalf("failed to create management server: %v", err) - } - serviceMgr := s.ServiceManager() - srv.SetReverseProxyManager(serviceMgr) - if serviceMgr != nil { - serviceMgr.StartExposeReaper(context.Background()) - } - mgmtProto.RegisterManagementServiceServer(gRPCAPIHandler, srv) - - mgmtProto.RegisterProxyServiceServer(gRPCAPIHandler, s.ReverseProxyGRPCServer()) - log.Info("ProxyService registered on gRPC server") - - return gRPCAPIHandler -} - func (s *BaseServer) ReverseProxyGRPCServer() *nbgrpc.ProxyServiceServer { return Create(s, func() *nbgrpc.ProxyServiceServer { proxyService := nbgrpc.NewProxyServiceServer(s.AccessLogsManager(), s.ProxyTokenStore(), s.PKCEVerifierStore(), s.proxyOIDCConfig(), s.PeersManager(), s.UsersManager(), s.ProxyManager(), s.Store()) diff --git a/management/internals/server/controllers.go b/management/internals/server/controllers.go index b635875f1..da220f6d2 100644 --- a/management/internals/server/controllers.go +++ b/management/internals/server/controllers.go @@ -21,9 +21,7 @@ import ( "github.com/netbirdio/netbird/management/server/integrations/integrated_validator" "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" "github.com/netbirdio/netbird/management/server/job" - "github.com/netbirdio/netbird/management/server/types" nbjwt "github.com/netbirdio/netbird/shared/auth/jwt" - "github.com/netbirdio/netbird/shared/management/proto" ) func (s *BaseServer) PeersUpdateManager() network_map.PeersUpdateManager { @@ -64,7 +62,7 @@ func (s *BaseServer) ProxyController() port_forwarding.Controller { func (s *BaseServer) SecretsManager() grpc.SecretsManager { return Create(s, func() grpc.SecretsManager { - secretsManager, err := grpc.NewTimeBasedAuthSecretsManager(s.PeersUpdateManager(), s.Config.TURNConfig, s.Config.Relay, s.SettingsManager(), s.GroupsManager(), s.ExtendNetBirdConfig) + secretsManager, err := grpc.NewTimeBasedAuthSecretsManager(s.PeersUpdateManager(), s.Config.TURNConfig, s.Config.Relay, s.SettingsManager(), s.GroupsManager()) if err != nil { log.Fatalf("failed to create secrets manager: %v", err) } @@ -127,7 +125,7 @@ func (s *BaseServer) EphemeralManager() ephemeral.Manager { func (s *BaseServer) NetworkMapController() network_map.Controller { return Create(s, func() network_map.Controller { - return nmapcontroller.NewController(context.Background(), s.Store(), s.Metrics(), s.PeersUpdateManager(), s.AccountRequestBuffer(), s.IntegratedValidator(), s.SettingsManager(), s.DNSDomain(), s.ProxyController(), s.EphemeralManager(), s.Config, s.ExtendNetBirdConfig) + return nmapcontroller.NewController(context.Background(), s.Store(), s.Metrics(), s.PeersUpdateManager(), s.AccountRequestBuffer(), s.IntegratedValidator(), s.SettingsManager(), s.DNSDomain(), s.ProxyController(), s.EphemeralManager(), s.Config) }) } @@ -150,7 +148,3 @@ func (s *BaseServer) AccountRequestBuffer() *server.AccountRequestBuffer { func (s *BaseServer) DNSDomain() string { return s.dnsDomain } - -func (s *BaseServer) ExtendNetBirdConfig(_ string, _ []string, config *proto.NetbirdConfig, _ *types.ExtraSettings) *proto.NetbirdConfig { - return config -} diff --git a/management/internals/shared/grpc/conversion.go b/management/internals/shared/grpc/conversion.go index 72f20a98a..12402b420 100644 --- a/management/internals/shared/grpc/conversion.go +++ b/management/internals/shared/grpc/conversion.go @@ -10,6 +10,8 @@ import ( log "github.com/sirupsen/logrus" goproto "google.golang.org/protobuf/proto" + integrationsConfig "github.com/netbirdio/management-integrations/integrations/config" + "github.com/netbirdio/netbird/client/ssh/auth" nbdns "github.com/netbirdio/netbird/dns" @@ -24,8 +26,6 @@ import ( "github.com/netbirdio/netbird/shared/sshauth" ) -type ConfigExtender func(peerID string, peerGroups []string, config *proto.NetbirdConfig, extras *types.ExtraSettings) *proto.NetbirdConfig - func toNetbirdConfig(config *nbconfig.Config, turnCredentials *Token, relayToken *Token, extraSettings *types.ExtraSettings) *proto.NetbirdConfig { if config == nil { return nil @@ -127,7 +127,7 @@ func toPeerConfig(peer *nbpeer.Peer, network *types.Network, dnsName string, set return peerConfig } -func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nbconfig.HttpServerConfig, deviceFlowConfig *nbconfig.DeviceAuthorizationFlow, peer *nbpeer.Peer, turnCredentials *Token, relayCredentials *Token, networkMap *types.NetworkMap, dnsName string, checks []*posture.Checks, dnsCache *cache.DNSConfigCache, settings *types.Settings, extraSettings *types.ExtraSettings, peerGroups []string, dnsFwdPort int64, configExtender ConfigExtender) *proto.SyncResponse { +func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nbconfig.HttpServerConfig, deviceFlowConfig *nbconfig.DeviceAuthorizationFlow, peer *nbpeer.Peer, turnCredentials *Token, relayCredentials *Token, networkMap *types.NetworkMap, dnsName string, checks []*posture.Checks, dnsCache *cache.DNSConfigCache, settings *types.Settings, extraSettings *types.ExtraSettings, peerGroups []string, dnsFwdPort int64) *proto.SyncResponse { // IPv6 data in AllowedIPs and SourcePrefixes wildcard expansion depends on // whether the target peer supports IPv6. Routes and firewall rules are already // filtered at the source (network map builder). @@ -146,10 +146,8 @@ func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nb } nbConfig := toNetbirdConfig(config, turnCredentials, relayCredentials, extraSettings) - if configExtender != nil { - nbConfig = configExtender(peer.ID, peerGroups, nbConfig, extraSettings) - } - response.NetbirdConfig = nbConfig + extendedConfig := integrationsConfig.ExtendNetBirdConfig(peer.ID, peerGroups, nbConfig, extraSettings) + response.NetbirdConfig = extendedConfig response.NetworkMap.PeerConfig = response.PeerConfig @@ -334,6 +332,7 @@ func toProtocolFirewallRules(rules []*types.FirewallRule, includeIPv6, useSource return result } + // populateSourcePrefixes sets SourcePrefixes on fwRule and returns any // additional rules needed (e.g. a v6 wildcard clone when the peer IP is unspecified). func populateSourcePrefixes(fwRule *proto.FirewallRule, rule *types.FirewallRule, includeIPv6 bool) []*proto.FirewallRule { diff --git a/management/internals/shared/grpc/server.go b/management/internals/shared/grpc/server.go index 5eabdbaf4..1d8234304 100644 --- a/management/internals/shared/grpc/server.go +++ b/management/internals/shared/grpc/server.go @@ -86,8 +86,6 @@ type Server struct { reverseProxyManager rpservice.Manager reverseProxyMu sync.RWMutex - - configExtender ConfigExtender } // NewServer creates a new Management server @@ -103,7 +101,6 @@ func NewServer( networkMapController network_map.Controller, oAuthConfigProvider idp.OAuthConfigProvider, sessionStore *auth.SessionStore, - configExtender ConfigExtender, ) (*Server, error) { if appMetrics != nil { // update gauge based on number of connected peers which is equal to open gRPC streams @@ -147,7 +144,6 @@ func NewServer( networkMapController: networkMapController, oAuthConfigProvider: oAuthConfigProvider, sessionStore: sessionStore, - configExtender: configExtender, loginFilter: newLoginFilter(), @@ -936,7 +932,7 @@ func (s *Server) sendInitialSync(ctx context.Context, peerKey wgtypes.Key, peer return status.Errorf(codes.Internal, "failed to get peer groups %s", err) } - plainResp := ToSyncResponse(ctx, s.config, s.config.HttpConfig, s.config.DeviceAuthorizationFlow, peer, turnToken, relayToken, networkMap, s.networkMapController.GetDNSDomain(settings), postureChecks, nil, settings, settings.Extra, peerGroups, dnsFwdPort, s.configExtender) + plainResp := ToSyncResponse(ctx, s.config, s.config.HttpConfig, s.config.DeviceAuthorizationFlow, peer, turnToken, relayToken, networkMap, s.networkMapController.GetDNSDomain(settings), postureChecks, nil, settings, settings.Extra, peerGroups, dnsFwdPort) key, err := s.secretsManager.GetWGKey() if err != nil { diff --git a/management/internals/shared/grpc/token_mgr.go b/management/internals/shared/grpc/token_mgr.go index 411d4035d..65e58ad41 100644 --- a/management/internals/shared/grpc/token_mgr.go +++ b/management/internals/shared/grpc/token_mgr.go @@ -12,6 +12,7 @@ import ( log "github.com/sirupsen/logrus" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + integrationsConfig "github.com/netbirdio/management-integrations/integrations/config" "github.com/netbirdio/netbird/management/internals/controllers/network_map" nbconfig "github.com/netbirdio/netbird/management/internals/server/config" "github.com/netbirdio/netbird/management/server/groups" @@ -45,12 +46,11 @@ type TimeBasedAuthSecretsManager struct { turnCancelMap map[string]chan struct{} relayCancelMap map[string]chan struct{} wgKey wgtypes.Key - configExtender ConfigExtender } type Token auth.Token -func NewTimeBasedAuthSecretsManager(updateManager network_map.PeersUpdateManager, turnCfg *nbconfig.TURNConfig, relayCfg *nbconfig.Relay, settingsManager settings.Manager, groupsManager groups.Manager, configExtender ConfigExtender) (*TimeBasedAuthSecretsManager, error) { +func NewTimeBasedAuthSecretsManager(updateManager network_map.PeersUpdateManager, turnCfg *nbconfig.TURNConfig, relayCfg *nbconfig.Relay, settingsManager settings.Manager, groupsManager groups.Manager) (*TimeBasedAuthSecretsManager, error) { key, err := wgtypes.GeneratePrivateKey() if err != nil { return nil, err @@ -65,7 +65,6 @@ func NewTimeBasedAuthSecretsManager(updateManager network_map.PeersUpdateManager settingsManager: settingsManager, groupsManager: groupsManager, wgKey: key, - configExtender: configExtender, } if turnCfg != nil { @@ -287,7 +286,6 @@ func (m *TimeBasedAuthSecretsManager) extendNetbirdConfig(ctx context.Context, p log.WithContext(ctx).Errorf("failed to get peer groups: %v", err) } - if m.configExtender != nil { - update.NetbirdConfig = m.configExtender(peerID, peerGroups, update.NetbirdConfig, extraSettings) - } + extendedConfig := integrationsConfig.ExtendNetBirdConfig(peerID, peerGroups, update.NetbirdConfig, extraSettings) + update.NetbirdConfig = extendedConfig }