mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-18 08:16:39 +00:00
[management] add ssh authorized users to network map cache (#5048)
This commit is contained in:
@@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user