[management] add ssh authorized users to network map cache (#5048)

This commit is contained in:
Vlad
2026-01-07 15:53:18 +01:00
committed by GitHub
parent 12a7fa24d7
commit afcdef6121
2 changed files with 289 additions and 386 deletions

View File

@@ -25,15 +25,12 @@ import (
"github.com/netbirdio/netbird/route" "github.com/netbirdio/netbird/route"
) )
// update flag is used to update the golden file.
// example: go test ./... -v -update
// var update = flag.Bool("update", false, "update golden files")
const ( const (
numPeers = 100 numPeers = 100
devGroupID = "group-dev" devGroupID = "group-dev"
opsGroupID = "group-ops" opsGroupID = "group-ops"
allGroupID = "group-all" allGroupID = "group-all"
sshUsersGroupID = "group-ssh-users"
routeID = route.ID("route-main") routeID = route.ID("route-main")
routeHA1ID = route.ID("route-ha-1") routeHA1ID = route.ID("route-ha-1")
routeHA2ID = route.ID("route-ha-2") routeHA2ID = route.ID("route-ha-2")
@@ -41,6 +38,7 @@ const (
policyIDAll = "policy-all" policyIDAll = "policy-all"
policyIDPosture = "policy-posture" policyIDPosture = "policy-posture"
policyIDDrop = "policy-drop" policyIDDrop = "policy-drop"
policyIDSSH = "policy-ssh"
postureCheckID = "posture-check-ver" postureCheckID = "posture-check-ver"
networkResourceID = "res-database" networkResourceID = "res-database"
networkID = "net-database" networkID = "net-database"
@@ -51,6 +49,9 @@ const (
offlinePeerID = "peer-99" // This peer will be completely offline. offlinePeerID = "peer-99" // This peer will be completely offline.
routingPeerID = "peer-95" // This peer is used for routing, it has a route to the network. routingPeerID = "peer-95" // This peer is used for routing, it has a route to the network.
testAccountID = "account-golden-test" testAccountID = "account-golden-test"
userAdminID = "user-admin"
userDevID = "user-dev"
userOpsID = "user-ops"
) )
func TestGetPeerNetworkMap_Golden(t *testing.T) { func TestGetPeerNetworkMap_Golden(t *testing.T) {
@@ -69,61 +70,34 @@ func TestGetPeerNetworkMap_Golden(t *testing.T) {
resourcePolicies := account.GetResourcePoliciesMap() resourcePolicies := account.GetResourcePoliciesMap()
routers := account.GetResourceRoutersMap() routers := account.GetResourceRoutersMap()
networkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers()) legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
normalizeAndSortNetworkMap(legacyNetworkMap)
normalizeAndSortNetworkMap(networkMap) legacyJSON, err := json.MarshalIndent(toNetworkMapJSON(legacyNetworkMap), "", " ")
require.NoError(t, err, "error marshaling legacy network map to JSON")
jsonData, err := json.MarshalIndent(networkMap, "", " ")
require.NoError(t, err, "error marshaling network map to JSON")
goldenFilePath := filepath.Join("testdata", "networkmap_golden.json")
t.Log("Update golden file...")
err = os.MkdirAll(filepath.Dir(goldenFilePath), 0755)
require.NoError(t, err)
err = os.WriteFile(goldenFilePath, jsonData, 0644)
require.NoError(t, err)
expectedJSON, err := os.ReadFile(goldenFilePath)
require.NoError(t, err, "error reading golden file")
require.JSONEq(t, string(expectedJSON), string(jsonData), "resulted network map from OLD method does not match golden file")
}
func TestGetPeerNetworkMap_Golden_New(t *testing.T) {
account := createTestAccountWithEntities()
ctx := context.Background()
validatedPeersMap := make(map[string]struct{})
for i := range numPeers {
peerID := fmt.Sprintf("peer-%d", i)
if peerID == offlinePeerID {
continue
}
validatedPeersMap[peerID] = struct{}{}
}
builder := types.NewNetworkMapBuilder(account, validatedPeersMap) builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
networkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil) newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
normalizeAndSortNetworkMap(newNetworkMap)
newJSON, err := json.MarshalIndent(toNetworkMapJSON(newNetworkMap), "", " ")
require.NoError(t, err, "error marshaling new network map to JSON")
normalizeAndSortNetworkMap(networkMap) if string(legacyJSON) != string(newJSON) {
legacyFilePath := filepath.Join("testdata", "networkmap_golden.json")
newFilePath := filepath.Join("testdata", "networkmap_golden_new.json")
jsonData, err := json.MarshalIndent(networkMap, "", " ") err = os.MkdirAll(filepath.Dir(legacyFilePath), 0755)
require.NoError(t, err, "error marshaling network map to JSON") require.NoError(t, err)
goldenFilePath := filepath.Join("testdata", "networkmap_golden_new.json") err = os.WriteFile(legacyFilePath, legacyJSON, 0644)
require.NoError(t, err)
t.Logf("Saved legacy network map to %s", legacyFilePath)
t.Log("Update golden file...") err = os.WriteFile(newFilePath, newJSON, 0644)
err = os.MkdirAll(filepath.Dir(goldenFilePath), 0755) require.NoError(t, err)
require.NoError(t, err) t.Logf("Saved new network map to %s", newFilePath)
err = os.WriteFile(goldenFilePath, jsonData, 0644)
require.NoError(t, err)
expectedJSON, err := os.ReadFile(goldenFilePath) require.JSONEq(t, string(legacyJSON), string(newJSON), "network maps from legacy and new builder do not match")
require.NoError(t, err, "error reading golden file") }
require.JSONEq(t, string(expectedJSON), string(jsonData), "resulted network map from NEW builder does not match golden file")
} }
func BenchmarkGetPeerNetworkMap(b *testing.B) { func BenchmarkGetPeerNetworkMap(b *testing.B) {
@@ -169,6 +143,8 @@ func TestGetPeerNetworkMap_Golden_WithNewPeer(t *testing.T) {
validatedPeersMap[peerID] = struct{}{} validatedPeersMap[peerID] = struct{}{}
} }
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
newPeerID := "peer-new-101" newPeerID := "peer-new-101"
newPeerIP := net.IP{100, 64, 1, 1} newPeerIP := net.IP{100, 64, 1, 1}
newPeer := &nbpeer.Peer{ newPeer := &nbpeer.Peer{
@@ -201,92 +177,36 @@ func TestGetPeerNetworkMap_Golden_WithNewPeer(t *testing.T) {
resourcePolicies := account.GetResourcePoliciesMap() resourcePolicies := account.GetResourcePoliciesMap()
routers := account.GetResourceRoutersMap() routers := account.GetResourceRoutersMap()
networkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers()) legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
normalizeAndSortNetworkMap(legacyNetworkMap)
legacyJSON, err := json.MarshalIndent(toNetworkMapJSON(legacyNetworkMap), "", " ")
require.NoError(t, err, "error marshaling legacy network map to JSON")
normalizeAndSortNetworkMap(networkMap) err = builder.OnPeerAddedIncremental(account, newPeerID)
jsonData, err := json.MarshalIndent(networkMap, "", " ")
require.NoError(t, err, "error marshaling network map to JSON")
goldenFilePath := filepath.Join("testdata", "networkmap_golden_with_new_peer.json")
t.Log("Update golden file with new peer...")
err = os.MkdirAll(filepath.Dir(goldenFilePath), 0755)
require.NoError(t, err)
err = os.WriteFile(goldenFilePath, jsonData, 0644)
require.NoError(t, err)
expectedJSON, err := os.ReadFile(goldenFilePath)
require.NoError(t, err, "error reading golden file")
require.JSONEq(t, string(expectedJSON), string(jsonData), "network map from OLD method with new peer does not match golden file")
}
func TestGetPeerNetworkMap_Golden_New_WithOnPeerAdded(t *testing.T) {
account := createTestAccountWithEntities()
ctx := context.Background()
validatedPeersMap := make(map[string]struct{})
for i := range numPeers {
peerID := fmt.Sprintf("peer-%d", i)
if peerID == offlinePeerID {
continue
}
validatedPeersMap[peerID] = struct{}{}
}
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
newPeerID := "peer-new-101"
newPeerIP := net.IP{100, 64, 1, 1}
newPeer := &nbpeer.Peer{
ID: newPeerID,
IP: newPeerIP,
Key: fmt.Sprintf("key-%s", newPeerID),
DNSLabel: "peernew101",
Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now()},
UserID: "user-admin",
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.26.0", GoOS: "linux"},
LastLogin: func() *time.Time { t := time.Now(); return &t }(),
}
account.Peers[newPeerID] = newPeer
if devGroup, exists := account.Groups[devGroupID]; exists {
devGroup.Peers = append(devGroup.Peers, newPeerID)
}
if allGroup, exists := account.Groups[allGroupID]; exists {
allGroup.Peers = append(allGroup.Peers, newPeerID)
}
validatedPeersMap[newPeerID] = struct{}{}
if account.Network != nil {
account.Network.Serial++
}
err := builder.OnPeerAddedIncremental(account, newPeerID)
require.NoError(t, err, "error adding peer to cache") require.NoError(t, err, "error adding peer to cache")
networkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil) newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
normalizeAndSortNetworkMap(newNetworkMap)
newJSON, err := json.MarshalIndent(toNetworkMapJSON(newNetworkMap), "", " ")
require.NoError(t, err, "error marshaling new network map to JSON")
normalizeAndSortNetworkMap(networkMap) if string(legacyJSON) != string(newJSON) {
legacyFilePath := filepath.Join("testdata", "networkmap_golden_with_new_peer.json")
newFilePath := filepath.Join("testdata", "networkmap_golden_new_with_onpeeradded.json")
jsonData, err := json.MarshalIndent(networkMap, "", " ") err = os.MkdirAll(filepath.Dir(legacyFilePath), 0755)
require.NoError(t, err, "error marshaling network map to JSON") require.NoError(t, err)
goldenFilePath := filepath.Join("testdata", "networkmap_golden_new_with_onpeeradded.json") err = os.WriteFile(legacyFilePath, legacyJSON, 0644)
t.Log("Update golden file with OnPeerAdded...") require.NoError(t, err)
err = os.MkdirAll(filepath.Dir(goldenFilePath), 0755) t.Logf("Saved legacy network map to %s", legacyFilePath)
require.NoError(t, err)
err = os.WriteFile(goldenFilePath, jsonData, 0644)
require.NoError(t, err)
expectedJSON, err := os.ReadFile(goldenFilePath) err = os.WriteFile(newFilePath, newJSON, 0644)
require.NoError(t, err, "error reading golden file") require.NoError(t, err)
t.Logf("Saved new network map to %s", newFilePath)
require.JSONEq(t, string(expectedJSON), string(jsonData), "network map from NEW builder with OnPeerAdded does not match golden file") require.JSONEq(t, string(legacyJSON), string(newJSON), "network maps with new peer from legacy and new builder do not match")
}
} }
func BenchmarkGetPeerNetworkMap_AfterPeerAdded(b *testing.B) { func BenchmarkGetPeerNetworkMap_AfterPeerAdded(b *testing.B) {
@@ -349,6 +269,8 @@ func TestGetPeerNetworkMap_Golden_WithNewRoutingPeer(t *testing.T) {
validatedPeersMap[peerID] = struct{}{} validatedPeersMap[peerID] = struct{}{}
} }
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
newRouterID := "peer-new-router-102" newRouterID := "peer-new-router-102"
newRouterIP := net.IP{100, 64, 1, 2} newRouterIP := net.IP{100, 64, 1, 2}
newRouter := &nbpeer.Peer{ newRouter := &nbpeer.Peer{
@@ -395,106 +317,36 @@ func TestGetPeerNetworkMap_Golden_WithNewRoutingPeer(t *testing.T) {
resourcePolicies := account.GetResourcePoliciesMap() resourcePolicies := account.GetResourcePoliciesMap()
routers := account.GetResourceRoutersMap() routers := account.GetResourceRoutersMap()
networkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers()) legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
normalizeAndSortNetworkMap(legacyNetworkMap)
legacyJSON, err := json.MarshalIndent(toNetworkMapJSON(legacyNetworkMap), "", " ")
require.NoError(t, err, "error marshaling legacy network map to JSON")
normalizeAndSortNetworkMap(networkMap) err = builder.OnPeerAddedIncremental(account, newRouterID)
jsonData, err := json.MarshalIndent(networkMap, "", " ")
require.NoError(t, err, "error marshaling network map to JSON")
goldenFilePath := filepath.Join("testdata", "networkmap_golden_with_new_router.json")
t.Log("Update golden file with new router...")
err = os.MkdirAll(filepath.Dir(goldenFilePath), 0755)
require.NoError(t, err)
err = os.WriteFile(goldenFilePath, jsonData, 0644)
require.NoError(t, err)
expectedJSON, err := os.ReadFile(goldenFilePath)
require.NoError(t, err, "error reading golden file")
require.JSONEq(t, string(expectedJSON), string(jsonData), "network map from OLD method with new router does not match golden file")
}
func TestGetPeerNetworkMap_Golden_New_WithOnPeerAddedRouter(t *testing.T) {
account := createTestAccountWithEntities()
ctx := context.Background()
validatedPeersMap := make(map[string]struct{})
for i := range numPeers {
peerID := fmt.Sprintf("peer-%d", i)
if peerID == offlinePeerID {
continue
}
validatedPeersMap[peerID] = struct{}{}
}
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
newRouterID := "peer-new-router-102"
newRouterIP := net.IP{100, 64, 1, 2}
newRouter := &nbpeer.Peer{
ID: newRouterID,
IP: newRouterIP,
Key: fmt.Sprintf("key-%s", newRouterID),
DNSLabel: "newrouter102",
Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now()},
UserID: "user-admin",
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.26.0", GoOS: "linux"},
LastLogin: func() *time.Time { t := time.Now(); return &t }(),
}
account.Peers[newRouterID] = newRouter
if opsGroup, exists := account.Groups[opsGroupID]; exists {
opsGroup.Peers = append(opsGroup.Peers, newRouterID)
}
if allGroup, exists := account.Groups[allGroupID]; exists {
allGroup.Peers = append(allGroup.Peers, newRouterID)
}
newRoute := &route.Route{
ID: route.ID("route-new-router"),
Network: netip.MustParsePrefix("172.16.0.0/24"),
Peer: newRouter.Key,
PeerID: newRouterID,
Description: "Route from new router",
Enabled: true,
PeerGroups: []string{opsGroupID},
Groups: []string{devGroupID, opsGroupID},
AccessControlGroups: []string{devGroupID},
AccountID: account.Id,
}
account.Routes[newRoute.ID] = newRoute
validatedPeersMap[newRouterID] = struct{}{}
if account.Network != nil {
account.Network.Serial++
}
err := builder.OnPeerAddedIncremental(account, newRouterID)
require.NoError(t, err, "error adding router to cache") require.NoError(t, err, "error adding router to cache")
networkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil) newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
normalizeAndSortNetworkMap(newNetworkMap)
newJSON, err := json.MarshalIndent(toNetworkMapJSON(newNetworkMap), "", " ")
require.NoError(t, err, "error marshaling new network map to JSON")
normalizeAndSortNetworkMap(networkMap) if string(legacyJSON) != string(newJSON) {
legacyFilePath := filepath.Join("testdata", "networkmap_golden_with_new_router.json")
newFilePath := filepath.Join("testdata", "networkmap_golden_new_with_onpeeradded_router.json")
jsonData, err := json.MarshalIndent(networkMap, "", " ") err = os.MkdirAll(filepath.Dir(legacyFilePath), 0755)
require.NoError(t, err, "error marshaling network map to JSON") require.NoError(t, err)
goldenFilePath := filepath.Join("testdata", "networkmap_golden_new_with_onpeeradded_router.json") err = os.WriteFile(legacyFilePath, legacyJSON, 0644)
require.NoError(t, err)
t.Logf("Saved legacy network map to %s", legacyFilePath)
t.Log("Update golden file with OnPeerAdded router...") err = os.WriteFile(newFilePath, newJSON, 0644)
err = os.MkdirAll(filepath.Dir(goldenFilePath), 0755) require.NoError(t, err)
require.NoError(t, err) t.Logf("Saved new network map to %s", newFilePath)
err = os.WriteFile(goldenFilePath, jsonData, 0644)
require.NoError(t, err)
expectedJSON, err := os.ReadFile(goldenFilePath) require.JSONEq(t, string(legacyJSON), string(newJSON), "network maps with new router from legacy and new builder do not match")
require.NoError(t, err, "error reading golden file") }
require.JSONEq(t, string(expectedJSON), string(jsonData), "network map from NEW builder with OnPeerAdded router does not match golden file")
} }
func BenchmarkGetPeerNetworkMap_AfterRouterPeerAdded(b *testing.B) { func BenchmarkGetPeerNetworkMap_AfterRouterPeerAdded(b *testing.B) {
@@ -579,7 +431,9 @@ func TestGetPeerNetworkMap_Golden_WithDeletedPeer(t *testing.T) {
validatedPeersMap[peerID] = struct{}{} validatedPeersMap[peerID] = struct{}{}
} }
deletedPeerID := "peer-25" // peer from devs group builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
deletedPeerID := "peer-25"
delete(account.Peers, deletedPeerID) delete(account.Peers, deletedPeerID)
@@ -604,85 +458,36 @@ func TestGetPeerNetworkMap_Golden_WithDeletedPeer(t *testing.T) {
resourcePolicies := account.GetResourcePoliciesMap() resourcePolicies := account.GetResourcePoliciesMap()
routers := account.GetResourceRoutersMap() routers := account.GetResourceRoutersMap()
networkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers()) legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
normalizeAndSortNetworkMap(legacyNetworkMap)
legacyJSON, err := json.MarshalIndent(toNetworkMapJSON(legacyNetworkMap), "", " ")
require.NoError(t, err, "error marshaling legacy network map to JSON")
normalizeAndSortNetworkMap(networkMap) err = builder.OnPeerDeleted(account, deletedPeerID)
jsonData, err := json.MarshalIndent(networkMap, "", " ")
require.NoError(t, err, "error marshaling network map to JSON")
goldenFilePath := filepath.Join("testdata", "networkmap_golden_with_deleted_peer.json")
t.Log("Update golden file with deleted peer...")
err = os.MkdirAll(filepath.Dir(goldenFilePath), 0755)
require.NoError(t, err)
err = os.WriteFile(goldenFilePath, jsonData, 0644)
require.NoError(t, err)
expectedJSON, err := os.ReadFile(goldenFilePath)
require.NoError(t, err, "error reading golden file")
require.JSONEq(t, string(expectedJSON), string(jsonData), "network map from OLD method with deleted peer does not match golden file")
}
func TestGetPeerNetworkMap_Golden_New_WithOnPeerDeleted(t *testing.T) {
account := createTestAccountWithEntities()
ctx := context.Background()
validatedPeersMap := make(map[string]struct{})
for i := range numPeers {
peerID := fmt.Sprintf("peer-%d", i)
if peerID == offlinePeerID {
continue
}
validatedPeersMap[peerID] = struct{}{}
}
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
deletedPeerID := "peer-25" // devs group peer
delete(account.Peers, deletedPeerID)
if devGroup, exists := account.Groups[devGroupID]; exists {
devGroup.Peers = slices.DeleteFunc(devGroup.Peers, func(id string) bool {
return id == deletedPeerID
})
}
if allGroup, exists := account.Groups[allGroupID]; exists {
allGroup.Peers = slices.DeleteFunc(allGroup.Peers, func(id string) bool {
return id == deletedPeerID
})
}
delete(validatedPeersMap, deletedPeerID)
if account.Network != nil {
account.Network.Serial++
}
err := builder.OnPeerDeleted(account, deletedPeerID)
require.NoError(t, err, "error deleting peer from cache") require.NoError(t, err, "error deleting peer from cache")
networkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil) newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
normalizeAndSortNetworkMap(newNetworkMap)
newJSON, err := json.MarshalIndent(toNetworkMapJSON(newNetworkMap), "", " ")
require.NoError(t, err, "error marshaling new network map to JSON")
normalizeAndSortNetworkMap(networkMap) if string(legacyJSON) != string(newJSON) {
legacyFilePath := filepath.Join("testdata", "networkmap_golden_with_deleted_peer.json")
newFilePath := filepath.Join("testdata", "networkmap_golden_new_with_onpeerdeleted.json")
jsonData, err := json.MarshalIndent(networkMap, "", " ") err = os.MkdirAll(filepath.Dir(legacyFilePath), 0755)
require.NoError(t, err, "error marshaling network map to JSON") require.NoError(t, err)
goldenFilePath := filepath.Join("testdata", "networkmap_golden_new_with_onpeerdeleted.json") err = os.WriteFile(legacyFilePath, legacyJSON, 0644)
t.Log("Update golden file with OnPeerDeleted...") require.NoError(t, err)
err = os.MkdirAll(filepath.Dir(goldenFilePath), 0755) t.Logf("Saved legacy network map to %s", legacyFilePath)
require.NoError(t, err)
err = os.WriteFile(goldenFilePath, jsonData, 0644)
require.NoError(t, err)
expectedJSON, err := os.ReadFile(goldenFilePath) err = os.WriteFile(newFilePath, newJSON, 0644)
require.NoError(t, err, "error reading golden file") require.NoError(t, err)
t.Logf("Saved new network map to %s", newFilePath)
require.JSONEq(t, string(expectedJSON), string(jsonData), "network map from NEW builder with OnPeerDeleted does not match golden file") require.JSONEq(t, string(legacyJSON), string(newJSON), "network maps with deleted peer from legacy and new builder do not match")
}
} }
func TestGetPeerNetworkMap_Golden_WithDeletedRouterPeer(t *testing.T) { func TestGetPeerNetworkMap_Golden_WithDeletedRouterPeer(t *testing.T) {
@@ -698,7 +503,9 @@ func TestGetPeerNetworkMap_Golden_WithDeletedRouterPeer(t *testing.T) {
validatedPeersMap[peerID] = struct{}{} validatedPeersMap[peerID] = struct{}{}
} }
deletedRouterID := "peer-75" // router peer builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
deletedRouterID := "peer-75"
var affectedRoute *route.Route var affectedRoute *route.Route
for _, r := range account.Routes { for _, r := range account.Routes {
@@ -730,93 +537,36 @@ func TestGetPeerNetworkMap_Golden_WithDeletedRouterPeer(t *testing.T) {
resourcePolicies := account.GetResourcePoliciesMap() resourcePolicies := account.GetResourcePoliciesMap()
routers := account.GetResourceRoutersMap() routers := account.GetResourceRoutersMap()
networkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers()) legacyNetworkMap := account.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, resourcePolicies, routers, nil, account.GetActiveGroupUsers())
normalizeAndSortNetworkMap(legacyNetworkMap)
legacyJSON, err := json.MarshalIndent(toNetworkMapJSON(legacyNetworkMap), "", " ")
require.NoError(t, err, "error marshaling legacy network map to JSON")
normalizeAndSortNetworkMap(networkMap) err = builder.OnPeerDeleted(account, deletedRouterID)
jsonData, err := json.MarshalIndent(networkMap, "", " ")
require.NoError(t, err, "error marshaling network map to JSON")
goldenFilePath := filepath.Join("testdata", "networkmap_golden_with_deleted_router_peer.json")
t.Log("Update golden file with deleted peer...")
err = os.MkdirAll(filepath.Dir(goldenFilePath), 0755)
require.NoError(t, err)
err = os.WriteFile(goldenFilePath, jsonData, 0644)
require.NoError(t, err)
expectedJSON, err := os.ReadFile(goldenFilePath)
require.NoError(t, err, "error reading golden file")
require.JSONEq(t, string(expectedJSON), string(jsonData), "network map from OLD method with deleted peer does not match golden file")
}
func TestGetPeerNetworkMap_Golden_New_WithDeletedRouterPeer(t *testing.T) {
account := createTestAccountWithEntities()
ctx := context.Background()
validatedPeersMap := make(map[string]struct{})
for i := range numPeers {
peerID := fmt.Sprintf("peer-%d", i)
if peerID == offlinePeerID {
continue
}
validatedPeersMap[peerID] = struct{}{}
}
builder := types.NewNetworkMapBuilder(account, validatedPeersMap)
deletedRouterID := "peer-75" // router peer
var affectedRoute *route.Route
for _, r := range account.Routes {
if r.PeerID == deletedRouterID {
affectedRoute = r
break
}
}
require.NotNil(t, affectedRoute, "Router peer should have a route")
for _, group := range account.Groups {
group.Peers = slices.DeleteFunc(group.Peers, func(id string) bool {
return id == deletedRouterID
})
}
for routeID, r := range account.Routes {
if r.Peer == account.Peers[deletedRouterID].Key || r.PeerID == deletedRouterID {
delete(account.Routes, routeID)
}
}
delete(account.Peers, deletedRouterID)
delete(validatedPeersMap, deletedRouterID)
if account.Network != nil {
account.Network.Serial++
}
err := builder.OnPeerDeleted(account, deletedRouterID)
require.NoError(t, err, "error deleting routing peer from cache") require.NoError(t, err, "error deleting routing peer from cache")
networkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil) newNetworkMap := builder.GetPeerNetworkMap(ctx, testingPeerID, dns.CustomZone{}, validatedPeersMap, nil)
normalizeAndSortNetworkMap(newNetworkMap)
newJSON, err := json.MarshalIndent(toNetworkMapJSON(newNetworkMap), "", " ")
require.NoError(t, err, "error marshaling new network map to JSON")
normalizeAndSortNetworkMap(networkMap) if string(legacyJSON) != string(newJSON) {
legacyFilePath := filepath.Join("testdata", "networkmap_golden_with_deleted_router_peer.json")
newFilePath := filepath.Join("testdata", "networkmap_golden_new_with_deleted_router.json")
jsonData, err := json.MarshalIndent(networkMap, "", " ") err = os.MkdirAll(filepath.Dir(legacyFilePath), 0755)
require.NoError(t, err) require.NoError(t, err)
goldenFilePath := filepath.Join("testdata", "networkmap_golden_new_with_deleted_router.json") err = os.WriteFile(legacyFilePath, legacyJSON, 0644)
require.NoError(t, err)
t.Logf("Saved legacy network map to %s", legacyFilePath)
t.Log("Update golden file with deleted router...") err = os.WriteFile(newFilePath, newJSON, 0644)
err = os.MkdirAll(filepath.Dir(goldenFilePath), 0755) require.NoError(t, err)
require.NoError(t, err) t.Logf("Saved new network map to %s", newFilePath)
err = os.WriteFile(goldenFilePath, jsonData, 0644)
require.NoError(t, err)
expectedJSON, err := os.ReadFile(goldenFilePath) require.JSONEq(t, string(legacyJSON), string(newJSON), "network maps with deleted router from legacy and new builder do not match")
require.NoError(t, err) }
require.JSONEq(t, string(expectedJSON), string(jsonData),
"network map after deleting router does not match golden file")
} }
func BenchmarkGetPeerNetworkMap_AfterPeerDeleted(b *testing.B) { func BenchmarkGetPeerNetworkMap_AfterPeerDeleted(b *testing.B) {
@@ -924,6 +674,54 @@ func normalizeAndSortNetworkMap(networkMap *types.NetworkMap) {
} }
} }
type networkMapJSON struct {
Peers []*nbpeer.Peer `json:"Peers"`
Network *types.Network `json:"Network"`
Routes []*route.Route `json:"Routes"`
DNSConfig dns.Config `json:"DNSConfig"`
OfflinePeers []*nbpeer.Peer `json:"OfflinePeers"`
FirewallRules []*types.FirewallRule `json:"FirewallRules"`
RoutesFirewallRules []*types.RouteFirewallRule `json:"RoutesFirewallRules"`
ForwardingRules []*types.ForwardingRule `json:"ForwardingRules"`
AuthorizedUsers map[string][]string `json:"AuthorizedUsers,omitempty"`
EnableSSH bool `json:"EnableSSH"`
}
func toNetworkMapJSON(nm *types.NetworkMap) *networkMapJSON {
result := &networkMapJSON{
Peers: nm.Peers,
Network: nm.Network,
Routes: nm.Routes,
DNSConfig: nm.DNSConfig,
OfflinePeers: nm.OfflinePeers,
FirewallRules: nm.FirewallRules,
RoutesFirewallRules: nm.RoutesFirewallRules,
ForwardingRules: nm.ForwardingRules,
EnableSSH: nm.EnableSSH,
}
if len(nm.AuthorizedUsers) > 0 {
result.AuthorizedUsers = make(map[string][]string)
localUsers := make([]string, 0, len(nm.AuthorizedUsers))
for localUser := range nm.AuthorizedUsers {
localUsers = append(localUsers, localUser)
}
sort.Strings(localUsers)
for _, localUser := range localUsers {
userIDs := nm.AuthorizedUsers[localUser]
sortedUserIDs := make([]string, 0, len(userIDs))
for userID := range userIDs {
sortedUserIDs = append(sortedUserIDs, userID)
}
sort.Strings(sortedUserIDs)
result.AuthorizedUsers[localUser] = sortedUserIDs
}
}
return result
}
func createTestAccountWithEntities() *types.Account { func createTestAccountWithEntities() *types.Account {
peers := make(map[string]*nbpeer.Peer) peers := make(map[string]*nbpeer.Peer)
devGroupPeers, opsGroupPeers, allGroupPeers := []string{}, []string{}, []string{} devGroupPeers, opsGroupPeers, allGroupPeers := []string{}, []string{}, []string{}
@@ -959,9 +757,10 @@ func createTestAccountWithEntities() *types.Account {
} }
groups := map[string]*types.Group{ groups := map[string]*types.Group{
allGroupID: {ID: allGroupID, Name: "All", Peers: allGroupPeers}, allGroupID: {ID: allGroupID, Name: "All", Peers: allGroupPeers},
devGroupID: {ID: devGroupID, Name: "Developers", Peers: devGroupPeers}, devGroupID: {ID: devGroupID, Name: "Developers", Peers: devGroupPeers},
opsGroupID: {ID: opsGroupID, Name: "Operations", Peers: opsGroupPeers}, opsGroupID: {ID: opsGroupID, Name: "Operations", Peers: opsGroupPeers},
sshUsersGroupID: {ID: sshUsersGroupID, Name: "SSH Users", Peers: []string{}},
} }
policies := []*types.Policy{ policies := []*types.Policy{
@@ -999,6 +798,15 @@ func createTestAccountWithEntities() *types.Account {
Sources: []string{opsGroupID}, DestinationResource: types.Resource{ID: networkResourceID}, Sources: []string{opsGroupID}, DestinationResource: types.Resource{ID: networkResourceID},
}}, }},
}, },
{
ID: policyIDSSH, Name: "SSH Access Policy", Enabled: true,
Rules: []*types.PolicyRule{{
ID: policyIDSSH, Name: "Allow SSH to Ops", Enabled: true, Action: types.PolicyTrafficActionAccept,
Protocol: types.PolicyRuleProtocolNetbirdSSH, Bidirectional: false,
Sources: []string{devGroupID}, Destinations: []string{opsGroupID},
AuthorizedGroups: map[string][]string{sshUsersGroupID: {"root", "admin"}},
}},
},
} }
routes := map[route.ID]*route.Route{ routes := map[route.ID]*route.Route{
@@ -1031,8 +839,15 @@ func createTestAccountWithEntities() *types.Account {
}, },
} }
users := map[string]*types.User{
userAdminID: {Id: userAdminID, Role: types.UserRoleAdmin, IsServiceUser: false, AccountID: testAccountID, AutoGroups: []string{allGroupID}},
userDevID: {Id: userDevID, Role: types.UserRoleUser, IsServiceUser: false, AccountID: testAccountID, AutoGroups: []string{sshUsersGroupID, devGroupID}},
userOpsID: {Id: userOpsID, Role: types.UserRoleUser, IsServiceUser: false, AccountID: testAccountID, AutoGroups: []string{sshUsersGroupID, opsGroupID}},
}
account := &types.Account{ account := &types.Account{
Id: testAccountID, Peers: peers, Groups: groups, Policies: policies, Routes: routes, Id: testAccountID, Peers: peers, Groups: groups, Policies: policies, Routes: routes,
Users: users,
Network: &types.Network{ Network: &types.Network{
Identifier: "net-golden-test", Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(16, 32)}, Serial: 1, Identifier: "net-golden-test", Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(16, 32)}, Serial: 1,
}, },

View File

@@ -12,6 +12,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
"github.com/netbirdio/netbird/client/ssh/auth"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
@@ -47,6 +48,10 @@ type NetworkMapCache struct {
peerACLs map[string]*PeerACLView peerACLs map[string]*PeerACLView
peerRoutes map[string]*PeerRoutesView peerRoutes map[string]*PeerRoutesView
peerDNS map[string]*nbdns.Config peerDNS map[string]*nbdns.Config
peerSSH map[string]*PeerSSHView
groupIDToUserIDs map[string][]string
allowedUserIDs map[string]struct{}
resourceRouters map[string]map[string]*routerTypes.NetworkRouter resourceRouters map[string]map[string]*routerTypes.NetworkRouter
resourcePolicies map[string][]*Policy resourcePolicies map[string][]*Policy
@@ -76,6 +81,11 @@ type PeerRoutesView struct {
RouteFirewallRuleIDs []string RouteFirewallRuleIDs []string
} }
type PeerSSHView struct {
EnableSSH bool
AuthorizedUsers map[string]map[string]struct{}
}
type NetworkMapBuilder struct { type NetworkMapBuilder struct {
account *Account account *Account
cache *NetworkMapCache cache *NetworkMapCache
@@ -108,6 +118,9 @@ func NewNetworkMapBuilder(account *Account, validatedPeers map[string]struct{})
peerACLs: make(map[string]*PeerACLView), peerACLs: make(map[string]*PeerACLView),
peerRoutes: make(map[string]*PeerRoutesView), peerRoutes: make(map[string]*PeerRoutesView),
peerDNS: make(map[string]*nbdns.Config), peerDNS: make(map[string]*nbdns.Config),
peerSSH: make(map[string]*PeerSSHView),
groupIDToUserIDs: make(map[string][]string),
allowedUserIDs: make(map[string]struct{}),
globalResources: make(map[string]*resourceTypes.NetworkResource), globalResources: make(map[string]*resourceTypes.NetworkResource),
acgToRoutes: make(map[string]map[route.ID]*RouteOwnerInfo), acgToRoutes: make(map[string]map[route.ID]*RouteOwnerInfo),
noACGRoutes: make(map[route.ID]*RouteOwnerInfo), noACGRoutes: make(map[route.ID]*RouteOwnerInfo),
@@ -165,9 +178,15 @@ func (b *NetworkMapBuilder) buildGlobalIndexes(account *Account) {
clear(b.cache.peerToRoutes) clear(b.cache.peerToRoutes)
clear(b.cache.acgToRoutes) clear(b.cache.acgToRoutes)
clear(b.cache.noACGRoutes) clear(b.cache.noACGRoutes)
clear(b.cache.groupIDToUserIDs)
clear(b.cache.allowedUserIDs)
clear(b.cache.peerSSH)
maps.Copy(b.cache.globalPeers, account.Peers) maps.Copy(b.cache.globalPeers, account.Peers)
b.cache.groupIDToUserIDs = account.GetActiveGroupUsers()
b.cache.allowedUserIDs = b.buildAllowedUserIDs(account)
for groupID, group := range account.Groups { for groupID, group := range account.Groups {
peersCopy := make([]string, len(group.Peers)) peersCopy := make([]string, len(group.Peers))
copy(peersCopy, group.Peers) copy(peersCopy, group.Peers)
@@ -242,7 +261,7 @@ func (b *NetworkMapBuilder) buildPeerACLView(account *Account, peerID string) {
return return
} }
allPotentialPeers, firewallRules := b.getPeerConnectionResources(account, peer, b.validatedPeers) allPotentialPeers, firewallRules, authorizedUsers, sshEnabled := b.getPeerConnectionResources(account, peer, b.validatedPeers)
isRouter, networkResourcesRoutes, sourcePeers := b.getNetworkResourcesForPeer(account, peer) isRouter, networkResourcesRoutes, sourcePeers := b.getNetworkResourcesForPeer(account, peer)
@@ -272,11 +291,15 @@ func (b *NetworkMapBuilder) buildPeerACLView(account *Account, peerID string) {
} }
b.cache.peerACLs[peerID] = view b.cache.peerACLs[peerID] = view
b.cache.peerSSH[peerID] = &PeerSSHView{
EnableSSH: sshEnabled,
AuthorizedUsers: authorizedUsers,
}
} }
func (b *NetworkMapBuilder) getPeerConnectionResources(account *Account, peer *nbpeer.Peer, func (b *NetworkMapBuilder) getPeerConnectionResources(account *Account, peer *nbpeer.Peer,
validatedPeersMap map[string]struct{}, validatedPeersMap map[string]struct{},
) ([]*nbpeer.Peer, []*FirewallRule) { ) ([]*nbpeer.Peer, []*FirewallRule, map[string]map[string]struct{}, bool) {
peerID := peer.ID peerID := peer.ID
ctx := context.Background() ctx := context.Background()
@@ -291,6 +314,9 @@ func (b *NetworkMapBuilder) getPeerConnectionResources(account *Account, peer *n
fwRules := make([]*FirewallRule, 0) fwRules := make([]*FirewallRule, 0)
peers := make([]*nbpeer.Peer, 0) peers := make([]*nbpeer.Peer, 0)
authorizedUsers := make(map[string]map[string]struct{})
sshEnabled := false
for _, group := range peerGroups { for _, group := range peerGroups {
policies := b.cache.groupToPolicies[group] policies := b.cache.groupToPolicies[group]
for _, policy := range policies { for _, policy := range policies {
@@ -363,12 +389,48 @@ func (b *NetworkMapBuilder) getPeerConnectionResources(account *Account, peer *n
rule, sourcePeers, FirewallRuleDirectionIN, rule, sourcePeers, FirewallRuleDirectionIN,
peer, &peers, &fwRules, peersExists, rulesExists, peer, &peers, &fwRules, peersExists, rulesExists,
) )
if rule.Protocol == PolicyRuleProtocolNetbirdSSH {
sshEnabled = true
switch {
case len(rule.AuthorizedGroups) > 0:
for groupID, localUsers := range rule.AuthorizedGroups {
userIDs, ok := b.cache.groupIDToUserIDs[groupID]
if !ok {
continue
}
if len(localUsers) == 0 {
localUsers = []string{auth.Wildcard}
}
for _, localUser := range localUsers {
if authorizedUsers[localUser] == nil {
authorizedUsers[localUser] = make(map[string]struct{})
}
for _, userID := range userIDs {
authorizedUsers[localUser][userID] = struct{}{}
}
}
}
case rule.AuthorizedUser != "":
if authorizedUsers[auth.Wildcard] == nil {
authorizedUsers[auth.Wildcard] = make(map[string]struct{})
}
authorizedUsers[auth.Wildcard][rule.AuthorizedUser] = struct{}{}
default:
authorizedUsers[auth.Wildcard] = maps.Clone(b.cache.allowedUserIDs)
}
} else if policyRuleImpliesLegacySSH(rule) && peer.SSHEnabled {
sshEnabled = true
authorizedUsers[auth.Wildcard] = maps.Clone(b.cache.allowedUserIDs)
}
} }
} }
} }
} }
return peers, fwRules return peers, fwRules, authorizedUsers, sshEnabled
} }
func (b *NetworkMapBuilder) isPeerInGroupscached(groupIDs []string, peerGroupsMap map[string]struct{}) bool { func (b *NetworkMapBuilder) isPeerInGroupscached(groupIDs []string, peerGroupsMap map[string]struct{}) bool {
@@ -438,7 +500,7 @@ func (b *NetworkMapBuilder) generateResourcescached(
PeerIP: peer.IP.String(), PeerIP: peer.IP.String(),
Direction: direction, Direction: direction,
Action: string(rule.Action), Action: string(rule.Action),
Protocol: string(rule.Protocol), Protocol: firewallRuleProtocol(rule.Protocol),
} }
var s strings.Builder var s strings.Builder
@@ -945,6 +1007,23 @@ func (b *NetworkMapBuilder) getPeerNSGroups(account *Account, peerID string, che
return peerNSGroups return peerNSGroups
} }
func (b *NetworkMapBuilder) buildAllowedUserIDs(account *Account) map[string]struct{} {
users := make(map[string]struct{})
for _, nbUser := range account.Users {
if !nbUser.IsBlocked() && !nbUser.IsServiceUser {
users[nbUser.Id] = struct{}{}
}
}
return users
}
func firewallRuleProtocol(protocol PolicyRuleProtocolType) string {
if protocol == PolicyRuleProtocolNetbirdSSH {
return string(PolicyRuleProtocolTCP)
}
return string(protocol)
}
// lock should be held // lock should be held
func (b *NetworkMapBuilder) updateAccountLocked(account *Account) *Account { func (b *NetworkMapBuilder) updateAccountLocked(account *Account) *Account {
if account.Network.CurrentSerial() > b.account.Network.CurrentSerial() { if account.Network.CurrentSerial() > b.account.Network.CurrentSerial() {
@@ -972,12 +1051,13 @@ func (b *NetworkMapBuilder) GetPeerNetworkMap(
aclView := b.cache.peerACLs[peerID] aclView := b.cache.peerACLs[peerID]
routesView := b.cache.peerRoutes[peerID] routesView := b.cache.peerRoutes[peerID]
dnsConfig := b.cache.peerDNS[peerID] dnsConfig := b.cache.peerDNS[peerID]
sshView := b.cache.peerSSH[peerID]
if aclView == nil || routesView == nil || dnsConfig == nil { if aclView == nil || routesView == nil || dnsConfig == nil {
return &NetworkMap{Network: account.Network.Copy()} return &NetworkMap{Network: account.Network.Copy()}
} }
nm := b.assembleNetworkMap(account, peer, aclView, routesView, dnsConfig, peersCustomZone, validatedPeers) nm := b.assembleNetworkMap(account, peer, aclView, routesView, dnsConfig, sshView, peersCustomZone, validatedPeers)
if metrics != nil { if metrics != nil {
objectCount := int64(len(nm.Peers) + len(nm.OfflinePeers) + len(nm.Routes) + len(nm.FirewallRules) + len(nm.RoutesFirewallRules)) objectCount := int64(len(nm.Peers) + len(nm.OfflinePeers) + len(nm.Routes) + len(nm.FirewallRules) + len(nm.RoutesFirewallRules))
@@ -995,7 +1075,7 @@ func (b *NetworkMapBuilder) GetPeerNetworkMap(
func (b *NetworkMapBuilder) assembleNetworkMap( func (b *NetworkMapBuilder) assembleNetworkMap(
account *Account, peer *nbpeer.Peer, aclView *PeerACLView, routesView *PeerRoutesView, account *Account, peer *nbpeer.Peer, aclView *PeerACLView, routesView *PeerRoutesView,
dnsConfig *nbdns.Config, customZone nbdns.CustomZone, validatedPeers map[string]struct{}, dnsConfig *nbdns.Config, sshView *PeerSSHView, customZone nbdns.CustomZone, validatedPeers map[string]struct{},
) *NetworkMap { ) *NetworkMap {
var peersToConnect []*nbpeer.Peer var peersToConnect []*nbpeer.Peer
@@ -1055,7 +1135,7 @@ func (b *NetworkMapBuilder) assembleNetworkMap(
finalDNSConfig.CustomZones = zones finalDNSConfig.CustomZones = zones
} }
return &NetworkMap{ nm := &NetworkMap{
Peers: peersToConnect, Peers: peersToConnect,
Network: account.Network.Copy(), Network: account.Network.Copy(),
Routes: routes, Routes: routes,
@@ -1064,6 +1144,13 @@ func (b *NetworkMapBuilder) assembleNetworkMap(
FirewallRules: firewallRules, FirewallRules: firewallRules,
RoutesFirewallRules: routesFirewallRules, RoutesFirewallRules: routesFirewallRules,
} }
if sshView != nil {
nm.EnableSSH = sshView.EnableSSH
nm.AuthorizedUsers = sshView.AuthorizedUsers
}
return nm
} }
func (b *NetworkMapBuilder) generateFirewallRuleID(rule *FirewallRule) string { func (b *NetworkMapBuilder) generateFirewallRuleID(rule *FirewallRule) string {
@@ -1772,7 +1859,7 @@ func (b *NetworkMapBuilder) addUpdateForPeersInGroups(
PeerIP: newPeer.IP.String(), PeerIP: newPeer.IP.String(),
Direction: direction, Direction: direction,
Action: string(rule.Action), Action: string(rule.Action),
Protocol: string(rule.Protocol), Protocol: firewallRuleProtocol(rule.Protocol),
} }
for _, peerID := range peers { for _, peerID := range peers {
if peerID == newPeerID { if peerID == newPeerID {
@@ -1823,7 +1910,7 @@ func (b *NetworkMapBuilder) addUpdateForDirectPeerResource(
PeerIP: newPeer.IP.String(), PeerIP: newPeer.IP.String(),
Direction: direction, Direction: direction,
Action: string(rule.Action), Action: string(rule.Action),
Protocol: string(rule.Protocol), Protocol: firewallRuleProtocol(rule.Protocol),
} }
b.addOrUpdateFirewallRuleInDelta(updates, targetPeerID, newPeerID, rule, direction, fr, fr.PeerIP, targetPeer) b.addOrUpdateFirewallRuleInDelta(updates, targetPeerID, newPeerID, rule, direction, fr, fr.PeerIP, targetPeer)
@@ -1989,6 +2076,7 @@ func (b *NetworkMapBuilder) OnPeerDeleted(acc *Account, peerID string) error {
delete(b.cache.peerACLs, peerID) delete(b.cache.peerACLs, peerID)
delete(b.cache.peerRoutes, peerID) delete(b.cache.peerRoutes, peerID)
delete(b.cache.peerDNS, peerID) delete(b.cache.peerDNS, peerID)
delete(b.cache.peerSSH, peerID)
delete(b.cache.globalPeers, peerID) delete(b.cache.globalPeers, peerID)