From be1c6f594badab4451147c17f48c1a83054addd6 Mon Sep 17 00:00:00 2001 From: bcmmbaga Date: Sat, 23 May 2026 00:06:32 +0300 Subject: [PATCH] Refactor client config ext --- .../network_map/controller/controller.go | 10 +- management/internals/server/boot.go | 122 +++++++++--------- management/internals/server/controllers.go | 10 +- .../internals/shared/grpc/conversion.go | 13 +- management/internals/shared/grpc/server.go | 6 +- management/internals/shared/grpc/token_mgr.go | 10 +- 6 files changed, 96 insertions(+), 75 deletions(-) diff --git a/management/internals/controllers/network_map/controller/controller.go b/management/internals/controllers/network_map/controller/controller.go index 590773dda..d3a800068 100644 --- a/management/internals/controllers/network_map/controller/controller.go +++ b/management/internals/controllers/network_map/controller/controller.go @@ -55,6 +55,8 @@ type Controller struct { proxyController port_forwarding.Controller integratedPeerValidator integrated_validator.IntegratedValidator + + configExtender grpc.ConfigExtender } type bufferUpdate struct { @@ -65,7 +67,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) *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, configExtender grpc.ConfigExtender) *Controller { nMetrics, err := newMetrics(metrics.UpdateChannelMetrics()) if err != nil { log.Fatal(fmt.Errorf("error creating metrics: %w", err)) @@ -84,6 +86,8 @@ func NewController(ctx context.Context, store store.Store, metrics telemetry.App proxyController: proxyController, EphemeralPeersManager: ephemeralPeersManager, + + configExtender: configExtender, } } @@ -203,7 +207,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) + 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) c.metrics.CountToSyncResponseDuration(time.Since(start)) c.peersUpdateManager.SendUpdate(ctx, p.ID, &network_map.UpdateMessage{ @@ -329,7 +333,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) + 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) 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 46e475143..99489f3ec 100644 --- a/management/internals/server/boot.go +++ b/management/internals/server/boot.go @@ -155,68 +155,72 @@ func (s *BaseServer) RateLimiter() *middleware.APIRateLimiter { func (s *BaseServer) GRPCServer() *grpc.Server { return Create(s, func() *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[[]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 + return s.BuildGRPCServer(s.ExtendNetBirdConfig) }) } +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 da220f6d2..b635875f1 100644 --- a/management/internals/server/controllers.go +++ b/management/internals/server/controllers.go @@ -21,7 +21,9 @@ 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 { @@ -62,7 +64,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()) + secretsManager, err := grpc.NewTimeBasedAuthSecretsManager(s.PeersUpdateManager(), s.Config.TURNConfig, s.Config.Relay, s.SettingsManager(), s.GroupsManager(), s.ExtendNetBirdConfig) if err != nil { log.Fatalf("failed to create secrets manager: %v", err) } @@ -125,7 +127,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) + 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) }) } @@ -148,3 +150,7 @@ 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 12402b420..72f20a98a 100644 --- a/management/internals/shared/grpc/conversion.go +++ b/management/internals/shared/grpc/conversion.go @@ -10,8 +10,6 @@ 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" @@ -26,6 +24,8 @@ 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) *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, configExtender ConfigExtender) *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,8 +146,10 @@ func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nb } nbConfig := toNetbirdConfig(config, turnCredentials, relayCredentials, extraSettings) - extendedConfig := integrationsConfig.ExtendNetBirdConfig(peer.ID, peerGroups, nbConfig, extraSettings) - response.NetbirdConfig = extendedConfig + if configExtender != nil { + nbConfig = configExtender(peer.ID, peerGroups, nbConfig, extraSettings) + } + response.NetbirdConfig = nbConfig response.NetworkMap.PeerConfig = response.PeerConfig @@ -332,7 +334,6 @@ 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 1d8234304..5eabdbaf4 100644 --- a/management/internals/shared/grpc/server.go +++ b/management/internals/shared/grpc/server.go @@ -86,6 +86,8 @@ type Server struct { reverseProxyManager rpservice.Manager reverseProxyMu sync.RWMutex + + configExtender ConfigExtender } // NewServer creates a new Management server @@ -101,6 +103,7 @@ 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 @@ -144,6 +147,7 @@ func NewServer( networkMapController: networkMapController, oAuthConfigProvider: oAuthConfigProvider, sessionStore: sessionStore, + configExtender: configExtender, loginFilter: newLoginFilter(), @@ -932,7 +936,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) + 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) 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 65e58ad41..411d4035d 100644 --- a/management/internals/shared/grpc/token_mgr.go +++ b/management/internals/shared/grpc/token_mgr.go @@ -12,7 +12,6 @@ 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" @@ -46,11 +45,12 @@ 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) (*TimeBasedAuthSecretsManager, error) { +func NewTimeBasedAuthSecretsManager(updateManager network_map.PeersUpdateManager, turnCfg *nbconfig.TURNConfig, relayCfg *nbconfig.Relay, settingsManager settings.Manager, groupsManager groups.Manager, configExtender ConfigExtender) (*TimeBasedAuthSecretsManager, error) { key, err := wgtypes.GeneratePrivateKey() if err != nil { return nil, err @@ -65,6 +65,7 @@ func NewTimeBasedAuthSecretsManager(updateManager network_map.PeersUpdateManager settingsManager: settingsManager, groupsManager: groupsManager, wgKey: key, + configExtender: configExtender, } if turnCfg != nil { @@ -286,6 +287,7 @@ func (m *TimeBasedAuthSecretsManager) extendNetbirdConfig(ctx context.Context, p log.WithContext(ctx).Errorf("failed to get peer groups: %v", err) } - extendedConfig := integrationsConfig.ExtendNetBirdConfig(peerID, peerGroups, update.NetbirdConfig, extraSettings) - update.NetbirdConfig = extendedConfig + if m.configExtender != nil { + update.NetbirdConfig = m.configExtender(peerID, peerGroups, update.NetbirdConfig, extraSettings) + } }