From c03435061cd2afe0ae4ef5e4cbce54a33dbe2645 Mon Sep 17 00:00:00 2001 From: Pedro Maia Costa <550684+pnmcosta@users.noreply.github.com> Date: Thu, 22 May 2025 14:09:00 +0100 Subject: [PATCH] [management] lazy connection account setting (#3855) --- management/server/account.go | 19 +++++++++++++++---- management/server/activity/codes.go | 6 ++++++ management/server/grpcserver.go | 13 +++++++------ management/server/http/api/openapi.yml | 5 +++++ management/server/http/api/types.gen.go | 3 +++ .../handlers/accounts/accounts_handler.go | 4 ++++ .../accounts/accounts_handler_test.go | 4 ++++ management/server/peer.go | 4 ++-- management/server/peer_test.go | 4 ++-- management/server/types/settings.go | 4 ++++ 10 files changed, 52 insertions(+), 14 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index a7b3e628b..6dc449c1e 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -339,13 +339,20 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountRoutingPeerDNSResolutionDisabled, nil) } updateAccountPeers = true - account.Network.Serial++ + } + + if oldSettings.LazyConnectionEnabled != newSettings.LazyConnectionEnabled { + if newSettings.LazyConnectionEnabled { + am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountLazyConnectionEnabled, nil) + } else { + am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountLazyConnectionDisabled, nil) + } + updateAccountPeers = true } if oldSettings.DNSDomain != newSettings.DNSDomain { am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountDNSDomainUpdated, nil) updateAccountPeers = true - account.Network.Serial++ } err = am.handleInactivityExpirationSettings(ctx, oldSettings, newSettings, userID, accountID) @@ -358,7 +365,11 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco return nil, fmt.Errorf("groups propagation failed: %w", err) } - updatedAccount := account.UpdateSettings(newSettings) + account.UpdateSettings(newSettings) + + if updateAccountPeers { + account.Network.Serial++ + } err = am.Store.SaveAccount(ctx, account) if err != nil { @@ -374,7 +385,7 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco go am.UpdateAccountPeers(ctx, accountID) } - return updatedAccount, nil + return account, nil } func (am *DefaultAccountManager) handleGroupsPropagationSettings(ctx context.Context, oldSettings, newSettings *types.Settings, userID, accountID string) error { diff --git a/management/server/activity/codes.go b/management/server/activity/codes.go index ed4be82e2..d9f56f097 100644 --- a/management/server/activity/codes.go +++ b/management/server/activity/codes.go @@ -171,6 +171,9 @@ const ( ResourceRemovedFromGroup Activity = 83 AccountDNSDomainUpdated Activity = 84 + + AccountLazyConnectionEnabled Activity = 85 + AccountLazyConnectionDisabled Activity = 86 ) var activityMap = map[Activity]Code{ @@ -268,6 +271,9 @@ var activityMap = map[Activity]Code{ ResourceRemovedFromGroup: {"Resource removed from group", "resource.group.delete"}, AccountDNSDomainUpdated: {"Account DNS domain updated", "account.dns.domain.update"}, + + AccountLazyConnectionEnabled: {"Account lazy connection enabled", "account.setting.lazy.connection.enable"}, + AccountLazyConnectionDisabled: {"Account lazy connection disabled", "account.setting.lazy.connection.disable"}, } // StringCode returns a string code of the activity diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index 3f89ac4f9..5786dc871 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -517,7 +517,7 @@ func (s *GRPCServer) prepareLoginResponse(ctx context.Context, peer *nbpeer.Peer // if peer has reached this point then it has logged in loginResp := &proto.LoginResponse{ NetbirdConfig: toNetbirdConfig(s.config, nil, relayToken, nil), - PeerConfig: toPeerConfig(peer, netMap.Network, s.accountManager.GetDNSDomain(settings), false), + PeerConfig: toPeerConfig(peer, netMap.Network, s.accountManager.GetDNSDomain(settings), settings), Checks: toProtocolChecks(ctx, postureChecks), } @@ -632,20 +632,21 @@ func toNetbirdConfig(config *types.Config, turnCredentials *Token, relayToken *T return nbConfig } -func toPeerConfig(peer *nbpeer.Peer, network *types.Network, dnsName string, dnsResolutionOnRoutingPeerEnabled bool) *proto.PeerConfig { +func toPeerConfig(peer *nbpeer.Peer, network *types.Network, dnsName string, settings *types.Settings) *proto.PeerConfig { netmask, _ := network.Net.Mask.Size() fqdn := peer.FQDN(dnsName) return &proto.PeerConfig{ Address: fmt.Sprintf("%s/%d", peer.IP.String(), netmask), // take it from the network SshConfig: &proto.SSHConfig{SshEnabled: peer.SSHEnabled}, Fqdn: fqdn, - RoutingPeerDnsResolutionEnabled: dnsResolutionOnRoutingPeerEnabled, + RoutingPeerDnsResolutionEnabled: settings.RoutingPeerDNSResolutionEnabled, + LazyConnectionEnabled: settings.LazyConnectionEnabled, } } -func toSyncResponse(ctx context.Context, config *types.Config, peer *nbpeer.Peer, turnCredentials *Token, relayCredentials *Token, networkMap *types.NetworkMap, dnsName string, checks []*posture.Checks, dnsCache *DNSConfigCache, dnsResolutionOnRoutingPeerEnabled bool, extraSettings *types.ExtraSettings) *proto.SyncResponse { +func toSyncResponse(ctx context.Context, config *types.Config, peer *nbpeer.Peer, turnCredentials *Token, relayCredentials *Token, networkMap *types.NetworkMap, dnsName string, checks []*posture.Checks, dnsCache *DNSConfigCache, settings *types.Settings, extraSettings *types.ExtraSettings) *proto.SyncResponse { response := &proto.SyncResponse{ - PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName, dnsResolutionOnRoutingPeerEnabled), + PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName, settings), NetworkMap: &proto.NetworkMap{ Serial: networkMap.Network.CurrentSerial(), Routes: toProtocolRoutes(networkMap.Routes), @@ -731,7 +732,7 @@ func (s *GRPCServer) sendInitialSync(ctx context.Context, peerKey wgtypes.Key, p return status.Errorf(codes.Internal, "error handling request") } - plainResp := toSyncResponse(ctx, s.config, peer, turnToken, relayToken, networkMap, s.accountManager.GetDNSDomain(settings), postureChecks, nil, settings.RoutingPeerDNSResolutionEnabled, settings.Extra) + plainResp := toSyncResponse(ctx, s.config, peer, turnToken, relayToken, networkMap, s.accountManager.GetDNSDomain(settings), postureChecks, nil, settings, settings.Extra) encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, plainResp) if err != nil { diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index 5ddfb5332..8edee32a7 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -118,6 +118,11 @@ components: example: my-organization.org extra: $ref: '#/components/schemas/AccountExtraSettings' + lazy_connection_enabled: + x-experimental: true + description: Enables or disables experimental lazy connection + type: boolean + example: true required: - peer_login_expiration_enabled - peer_login_expiration diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go index c2efb835a..5dd6f23b8 100644 --- a/management/server/http/api/types.gen.go +++ b/management/server/http/api/types.gen.go @@ -289,6 +289,9 @@ type AccountSettings struct { // JwtGroupsEnabled Allows extract groups from JWT claim and add it to account groups. JwtGroupsEnabled *bool `json:"jwt_groups_enabled,omitempty"` + // LazyConnectionEnabled Enables or disables experimental lazy connection + LazyConnectionEnabled *bool `json:"lazy_connection_enabled,omitempty"` + // PeerInactivityExpiration Period of time of inactivity after which peer session expires (seconds). PeerInactivityExpiration int `json:"peer_inactivity_expiration"` diff --git a/management/server/http/handlers/accounts/accounts_handler.go b/management/server/http/handlers/accounts/accounts_handler.go index 7cad26bd6..638524e31 100644 --- a/management/server/http/handlers/accounts/accounts_handler.go +++ b/management/server/http/handlers/accounts/accounts_handler.go @@ -122,6 +122,9 @@ func (h *handler) updateAccount(w http.ResponseWriter, r *http.Request) { if req.Settings.DnsDomain != nil { settings.DNSDomain = *req.Settings.DnsDomain } + if req.Settings.LazyConnectionEnabled != nil { + settings.LazyConnectionEnabled = *req.Settings.LazyConnectionEnabled + } updatedAccount, err := h.accountManager.UpdateAccountSettings(r.Context(), accountID, userID, settings) if err != nil { @@ -181,6 +184,7 @@ func toAccountResponse(accountID string, settings *types.Settings, meta *types.A JwtAllowGroups: &jwtAllowGroups, RegularUsersViewBlocked: settings.RegularUsersViewBlocked, RoutingPeerDnsResolutionEnabled: &settings.RoutingPeerDNSResolutionEnabled, + LazyConnectionEnabled: &settings.LazyConnectionEnabled, DnsDomain: &settings.DNSDomain, } diff --git a/management/server/http/handlers/accounts/accounts_handler_test.go b/management/server/http/handlers/accounts/accounts_handler_test.go index 57bbffc7c..fec5140f4 100644 --- a/management/server/http/handlers/accounts/accounts_handler_test.go +++ b/management/server/http/handlers/accounts/accounts_handler_test.go @@ -108,6 +108,7 @@ func TestAccounts_AccountsHandler(t *testing.T) { JwtAllowGroups: &[]string{}, RegularUsersViewBlocked: true, RoutingPeerDnsResolutionEnabled: br(false), + LazyConnectionEnabled: br(false), DnsDomain: sr(""), }, expectedArray: true, @@ -129,6 +130,7 @@ func TestAccounts_AccountsHandler(t *testing.T) { JwtAllowGroups: &[]string{}, RegularUsersViewBlocked: false, RoutingPeerDnsResolutionEnabled: br(false), + LazyConnectionEnabled: br(false), DnsDomain: sr(""), }, expectedArray: false, @@ -150,6 +152,7 @@ func TestAccounts_AccountsHandler(t *testing.T) { JwtAllowGroups: &[]string{"test"}, RegularUsersViewBlocked: true, RoutingPeerDnsResolutionEnabled: br(false), + LazyConnectionEnabled: br(false), DnsDomain: sr(""), }, expectedArray: false, @@ -171,6 +174,7 @@ func TestAccounts_AccountsHandler(t *testing.T) { JwtAllowGroups: &[]string{}, RegularUsersViewBlocked: true, RoutingPeerDnsResolutionEnabled: br(false), + LazyConnectionEnabled: br(false), DnsDomain: sr(""), }, expectedArray: false, diff --git a/management/server/peer.go b/management/server/peer.go index 8bc6cdb05..f91db928d 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -1221,7 +1221,7 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account return } - update := toSyncResponse(ctx, nil, p, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings.RoutingPeerDNSResolutionEnabled, extraSetting) + update := toSyncResponse(ctx, nil, p, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting) am.peersUpdateManager.SendUpdate(ctx, p.ID, &UpdateMessage{Update: update, NetworkMap: remotePeerNetworkMap}) }(peer) } @@ -1306,7 +1306,7 @@ func (am *DefaultAccountManager) UpdateAccountPeer(ctx context.Context, accountI return } - update := toSyncResponse(ctx, nil, peer, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings.RoutingPeerDNSResolutionEnabled, extraSettings) + update := toSyncResponse(ctx, nil, peer, nil, nil, remotePeerNetworkMap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSettings) am.peersUpdateManager.SendUpdate(ctx, peer.ID, &UpdateMessage{Update: update, NetworkMap: remotePeerNetworkMap}) } diff --git a/management/server/peer_test.go b/management/server/peer_test.go index 9c1de0659..3d782f04c 100644 --- a/management/server/peer_test.go +++ b/management/server/peer_test.go @@ -1157,8 +1157,8 @@ func TestToSyncResponse(t *testing.T) { }, } dnsCache := &DNSConfigCache{} - - response := toSyncResponse(context.Background(), config, peer, turnRelayToken, turnRelayToken, networkMap, dnsName, checks, dnsCache, true, nil) + accountSettings := &types.Settings{RoutingPeerDNSResolutionEnabled: true} + response := toSyncResponse(context.Background(), config, peer, turnRelayToken, turnRelayToken, networkMap, dnsName, checks, dnsCache, accountSettings, nil) assert.NotNil(t, response) // assert peer config diff --git a/management/server/types/settings.go b/management/server/types/settings.go index c8de2a98c..bd361f3ff 100644 --- a/management/server/types/settings.go +++ b/management/server/types/settings.go @@ -44,6 +44,9 @@ type Settings struct { // Extra is a dictionary of Account settings Extra *ExtraSettings `gorm:"embedded;embeddedPrefix:extra_"` + + // LazyConnectionEnabled indicates wether the experimental feature is enabled or disabled + LazyConnectionEnabled bool `gorm:"default:false"` } // Copy copies the Settings struct @@ -61,6 +64,7 @@ func (s *Settings) Copy() *Settings { PeerInactivityExpiration: s.PeerInactivityExpiration, RoutingPeerDNSResolutionEnabled: s.RoutingPeerDNSResolutionEnabled, + LazyConnectionEnabled: s.LazyConnectionEnabled, DNSDomain: s.DNSDomain, } if s.Extra != nil {