implement nmap builder to the code

This commit is contained in:
crn4
2025-09-03 13:28:45 +02:00
parent f4b2bed1b9
commit abd7a84a46
12 changed files with 332 additions and 117 deletions

View File

@@ -51,6 +51,8 @@ const (
peerSchedulerRetryInterval = 3 * time.Second
emptyUserID = "empty user ID in claims"
errorGettingDomainAccIDFmt = "error getting account ID by private domain: %v"
envNewNetworkMapBuilder = "NB_EXPERIMENT_NETWORK_MAP"
)
type userLoggedInOnce bool
@@ -104,6 +106,10 @@ type DefaultAccountManager struct {
updateAccountPeersBufferInterval atomic.Int64
disableDefaultPolicy bool
holder *types.Holder
expNewNetworkMap bool
}
func isUniqueConstraintError(err error) bool {
@@ -191,6 +197,12 @@ func BuildManager(
log.WithContext(ctx).Debugf("took %v to instantiate account manager", time.Since(start))
}()
newNetworkMapBuilder, err := strconv.ParseBool(os.Getenv(envNewNetworkMapBuilder))
if err != nil {
log.WithContext(ctx).Warnf("failed to parse %s, using default value false: %v", envNewNetworkMapBuilder, err)
newNetworkMapBuilder = false
}
am := &DefaultAccountManager{
Store: store,
geo: geo,
@@ -211,6 +223,9 @@ func BuildManager(
settingsManager: settingsManager,
permissionsManager: permissionsManager,
disableDefaultPolicy: disableDefaultPolicy,
holder: types.NewHolder(),
expNewNetworkMap: newNetworkMapBuilder,
}
am.startWarmup(ctx)

View File

@@ -0,0 +1,16 @@
package server
import (
"github.com/netbirdio/netbird/management/server/types"
)
func (am *DefaultAccountManager) enrichAccountFromHolder(account *types.Account) {
a := am.holder.GetAccount(account.Id)
if a == nil {
am.holder.AddAccount(account)
return
}
account.NetworkMapCache = a.NetworkMapCache
account.NetworkMapCache.UpdateAccountPointer(account)
am.holder.AddAccount(account)
}

View File

@@ -0,0 +1,37 @@
package server
import (
"context"
nbdns "github.com/netbirdio/netbird/dns"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/management/server/types"
)
func (am *DefaultAccountManager) getPeerNetworkMapExp(
ctx context.Context,
account *types.Account,
peerId string,
validatedPeers map[string]struct{},
customZone nbdns.CustomZone,
metrics *telemetry.AccountManagerMetrics,
) *types.NetworkMap {
am.enrichAccountFromHolder(account)
return account.GetPeerNetworkMapExp(ctx, peerId, customZone, validatedPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), metrics)
}
func (am *DefaultAccountManager) onPeerAddedUpdNetworkMapCache(account *types.Account, peerId string, validatedPeers map[string]struct{}) {
am.enrichAccountFromHolder(account)
account.OnPeerAddedUpdNetworkMapCache(peerId, validatedPeers)
}
func (am *DefaultAccountManager) onPeerDeletedUpdNetworkMapCache(account *types.Account, peerId string, validatedPeers map[string]struct{}) {
am.enrichAccountFromHolder(account)
account.OnPeerDeletedUpdNetworkMapCache(peerId, validatedPeers)
}
func (am *DefaultAccountManager) updatePeerInNetworkMapCache(account *types.Account, peer *nbpeer.Peer) {
am.enrichAccountFromHolder(account)
account.UpdatePeerInNetworkMapCache(peer)
}

View File

@@ -389,6 +389,20 @@ func (am *DefaultAccountManager) DeletePeer(ctx context.Context, accountID, peer
storeEvent()
}
if updateAccountPeers && am.expNewNetworkMap {
account, err := am.Store.GetAccount(ctx, accountID)
if err != nil {
return err
}
validatedPeers, err := am.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra)
if err != nil {
return err
}
am.onPeerDeletedUpdNetworkMapCache(account, peerID, validatedPeers)
}
if updateAccountPeers && userID != activity.SystemInitiator {
am.BufferUpdateAccountPeers(ctx, accountID)
}
@@ -425,7 +439,13 @@ func (am *DefaultAccountManager) GetNetworkMap(ctx context.Context, peerID strin
return nil, err
}
networkMap := account.GetPeerNetworkMap(ctx, peer.ID, customZone, validatedPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil)
var networkMap *types.NetworkMap
if am.expNewNetworkMap {
networkMap = am.getPeerNetworkMapExp(ctx, account, peerID, validatedPeers, customZone, nil)
} else {
networkMap = account.GetPeerNetworkMap(ctx, peer.ID, customZone, validatedPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil)
}
proxyNetworkMap, ok := proxyNetworkMaps[peer.ID]
if ok {
@@ -695,6 +715,19 @@ func (am *DefaultAccountManager) AddPeer(ctx context.Context, setupKey, userID s
am.StoreEvent(ctx, opEvent.InitiatorID, opEvent.TargetID, opEvent.AccountID, opEvent.Activity, opEvent.Meta)
if am.expNewNetworkMap {
account, err := am.Store.GetAccount(ctx, accountID)
if err != nil {
return nil, nil, nil, err
}
validatedPeers, err := am.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra)
if err != nil {
return nil, nil, nil, err
}
am.onPeerAddedUpdNetworkMapCache(account, newPeer.ID, validatedPeers)
}
if updateAccountPeers {
am.BufferUpdateAccountPeers(ctx, accountID)
}
@@ -783,6 +816,11 @@ func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync types.PeerSy
}
if isStatusChanged || sync.UpdateAccountPeers || (updated && len(postureChecks) > 0) {
account, err := am.Store.GetAccountByPeerID(ctx, peer.ID)
if err != nil {
return nil, nil, nil, err
}
am.updatePeerInNetworkMapCache(account, peer)
am.BufferUpdateAccountPeers(ctx, accountID)
}
@@ -1055,7 +1093,13 @@ func (am *DefaultAccountManager) getValidatedPeerWithMap(ctx context.Context, is
return nil, nil, nil, err
}
networkMap := account.GetPeerNetworkMap(ctx, peer.ID, customZone, approvedPeersMap, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), am.metrics.AccountManagerMetrics())
var networkMap *types.NetworkMap
if am.expNewNetworkMap {
networkMap = am.getPeerNetworkMapExp(ctx, account, peer.ID, approvedPeersMap, customZone, am.metrics.AccountManagerMetrics())
} else {
networkMap = account.GetPeerNetworkMap(ctx, peer.ID, customZone, approvedPeersMap, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), am.metrics.AccountManagerMetrics())
}
proxyNetworkMap, ok := proxyNetworkMaps[peer.ID]
if ok {
@@ -1257,7 +1301,13 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account
am.metrics.UpdateChannelMetrics().CountCalcPostureChecksDuration(time.Since(start))
start = time.Now()
remotePeerNetworkMap := account.GetPeerNetworkMap(ctx, p.ID, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics())
var remotePeerNetworkMap *types.NetworkMap
if am.expNewNetworkMap {
remotePeerNetworkMap = am.getPeerNetworkMapExp(ctx, account, peer.ID, approvedPeersMap, customZone, am.metrics.AccountManagerMetrics())
} else {
remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, p.ID, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics())
}
am.metrics.UpdateChannelMetrics().CountCalcPeerNetworkMapDuration(time.Since(start))
start = time.Now()
@@ -1366,7 +1416,13 @@ func (am *DefaultAccountManager) UpdateAccountPeer(ctx context.Context, accountI
return
}
remotePeerNetworkMap := account.GetPeerNetworkMap(ctx, peerId, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics())
var remotePeerNetworkMap *types.NetworkMap
if am.expNewNetworkMap {
remotePeerNetworkMap = am.getPeerNetworkMapExp(ctx, account, peer.ID, approvedPeersMap, customZone, am.metrics.AccountManagerMetrics())
} else {
remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, peerId, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics())
}
proxyNetworkMap, ok := proxyNetworkMaps[peer.ID]
if ok {

View File

@@ -166,6 +166,15 @@ func TestPeer_SessionExpired(t *testing.T) {
}
func TestAccountManager_GetNetworkMap(t *testing.T) {
testGetNetworkMapGeneral(t)
}
func TestAccountManager_GetNetworkMap_Experimental(t *testing.T) {
os.Setenv(envNewNetworkMapBuilder, "true")
testGetNetworkMapGeneral(t)
}
func testGetNetworkMapGeneral(t *testing.T) {
manager, err := createManager(t)
if err != nil {
t.Fatal(err)
@@ -1545,6 +1554,7 @@ func Test_RegisterPeerRollbackOnFailure(t *testing.T) {
}
func Test_LoginPeer(t *testing.T) {
os.Setenv(envNewNetworkMapBuilder, "true")
if runtime.GOOS == "windows" {
t.Skip("The SQLite store is not properly supported by Windows yet")
}

View File

@@ -86,6 +86,8 @@ type Account struct {
NetworkRouters []*routerTypes.NetworkRouter `gorm:"foreignKey:AccountID;references:id"`
NetworkResources []*resourceTypes.NetworkResource `gorm:"foreignKey:AccountID;references:id"`
Onboarding AccountOnboarding `gorm:"foreignKey:AccountID;references:id;constraint:OnDelete:CASCADE"`
NetworkMapCache *NetworkMapBuilder `gorm:"-"`
}
// Subclass used in gorm to only load network and not whole account

View File

@@ -0,0 +1,26 @@
package types
import "sync"
type Holder struct {
mu sync.RWMutex
accounts map[string]*Account
}
func NewHolder() *Holder {
return &Holder{
accounts: make(map[string]*Account),
}
}
func (h *Holder) GetAccount(id string) *Account {
h.mu.RLock()
defer h.mu.RUnlock()
return h.accounts[id]
}
func (h *Holder) AddAccount(account *Account) {
h.mu.Lock()
defer h.mu.Unlock()
h.accounts[account.Id] = account
}

View File

@@ -841,3 +841,38 @@ func peerIsNameserver(peer *nbpeer.Peer, nsGroup *nbdns.NameServerGroup) bool {
}
return false
}
func (a *Account) initNetworkMapBuilder(validatedPeers map[string]struct{}) {
if a.NetworkMapCache != nil {
return
}
a.NetworkMapCache = NewNetworkMapBuilder(a, validatedPeers)
}
func (a *Account) GetPeerNetworkMapExp(
ctx context.Context,
peerID string,
peersCustomZone nbdns.CustomZone,
validatedPeers map[string]struct{},
resourcePolicies map[string][]*Policy,
routers map[string]map[string]*routerTypes.NetworkRouter,
metrics *telemetry.AccountManagerMetrics,
) *NetworkMap {
a.initNetworkMapBuilder(validatedPeers)
return a.NetworkMapCache.GetPeerNetworkMap(ctx, peerID, peersCustomZone, validatedPeers, resourcePolicies, routers, metrics)
}
func (a *Account) OnPeerAddedUpdNetworkMapCache(peerId string, validatedPeers map[string]struct{}) error {
a.initNetworkMapBuilder(validatedPeers)
return a.NetworkMapCache.OnPeerAddedIncremental(peerId)
}
func (a *Account) OnPeerDeletedUpdNetworkMapCache(peerId string, validatedPeers map[string]struct{}) error {
a.initNetworkMapBuilder(validatedPeers)
return a.NetworkMapCache.OnPeerDeleted(peerId)
}
func (a *Account) UpdatePeerInNetworkMapCache(peer *nbpeer.Peer) {
a.initNetworkMapBuilder(nil)
a.NetworkMapCache.UpdatePeer(peer)
}

View File

@@ -105,7 +105,7 @@ func TestGetPeerNetworkMap_Golden_New(t *testing.T) {
validatedPeersMap[peerID] = struct{}{}
}
builder := types.NewImprovedNetworkMapBuilder(account, validatedPeersMap)
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
networkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil, nil, nil)
normalizeAndSortNetworkMap(networkMap)
@@ -149,7 +149,7 @@ func BenchmarkGetPeerNetworkMap(b *testing.B) {
b.ResetTimer()
b.Run("new builder", func(b *testing.B) {
for range b.N {
builder := types.NewImprovedNetworkMapBuilder(account, validatedPeersMap)
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
for _, peerID := range peerIDs {
_ = builder.GetPeerNetworkMap(ctx, peerID, dns.CustomZone{}, validatedPeersMap, nil, nil, nil)
}
@@ -236,7 +236,7 @@ func TestGetPeerNetworkMap_Golden_New_WithOnPeerAdded(t *testing.T) {
validatedPeersMap[peerID] = struct{}{}
}
builder := types.NewImprovedNetworkMapBuilder(account, validatedPeersMap)
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
newPeerID := "peer-new-101"
newPeerIP := net.IP{100, 64, 1, 1}
@@ -299,7 +299,7 @@ func BenchmarkGetPeerNetworkMap_AfterPeerAdded(b *testing.B) {
validatedPeersMap[peerID] = struct{}{}
peerIDs = append(peerIDs, peerID)
}
builder := types.NewImprovedNetworkMapBuilder(account, validatedPeersMap)
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
newPeerID := "peer-new-101"
newPeer := &nbpeer.Peer{
ID: newPeerID,
@@ -429,7 +429,7 @@ func TestGetPeerNetworkMap_Golden_New_WithOnPeerAddedRouter(t *testing.T) {
validatedPeersMap[peerID] = struct{}{}
}
builder := types.NewImprovedNetworkMapBuilder(account, validatedPeersMap)
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
newRouterID := "peer-new-router-102"
newRouterIP := net.IP{100, 64, 1, 2}
@@ -507,7 +507,7 @@ func BenchmarkGetPeerNetworkMap_AfterRouterPeerAdded(b *testing.B) {
validatedPeersMap[peerID] = struct{}{}
peerIDs = append(peerIDs, peerID)
}
builder := types.NewImprovedNetworkMapBuilder(account, validatedPeersMap)
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
newRouterID := "peer-new-router-102"
newRouterIP := net.IP{100, 64, 1, 2}
newRouter := &nbpeer.Peer{
@@ -638,7 +638,7 @@ func TestGetPeerNetworkMap_Golden_New_WithOnPeerDeleted(t *testing.T) {
validatedPeersMap[peerID] = struct{}{}
}
builder := types.NewImprovedNetworkMapBuilder(account, validatedPeersMap)
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
deletedPeerID := "peer-25" // devs group peer
@@ -764,7 +764,7 @@ func TestGetPeerNetworkMap_Golden_New_WithDeletedRouterPeer(t *testing.T) {
validatedPeersMap[peerID] = struct{}{}
}
builder := types.NewImprovedNetworkMapBuilder(account, validatedPeersMap)
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
deletedRouterID := "peer-75" // router peer
@@ -841,7 +841,7 @@ func BenchmarkGetPeerNetworkMap_AfterPeerDeleted(b *testing.B) {
})
delete(validatedPeersMap, deletedPeerID)
builder := types.NewImprovedNetworkMapBuilder(account, validatedPeersMap)
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
b.ResetTimer()
b.Run("old builder after delete", func(b *testing.B) {

View File

@@ -6,6 +6,7 @@ import (
"slices"
"strings"
"sync"
"sync/atomic"
"time"
log "github.com/sirupsen/logrus"
@@ -48,15 +49,14 @@ type PeerRoutesView struct {
RouteFirewallRuleIDs []string
}
type ImprovedNetworkMapBuilder struct {
account *Account
type NetworkMapBuilder struct {
account atomic.Pointer[Account]
cache *NetworkMapCache
validatedPeers map[string]struct{}
}
func NewImprovedNetworkMapBuilder(account *Account, validatedPeers map[string]struct{}) *ImprovedNetworkMapBuilder {
builder := &ImprovedNetworkMapBuilder{
account: account,
func NewNetworkMapBuilder(account *Account, validatedPeers map[string]struct{}) *NetworkMapBuilder {
builder := &NetworkMapBuilder{
cache: &NetworkMapCache{
globalRoutes: make(map[route.ID]*route.Route),
globalRules: make(map[string]*FirewallRule),
@@ -72,45 +72,43 @@ func NewImprovedNetworkMapBuilder(account *Account, validatedPeers map[string]st
},
validatedPeers: make(map[string]struct{}),
}
builder.account.Store(account)
maps.Copy(builder.validatedPeers, validatedPeers)
for k, v := range validatedPeers {
builder.validatedPeers[k] = v
}
builder.initialBuild()
builder.initialBuild(account)
return builder
}
func (b *ImprovedNetworkMapBuilder) initialBuild() {
func (b *NetworkMapBuilder) initialBuild(account *Account) {
b.cache.mu.Lock()
defer b.cache.mu.Unlock()
start := time.Now()
b.buildGlobalIndexes()
b.buildGlobalIndexes(account)
for peerID := range b.account.Peers {
b.buildPeerACLView(peerID)
b.buildPeerRoutesView(peerID)
b.buildPeerDNSView(peerID)
for peerID := range account.Peers {
b.buildPeerACLView(account, peerID)
b.buildPeerRoutesView(account, peerID)
b.buildPeerDNSView(account, peerID)
}
log.Debugf("NetworkMapBuilder: Initial build completed in %v for account %s", time.Since(start), b.account.Id)
log.Debugf("NetworkMapBuilder: Initial build completed in %v for account %s", time.Since(start), account.Id)
}
func (b *ImprovedNetworkMapBuilder) buildGlobalIndexes() {
func (b *NetworkMapBuilder) buildGlobalIndexes(account *Account) {
clear(b.cache.globalPeers)
clear(b.cache.groupToPeers)
clear(b.cache.peerToGroups)
clear(b.cache.policyToRules)
clear(b.cache.groupToPolicies)
for id, peer := range b.account.Peers {
for id, peer := range account.Peers {
b.cache.globalPeers[id] = peer
}
for groupID, group := range b.account.Groups {
for groupID, group := range account.Groups {
peersCopy := make([]string, len(group.Peers))
copy(peersCopy, group.Peers)
b.cache.groupToPeers[groupID] = peersCopy
@@ -120,7 +118,7 @@ func (b *ImprovedNetworkMapBuilder) buildGlobalIndexes() {
}
}
for _, policy := range b.account.Policies {
for _, policy := range account.Policies {
if !policy.Enabled {
continue
}
@@ -147,21 +145,21 @@ func (b *ImprovedNetworkMapBuilder) buildGlobalIndexes() {
}
}
func (b *ImprovedNetworkMapBuilder) buildPeerACLView(peerID string) {
func (b *NetworkMapBuilder) buildPeerACLView(account *Account, peerID string) {
ctx := context.Background()
peer := b.account.GetPeer(peerID)
peer := account.GetPeer(peerID)
if peer == nil {
return
}
allPotentialPeers, firewallRules := b.account.GetPeerConnectionResources(ctx, peer, b.validatedPeers)
allPotentialPeers, firewallRules := account.GetPeerConnectionResources(ctx, peer, b.validatedPeers)
resourceRouters := b.account.GetResourceRoutersMap()
resourcePolicies := b.account.GetResourcePoliciesMap()
isRouter, networkResourcesRoutes, sourcePeers := b.account.GetNetworkResourcesRoutesToSync(ctx, peerID, resourcePolicies, resourceRouters)
resourceRouters := account.GetResourceRoutersMap()
resourcePolicies := account.GetResourcePoliciesMap()
isRouter, networkResourcesRoutes, sourcePeers := account.GetNetworkResourcesRoutesToSync(ctx, peerID, resourcePolicies, resourceRouters)
var emptyExpiredPeers []*nbpeer.Peer
finalAllPeers := b.account.addNetworksRoutingPeers(
finalAllPeers := account.addNetworksRoutingPeers(
networkResourcesRoutes,
peer,
allPotentialPeers,
@@ -188,9 +186,9 @@ func (b *ImprovedNetworkMapBuilder) buildPeerACLView(peerID string) {
b.cache.peerACLs[peerID] = view
}
func (b *ImprovedNetworkMapBuilder) buildPeerRoutesView(peerID string) {
func (b *NetworkMapBuilder) buildPeerRoutesView(account *Account, peerID string) {
ctx := context.Background()
peer := b.account.GetPeer(peerID)
peer := account.GetPeer(peerID)
if peer == nil {
return
}
@@ -201,7 +199,7 @@ func (b *ImprovedNetworkMapBuilder) buildPeerRoutesView(peerID string) {
RouteFirewallRuleIDs: make([]string, 0),
}
enabledRoutes, disabledRoutes := b.account.getRoutingPeerRoutes(ctx, peerID)
enabledRoutes, disabledRoutes := account.getRoutingPeerRoutes(ctx, peerID)
for _, rt := range enabledRoutes {
if rt.PeerID != "" && rt.PeerID != peerID {
if b.cache.globalPeers[rt.PeerID] == nil {
@@ -231,9 +229,9 @@ func (b *ImprovedNetworkMapBuilder) buildPeerRoutesView(peerID string) {
continue
}
activeRoutes, _ := b.account.getRoutingPeerRoutes(ctx, aclPeerID)
groupFilteredRoutes := b.account.filterRoutesByGroups(activeRoutes, peerGroupsMap)
haFilteredRoutes := b.account.filterRoutesFromPeersOfSameHAGroup(groupFilteredRoutes, peerRoutesMembership)
activeRoutes, _ := account.getRoutingPeerRoutes(ctx, aclPeerID)
groupFilteredRoutes := account.filterRoutesByGroups(activeRoutes, peerGroupsMap)
haFilteredRoutes := account.filterRoutesFromPeersOfSameHAGroup(groupFilteredRoutes, peerRoutesMembership)
for _, inheritedRoute := range haFilteredRoutes {
b.cache.globalRoutes[inheritedRoute.ID] = inheritedRoute
@@ -241,16 +239,16 @@ func (b *ImprovedNetworkMapBuilder) buildPeerRoutesView(peerID string) {
}
}
resourceRouters := b.account.GetResourceRoutersMap()
resourcePolicies := b.account.GetResourcePoliciesMap()
_, networkResourcesRoutes, _ := b.account.GetNetworkResourcesRoutesToSync(ctx, peerID, resourcePolicies, resourceRouters)
resourceRouters := account.GetResourceRoutersMap()
resourcePolicies := account.GetResourcePoliciesMap()
_, networkResourcesRoutes, _ := account.GetNetworkResourcesRoutesToSync(ctx, peerID, resourcePolicies, resourceRouters)
for _, rt := range networkResourcesRoutes {
view.NetworkResourceIDs = append(view.NetworkResourceIDs, rt.ID)
b.cache.globalRoutes[rt.ID] = rt
}
routeFirewallRules := b.account.GetPeerRoutesFirewallRules(ctx, peerID, b.validatedPeers)
routeFirewallRules := account.GetPeerRoutesFirewallRules(ctx, peerID, b.validatedPeers)
for _, rule := range routeFirewallRules {
ruleID := b.generateRouteFirewallRuleID(rule)
view.RouteFirewallRuleIDs = append(view.RouteFirewallRuleIDs, ruleID)
@@ -258,7 +256,7 @@ func (b *ImprovedNetworkMapBuilder) buildPeerRoutesView(peerID string) {
}
if len(networkResourcesRoutes) > 0 {
networkResourceFirewallRules := b.account.GetPeerNetworkResourceFirewallRules(ctx, peer, b.validatedPeers, networkResourcesRoutes, resourcePolicies)
networkResourceFirewallRules := account.GetPeerNetworkResourceFirewallRules(ctx, peer, b.validatedPeers, networkResourcesRoutes, resourcePolicies)
for _, rule := range networkResourceFirewallRules {
ruleID := b.generateRouteFirewallRuleID(rule)
view.RouteFirewallRuleIDs = append(view.RouteFirewallRuleIDs, ruleID)
@@ -269,20 +267,24 @@ func (b *ImprovedNetworkMapBuilder) buildPeerRoutesView(peerID string) {
b.cache.peerRoutes[peerID] = view
}
func (b *ImprovedNetworkMapBuilder) buildPeerDNSView(peerID string) {
dnsManagementStatus := b.account.getPeerDNSManagementStatus(peerID)
func (b *NetworkMapBuilder) buildPeerDNSView(account *Account, peerID string) {
dnsManagementStatus := account.getPeerDNSManagementStatus(peerID)
dnsConfig := &nbdns.Config{
ServiceEnable: dnsManagementStatus,
}
if dnsManagementStatus {
dnsConfig.NameServerGroups = getPeerNSGroups(b.account, peerID)
dnsConfig.NameServerGroups = getPeerNSGroups(account, peerID)
}
b.cache.peerDNS[peerID] = dnsConfig
}
func (b *ImprovedNetworkMapBuilder) GetPeerNetworkMap(
func (b *NetworkMapBuilder) UpdateAccountPointer(account *Account) {
b.account.Store(account)
}
func (b *NetworkMapBuilder) GetPeerNetworkMap(
ctx context.Context,
peerID string,
peersCustomZone nbdns.CustomZone,
@@ -292,10 +294,11 @@ func (b *ImprovedNetworkMapBuilder) GetPeerNetworkMap(
metrics *telemetry.AccountManagerMetrics,
) *NetworkMap {
start := time.Now()
account := b.account.Load()
peer := b.account.GetPeer(peerID)
peer := account.GetPeer(peerID)
if peer == nil {
return &NetworkMap{Network: b.account.Network.Copy()}
return &NetworkMap{Network: account.Network.Copy()}
}
if !maps.Equal(b.validatedPeers, validatedPeers) {
@@ -311,10 +314,10 @@ func (b *ImprovedNetworkMapBuilder) GetPeerNetworkMap(
if aclView == nil || routesView == nil || dnsConfig == nil {
// log.Warnf("NetworkMapBuilder: Cache miss for peer %s, falling back to original method", peerID)
// return b.account.GetPeerNetworkMap(ctx, peerID, peersCustomZone, validatedPeers, resourcePolicies, routers, metrics)
// return account.GetPeerNetworkMap(ctx, peerID, peersCustomZone, validatedPeers, resourcePolicies, routers, metrics)
}
nm := b.assembleNetworkMap(aclView, routesView, dnsConfig, peersCustomZone, validatedPeers)
nm := b.assembleNetworkMap(account, aclView, routesView, dnsConfig, peersCustomZone, validatedPeers)
if metrics != nil {
objectCount := int64(len(nm.Peers) + len(nm.OfflinePeers) + len(nm.Routes) + len(nm.FirewallRules) + len(nm.RoutesFirewallRules))
@@ -323,14 +326,15 @@ func (b *ImprovedNetworkMapBuilder) GetPeerNetworkMap(
if objectCount > 5000 {
log.WithContext(ctx).Tracef("account: %s has a total resource count of %d objects from cache",
b.account.Id, objectCount)
account.Id, objectCount)
}
}
return nm
}
func (b *ImprovedNetworkMapBuilder) assembleNetworkMap(
func (b *NetworkMapBuilder) assembleNetworkMap(
account *Account,
aclView *PeerACLView,
routesView *PeerRoutesView,
dnsConfig *nbdns.Config,
@@ -351,8 +355,8 @@ func (b *ImprovedNetworkMapBuilder) assembleNetworkMap(
continue
}
expired, _ := peer.LoginExpired(b.account.Settings.PeerLoginExpiration)
if b.account.Settings.PeerLoginExpirationEnabled && expired {
expired, _ := peer.LoginExpired(account.Settings.PeerLoginExpiration)
if account.Settings.PeerLoginExpirationEnabled && expired {
expiredPeers = append(expiredPeers, peer)
} else {
peersToConnect = append(peersToConnect, peer)
@@ -389,7 +393,7 @@ func (b *ImprovedNetworkMapBuilder) assembleNetworkMap(
return &NetworkMap{
Peers: peersToConnect,
Network: b.account.Network.Copy(),
Network: account.Network.Copy(),
Routes: routes,
DNSConfig: finalDNSConfig,
OfflinePeers: expiredPeers,
@@ -398,19 +402,19 @@ func (b *ImprovedNetworkMapBuilder) assembleNetworkMap(
}
}
func (b *ImprovedNetworkMapBuilder) generateFirewallRuleID(rule *FirewallRule) string {
func (b *NetworkMapBuilder) generateFirewallRuleID(rule *FirewallRule) string {
portRange := ""
portRange = fmt.Sprintf("%d-%d", rule.PortRange.Start, rule.PortRange.End)
return fmt.Sprintf("fw:%s:%s:%d:%s:%s:%s:%s",
rule.PolicyID, rule.PeerIP, rule.Direction, rule.Protocol, rule.Action, rule.Port, portRange)
}
func (b *ImprovedNetworkMapBuilder) generateRouteFirewallRuleID(rule *RouteFirewallRule) string {
func (b *NetworkMapBuilder) generateRouteFirewallRuleID(rule *RouteFirewallRule) string {
return fmt.Sprintf("route-fw:%s:%s:%s:%s:%s:%d",
rule.RouteID, rule.Destination, rule.Action, strings.Join(rule.SourceRanges, ","), rule.Protocol, rule.Port)
}
// func (b *ImprovedNetworkMapBuilder) estimateMemoryUsage() int64 {
// func (b *NetworkMapBuilder) estimateMemoryUsage() int64 {
// b.cache.mu.RLock()
// defer b.cache.mu.RUnlock()
@@ -449,7 +453,7 @@ func (b *ImprovedNetworkMapBuilder) generateRouteFirewallRuleID(rule *RouteFirew
// return estimate
// }
func (b *ImprovedNetworkMapBuilder) isPeerInGroups(groupIDs []string, peerGroups []string) bool {
func (b *NetworkMapBuilder) isPeerInGroups(groupIDs []string, peerGroups []string) bool {
for _, groupID := range groupIDs {
for _, peerGroupID := range peerGroups {
if groupID == peerGroupID {
@@ -460,8 +464,8 @@ func (b *ImprovedNetworkMapBuilder) isPeerInGroups(groupIDs []string, peerGroups
return false
}
func (b *ImprovedNetworkMapBuilder) isPeerRouter(peerID string) bool {
for _, r := range b.account.Routes {
func (b *NetworkMapBuilder) isPeerRouter(account *Account, peerID string) bool {
for _, r := range account.Routes {
if !r.Enabled {
continue
}
@@ -478,7 +482,7 @@ func (b *ImprovedNetworkMapBuilder) isPeerRouter(peerID string) bool {
}
}
routers := b.account.GetResourceRoutersMap()
routers := account.GetResourceRoutersMap()
for _, networkRouters := range routers {
if router, exists := networkRouters[peerID]; exists && router.Enabled {
return true
@@ -504,8 +508,9 @@ type ViewDelta struct {
RemovedRuleIDs []string
}
func (b *ImprovedNetworkMapBuilder) OnPeerAddedIncremental(peerID string) error {
peer := b.account.GetPeer(peerID)
func (b *NetworkMapBuilder) OnPeerAddedIncremental(peerID string) error {
account := b.account.Load()
peer := account.GetPeer(peerID)
if peer == nil {
return fmt.Errorf("peer %s not found in account", peerID)
}
@@ -517,21 +522,21 @@ func (b *ImprovedNetworkMapBuilder) OnPeerAddedIncremental(peerID string) error
b.cache.globalPeers[peerID] = peer
peerGroups := b.updateGroupIndexesForNewPeer(peerID)
peerGroups := b.updateGroupIndexesForNewPeer(account, peerID)
b.buildPeerACLView(peerID)
b.buildPeerRoutesView(peerID)
b.buildPeerDNSView(peerID)
b.buildPeerACLView(account, peerID)
b.buildPeerRoutesView(account, peerID)
b.buildPeerDNSView(account, peerID)
b.incrementalUpdateAffectedPeers(peerID, peerGroups)
b.incrementalUpdateAffectedPeers(account, peerID, peerGroups)
return nil
}
func (b *ImprovedNetworkMapBuilder) updateGroupIndexesForNewPeer(peerID string) []string {
func (b *NetworkMapBuilder) updateGroupIndexesForNewPeer(account *Account, peerID string) []string {
peerGroups := make([]string, 0)
for groupID, group := range b.account.Groups {
for groupID, group := range account.Groups {
for _, pid := range group.Peers {
if pid == peerID {
if !slices.Contains(b.cache.groupToPeers[groupID], peerID) {
@@ -547,13 +552,13 @@ func (b *ImprovedNetworkMapBuilder) updateGroupIndexesForNewPeer(peerID string)
return peerGroups
}
func (b *ImprovedNetworkMapBuilder) incrementalUpdateAffectedPeers(newPeerID string, peerGroups []string) {
func (b *NetworkMapBuilder) incrementalUpdateAffectedPeers(account *Account, newPeerID string, peerGroups []string) {
ctx := context.Background()
updates := b.calculateIncrementalUpdates(newPeerID, peerGroups)
updates := b.calculateIncrementalUpdates(account, newPeerID, peerGroups)
if b.isPeerRouter(newPeerID) {
affectedByRoutes := b.findPeersAffectedByNewRouter(ctx, newPeerID, peerGroups)
if b.isPeerRouter(account, newPeerID) {
affectedByRoutes := b.findPeersAffectedByNewRouter(ctx, account, newPeerID, peerGroups)
for affectedPeerID := range affectedByRoutes {
if affectedPeerID == newPeerID {
continue
@@ -570,14 +575,14 @@ func (b *ImprovedNetworkMapBuilder) incrementalUpdateAffectedPeers(newPeerID str
}
for affectedPeerID, delta := range updates {
b.applyDeltaToPeer(affectedPeerID, delta)
b.applyDeltaToPeer(account, affectedPeerID, delta)
}
}
func (b *ImprovedNetworkMapBuilder) findPeersAffectedByNewRouter(ctx context.Context, newRouterID string, routerGroups []string) map[string]struct{} {
func (b *NetworkMapBuilder) findPeersAffectedByNewRouter(ctx context.Context, account *Account, newRouterID string, routerGroups []string) map[string]struct{} {
affected := make(map[string]struct{})
enabledRoutes, _ := b.account.getRoutingPeerRoutes(ctx, newRouterID)
enabledRoutes, _ := account.getRoutingPeerRoutes(ctx, newRouterID)
for _, route := range enabledRoutes {
for _, distGroupID := range route.Groups {
@@ -601,7 +606,7 @@ func (b *ImprovedNetworkMapBuilder) findPeersAffectedByNewRouter(ctx context.Con
}
}
for _, route := range b.account.Routes {
for _, route := range account.Routes {
if !route.Enabled {
continue
}
@@ -628,7 +633,7 @@ func (b *ImprovedNetworkMapBuilder) findPeersAffectedByNewRouter(ctx context.Con
return affected
}
func (b *ImprovedNetworkMapBuilder) calculateIncrementalUpdates(newPeerID string, peerGroups []string) map[string]*PeerUpdateDelta {
func (b *NetworkMapBuilder) calculateIncrementalUpdates(account *Account, newPeerID string, peerGroups []string) map[string]*PeerUpdateDelta {
updates := make(map[string]*PeerUpdateDelta)
ctx := context.Background()
@@ -637,7 +642,7 @@ func (b *ImprovedNetworkMapBuilder) calculateIncrementalUpdates(newPeerID string
return updates
}
for _, policy := range b.account.Policies {
for _, policy := range account.Policies {
if !policy.Enabled {
continue
}
@@ -671,12 +676,12 @@ func (b *ImprovedNetworkMapBuilder) calculateIncrementalUpdates(newPeerID string
b.calculateRouteFirewallUpdates(newPeerID, newPeer, peerGroups, updates)
b.calculateNetworkResourceFirewallUpdates(ctx, newPeerID, newPeer, peerGroups, updates)
b.calculateNetworkResourceFirewallUpdates(ctx, account, newPeerID, newPeer, peerGroups, updates)
return updates
}
func (b *ImprovedNetworkMapBuilder) calculateRouteFirewallUpdates(
func (b *NetworkMapBuilder) calculateRouteFirewallUpdates(
newPeerID string,
newPeer *nbpeer.Peer,
peerGroups []string,
@@ -710,7 +715,7 @@ func (b *ImprovedNetworkMapBuilder) calculateRouteFirewallUpdates(
}
}
func (b *ImprovedNetworkMapBuilder) addRouteFirewallUpdate(
func (b *NetworkMapBuilder) addRouteFirewallUpdate(
updates map[string]*PeerUpdateDelta,
peerID string,
routeID string,
@@ -737,17 +742,18 @@ func (b *ImprovedNetworkMapBuilder) addRouteFirewallUpdate(
})
}
func (b *ImprovedNetworkMapBuilder) calculateNetworkResourceFirewallUpdates(
func (b *NetworkMapBuilder) calculateNetworkResourceFirewallUpdates(
ctx context.Context,
account *Account,
newPeerID string,
newPeer *nbpeer.Peer,
peerGroups []string,
updates map[string]*PeerUpdateDelta,
) {
resourcePolicies := b.account.GetResourcePoliciesMap()
routers := b.account.GetResourceRoutersMap()
resourcePolicies := account.GetResourcePoliciesMap()
routers := account.GetResourceRoutersMap()
for _, resource := range b.account.NetworkResources {
for _, resource := range account.NetworkResources {
if !resource.Enabled {
continue
}
@@ -763,7 +769,7 @@ func (b *ImprovedNetworkMapBuilder) calculateNetworkResourceFirewallUpdates(
sourceGroups := policy.SourceGroups()
for _, sourceGroup := range sourceGroups {
if slices.Contains(peerGroups, sourceGroup) {
if b.account.validatePostureChecksOnPeer(ctx, policy.SourcePostureChecks, newPeerID) {
if account.validatePostureChecksOnPeer(ctx, policy.SourcePostureChecks, newPeerID) {
peerHasAccess = true
break
}
@@ -824,7 +830,7 @@ type RouteFirewallRuleUpdate struct {
AddSourceIP string
}
func (b *ImprovedNetworkMapBuilder) addUpdateForPeersInGroups(
func (b *NetworkMapBuilder) addUpdateForPeersInGroups(
updates map[string]*PeerUpdateDelta,
groupIDs []string,
newPeerID string,
@@ -885,7 +891,7 @@ func (b *ImprovedNetworkMapBuilder) addUpdateForPeersInGroups(
}
}
func (b *ImprovedNetworkMapBuilder) applyDeltaToPeer(peerID string, delta *PeerUpdateDelta) {
func (b *NetworkMapBuilder) applyDeltaToPeer(account *Account, peerID string, delta *PeerUpdateDelta) {
if delta.AddConnectedPeer != "" || len(delta.AddFirewallRules) > 0 {
if aclView := b.cache.peerACLs[peerID]; aclView != nil {
if delta.AddConnectedPeer != "" && !slices.Contains(aclView.ConnectedPeerIDs, delta.AddConnectedPeer) {
@@ -903,7 +909,7 @@ func (b *ImprovedNetworkMapBuilder) applyDeltaToPeer(peerID string, delta *PeerU
}
if delta.RebuildRoutesView {
b.buildPeerRoutesView(peerID)
b.buildPeerRoutesView(account, peerID)
} else if len(delta.UpdateRouteFirewallRules) > 0 {
if routesView := b.cache.peerRoutes[peerID]; routesView != nil {
b.updateRouteFirewallRules(routesView, delta.UpdateRouteFirewallRules)
@@ -911,10 +917,10 @@ func (b *ImprovedNetworkMapBuilder) applyDeltaToPeer(peerID string, delta *PeerU
}
if delta.UpdateDNS {
b.buildPeerDNSView(peerID)
b.buildPeerDNSView(account, peerID)
}
}
func (b *ImprovedNetworkMapBuilder) updateRouteFirewallRules(
func (b *NetworkMapBuilder) updateRouteFirewallRules(
routesView *PeerRoutesView,
updates []*RouteFirewallRuleUpdate,
) {
@@ -951,10 +957,12 @@ func (b *ImprovedNetworkMapBuilder) updateRouteFirewallRules(
}
}
func (b *ImprovedNetworkMapBuilder) OnPeerDeleted(peerID string) error {
func (b *NetworkMapBuilder) OnPeerDeleted(peerID string) error {
b.cache.mu.Lock()
defer b.cache.mu.Unlock()
account := b.account.Load()
deletedPeer := b.cache.globalPeers[peerID]
if deletedPeer == nil {
return fmt.Errorf("peer %s not found in cache", peerID)
@@ -969,15 +977,15 @@ func (b *ImprovedNetworkMapBuilder) OnPeerDeleted(peerID string) error {
delete(b.validatedPeers, peerID)
routesToDelete := []route.ID{}
for routeID, r := range b.account.Routes {
for routeID, r := range account.Routes {
if r.Peer == deletedPeerKey || r.PeerID == peerID {
if len(r.PeerGroups) > 0 {
newPeerAssigned := false
for _, groupID := range r.PeerGroups {
if group := b.account.GetGroup(groupID); group != nil {
if group := account.GetGroup(groupID); group != nil {
for _, candidatePeerID := range group.Peers {
if candidatePeerID != peerID {
if candidatePeer := b.account.GetPeer(candidatePeerID); candidatePeer != nil {
if candidatePeer := account.GetPeer(candidatePeerID); candidatePeer != nil {
r.Peer = candidatePeer.Key
r.PeerID = candidatePeerID
newPeerAssigned = true
@@ -1001,7 +1009,7 @@ func (b *ImprovedNetworkMapBuilder) OnPeerDeleted(peerID string) error {
}
for _, routeID := range routesToDelete {
delete(b.account.Routes, routeID)
delete(account.Routes, routeID)
}
delete(b.cache.peerACLs, peerID)
@@ -1021,7 +1029,7 @@ func (b *ImprovedNetworkMapBuilder) OnPeerDeleted(peerID string) error {
affectedPeers := make(map[string]struct{})
for _, r := range b.account.Routes {
for _, r := range account.Routes {
for _, groupID := range r.Groups {
if peers := b.cache.groupToPeers[groupID]; peers != nil {
for _, p := range peers {
@@ -1043,7 +1051,7 @@ func (b *ImprovedNetworkMapBuilder) OnPeerDeleted(peerID string) error {
if affectedPeerID == peerID {
continue
}
b.buildPeerRoutesView(affectedPeerID)
b.buildPeerRoutesView(account, affectedPeerID)
}
peerDeletionUpdates := b.findPeersAffectedByDeletedPeerACL(peerID, peerIP)
@@ -1058,7 +1066,7 @@ func (b *ImprovedNetworkMapBuilder) OnPeerDeleted(peerID string) error {
return nil
}
func (b *ImprovedNetworkMapBuilder) findPeersAffectedByDeletedPeerACL(
func (b *NetworkMapBuilder) findPeersAffectedByDeletedPeerACL(
deletedPeerID string,
peerIP string,
) map[string]*PeerDeletionUpdate {
@@ -1102,7 +1110,7 @@ type PeerDeletionUpdate struct {
PeerIP string
}
func (b *ImprovedNetworkMapBuilder) applyDeletionUpdates(peerID string, updates *PeerDeletionUpdate) {
func (b *NetworkMapBuilder) applyDeletionUpdates(peerID string, updates *PeerDeletionUpdate) {
if aclView := b.cache.peerACLs[peerID]; aclView != nil {
aclView.ConnectedPeerIDs = slices.DeleteFunc(aclView.ConnectedPeerIDs, func(id string) bool {
return id == updates.RemovePeerID
@@ -1128,7 +1136,7 @@ func (b *ImprovedNetworkMapBuilder) applyDeletionUpdates(peerID string, updates
}
}
func (b *ImprovedNetworkMapBuilder) removeIPFromRouteFirewallRules(routesView *PeerRoutesView, peerIP string) {
func (b *NetworkMapBuilder) removeIPFromRouteFirewallRules(routesView *PeerRoutesView, peerIP string) {
sourceIPv4 := peerIP + "/32"
sourceIPv6 := peerIP + "/128"
@@ -1153,7 +1161,7 @@ func (b *ImprovedNetworkMapBuilder) removeIPFromRouteFirewallRules(routesView *P
}
}
func (b *ImprovedNetworkMapBuilder) cleanupUnusedRules() {
func (b *NetworkMapBuilder) cleanupUnusedRules() {
usedFirewallRules := make(map[string]struct{})
usedRouteRules := make(map[string]struct{})
usedRoutes := make(map[route.ID]struct{})
@@ -1195,3 +1203,13 @@ func (b *ImprovedNetworkMapBuilder) cleanupUnusedRules() {
}
}
}
func (b *NetworkMapBuilder) UpdatePeer(peer *nbpeer.Peer) {
b.cache.mu.Lock()
defer b.cache.mu.Unlock()
peerStored, ok := b.cache.globalPeers[peer.ID]
if !ok {
return
}
*peerStored = *peer
}

View File

@@ -6064,7 +6064,7 @@
"SSHEnabled": false,
"LoginExpirationEnabled": false,
"InactivityExpirationEnabled": false,
"LastLogin": "2025-08-21T15:30:55.588569+02:00",
"LastLogin": "2025-09-03T00:23:03.190135+02:00",
"CreatedAt": "0001-01-01T00:00:00Z",
"Ephemeral": false,
"Location": {

View File

@@ -6064,7 +6064,7 @@
"SSHEnabled": false,
"LoginExpirationEnabled": false,
"InactivityExpirationEnabled": false,
"LastLogin": "2025-08-21T15:30:47.664393+02:00",
"LastLogin": "2025-09-03T00:22:59.223362+02:00",
"CreatedAt": "0001-01-01T00:00:00Z",
"Ephemeral": false,
"Location": {