component types

This commit is contained in:
pascal
2026-07-22 23:37:13 +02:00
parent 96963b6751
commit eebafde784
35 changed files with 609 additions and 426 deletions
@@ -5,9 +5,6 @@ import (
"strconv" "strconv"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/types" "github.com/netbirdio/netbird/management/server/types"
nbroute "github.com/netbirdio/netbird/route" nbroute "github.com/netbirdio/netbird/route"
"github.com/netbirdio/netbird/shared/management/networkmap" "github.com/netbirdio/netbird/shared/management/networkmap"
@@ -166,7 +163,7 @@ func (e *componentEncoder) indexAllPeers() {
} }
} }
func (e *componentEncoder) appendPeer(p *nbpeer.Peer) uint32 { func (e *componentEncoder) appendPeer(p *types.ComponentPeer) uint32 {
if idx, ok := e.peerOrder[p.ID]; ok { if idx, ok := e.peerOrder[p.ID]; ok {
return idx return idx
} }
@@ -180,7 +177,7 @@ func (e *componentEncoder) appendPeer(p *nbpeer.Peer) uint32 {
// (c.RouterPeers may contain peers not in c.Peers when validation rules drop // (c.RouterPeers may contain peers not in c.Peers when validation rules drop
// them) and returns their wire indexes for the RouterPeerIndexes field. Must // them) and returns their wire indexes for the RouterPeerIndexes field. Must
// run before any encoder that resolves peer ids via e.peerOrder. // run before any encoder that resolves peer ids via e.peerOrder.
func (e *componentEncoder) indexRouterPeers(routers map[string]*nbpeer.Peer) []uint32 { func (e *componentEncoder) indexRouterPeers(routers map[string]*types.ComponentPeer) []uint32 {
if len(routers) == 0 { if len(routers) == 0 {
return nil return nil
} }
@@ -514,7 +511,7 @@ func encodeCustomZones(zones []nbdns.CustomZone) []*proto.CustomZone {
return out return out
} }
func (e *componentEncoder) encodeNetworkResources(resources []*resourceTypes.NetworkResource) []*proto.NetworkResourceRaw { func (e *componentEncoder) encodeNetworkResources(resources []*types.ComponentResource) []*proto.NetworkResourceRaw {
if len(resources) == 0 { if len(resources) == 0 {
return nil return nil
} }
@@ -543,7 +540,7 @@ func (e *componentEncoder) encodeNetworkResources(resources []*resourceTypes.Net
return out return out
} }
func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*routerTypes.NetworkRouter) map[string]*proto.NetworkRouterList { func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*types.ComponentRouter) map[string]*proto.NetworkRouterList {
if len(routersMap) == 0 { if len(routersMap) == 0 {
return nil return nil
} }
@@ -692,20 +689,20 @@ func toAccountNetwork(n *types.Network) *proto.AccountNetwork {
return out return out
} }
func toPeerCompact(p *nbpeer.Peer) *proto.PeerCompact { func toPeerCompact(p *types.ComponentPeer) *proto.PeerCompact {
pc := &proto.PeerCompact{ pc := &proto.PeerCompact{
WgPubKey: decodeWgKey(p.Key), WgPubKey: decodeWgKey(p.Key),
SshPubKey: []byte(p.SSHKey), SshPubKey: []byte(p.SSHKey),
DnsLabel: p.DNSLabel, DnsLabel: p.DNSLabel,
AgentVersion: p.Meta.WtVersion, AgentVersion: p.AgentVersion,
AddedWithSsoLogin: p.UserID != "", AddedWithSsoLogin: p.AddedWithSSOLogin,
LoginExpirationEnabled: p.LoginExpirationEnabled, LoginExpirationEnabled: p.LoginExpirationEnabled,
SshEnabled: p.SSHEnabled, SshEnabled: p.SSHEnabled,
SupportsIpv6: p.SupportsIPv6(), SupportsIpv6: p.SupportsIPv6,
SupportsSourcePrefixes: p.SupportsSourcePrefixes(), SupportsSourcePrefixes: p.SupportsSourcePrefixes,
ServerSshAllowed: p.Meta.Flags.ServerSSHAllowed, ServerSshAllowed: p.ServerSSHAllowed,
} }
if p.LastLogin != nil { if !p.LastLogin.IsZero() {
pc.LastLoginUnixNano = p.LastLogin.UnixNano() pc.LastLoginUnixNano = p.LastLogin.UnixNano()
} }
switch { switch {
@@ -15,9 +15,6 @@ import (
goproto "google.golang.org/protobuf/proto" goproto "google.golang.org/protobuf/proto"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/types" "github.com/netbirdio/netbird/management/server/types"
nbroute "github.com/netbirdio/netbird/route" nbroute "github.com/netbirdio/netbird/route"
"github.com/netbirdio/netbird/shared/management/proto" "github.com/netbirdio/netbird/shared/management/proto"
@@ -155,29 +152,28 @@ func envelopesEquivalent(a, b *proto.NetworkMapEnvelope) bool {
} }
func newTestComponents() *types.NetworkMapComponents { func newTestComponents() *types.NetworkMapComponents {
peerA := &nbpeer.Peer{ peerA := &types.ComponentPeer{
ID: "peer-a", ID: "peer-a",
Key: testWgKeyA, Key: testWgKeyA,
IP: netip.AddrFrom4([4]byte{100, 64, 0, 1}), IP: netip.AddrFrom4([4]byte{100, 64, 0, 1}),
DNSLabel: "peera", DNSLabel: "peera",
SSHKey: "ssh-a", SSHKey: "ssh-a",
Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now()}, AgentVersion: "0.40.0",
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"},
} }
peerB := &nbpeer.Peer{ peerB := &types.ComponentPeer{
ID: "peer-b", ID: "peer-b",
Key: testWgKeyB, Key: testWgKeyB,
IP: netip.AddrFrom4([4]byte{100, 64, 0, 2}), IP: netip.AddrFrom4([4]byte{100, 64, 0, 2}),
IPv6: netip.AddrFrom16([16]byte{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}), IPv6: netip.AddrFrom16([16]byte{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}),
DNSLabel: "peerb", DNSLabel: "peerb",
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.25.0"}, AgentVersion: "0.25.0",
} }
peerC := &nbpeer.Peer{ peerC := &types.ComponentPeer{
ID: "peer-c", ID: "peer-c",
Key: testWgKeyC, Key: testWgKeyC,
IP: netip.AddrFrom4([4]byte{100, 64, 0, 3}), IP: netip.AddrFrom4([4]byte{100, 64, 0, 3}),
DNSLabel: "peerc", DNSLabel: "peerc",
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, AgentVersion: "0.40.0",
} }
return &types.NetworkMapComponents{ return &types.NetworkMapComponents{
@@ -191,12 +187,12 @@ func newTestComponents() *types.NetworkMapComponents {
PeerLoginExpirationEnabled: true, PeerLoginExpirationEnabled: true,
PeerLoginExpiration: 2 * time.Hour, PeerLoginExpiration: 2 * time.Hour,
}, },
Peers: map[string]*nbpeer.Peer{ Peers: map[string]*types.ComponentPeer{
"peer-a": peerA, "peer-a": peerA,
"peer-b": peerB, "peer-b": peerB,
"peer-c": peerC, "peer-c": peerC,
}, },
Groups: map[string]*types.Group{ Groups: map[string]*types.ComponentGroup{
"group-src": {ID: "group-src", PublicID: "1", Name: "Src", Peers: []string{"peer-a"}}, "group-src": {ID: "group-src", PublicID: "1", Name: "Src", Peers: []string{"peer-a"}},
"group-dst": {ID: "group-dst", PublicID: "2", Name: "Dst", Peers: []string{"peer-b", "peer-c"}}, "group-dst": {ID: "group-dst", PublicID: "2", Name: "Dst", Peers: []string{"peer-b", "peer-c"}},
}, },
@@ -215,7 +211,7 @@ func newTestComponents() *types.NetworkMapComponents {
}}, }},
}, },
}, },
RouterPeers: map[string]*nbpeer.Peer{"peer-c": peerC}, RouterPeers: map[string]*types.ComponentPeer{"peer-c": peerC},
} }
} }
@@ -381,12 +377,12 @@ func TestEncodeNetworkMapEnvelope_MalformedWgKey(t *testing.T) {
func TestEncodeNetworkMapEnvelope_IPv6OnlyPeer(t *testing.T) { func TestEncodeNetworkMapEnvelope_IPv6OnlyPeer(t *testing.T) {
c := newTestComponents() c := newTestComponents()
v6Only := &nbpeer.Peer{ v6Only := &types.ComponentPeer{
ID: "peer-v6", ID: "peer-v6",
Key: testWgKeyA, Key: testWgKeyA,
IPv6: netip.AddrFrom16([16]byte{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9}), IPv6: netip.AddrFrom16([16]byte{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9}),
DNSLabel: "peerv6", DNSLabel: "peerv6",
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, AgentVersion: "0.40.0",
} }
c.Peers["peer-v6"] = v6Only c.Peers["peer-v6"] = v6Only
@@ -405,11 +401,11 @@ func TestEncodeNetworkMapEnvelope_IPv6OnlyPeer(t *testing.T) {
func TestEncodeNetworkMapEnvelope_PeerWithoutIP(t *testing.T) { func TestEncodeNetworkMapEnvelope_PeerWithoutIP(t *testing.T) {
c := newTestComponents() c := newTestComponents()
c.Peers["peer-noip"] = &nbpeer.Peer{ c.Peers["peer-noip"] = &types.ComponentPeer{
ID: "peer-noip", ID: "peer-noip",
Key: testWgKeyA, Key: testWgKeyA,
DNSLabel: "peernoip", DNSLabel: "peernoip",
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, AgentVersion: "0.40.0",
} }
full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull()
@@ -444,9 +440,9 @@ func TestEncodeNetworkMapEnvelope_EmptyInput(t *testing.T) {
func TestEncodeNetworkMapEnvelope_PeerLoginExpirationFields(t *testing.T) { func TestEncodeNetworkMapEnvelope_PeerLoginExpirationFields(t *testing.T) {
c := newTestComponents() c := newTestComponents()
now := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC) now := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC)
c.Peers["peer-a"].UserID = "user-1" c.Peers["peer-a"].AddedWithSSOLogin = true
c.Peers["peer-a"].LoginExpirationEnabled = true c.Peers["peer-a"].LoginExpirationEnabled = true
c.Peers["peer-a"].LastLogin = &now c.Peers["peer-a"].LastLogin = now
full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull()
@@ -557,7 +553,7 @@ func TestEncodeNetworkMapEnvelope_ResourceOnlyPolicyShippedAndIndexed(t *testing
} }
// Resource must appear in components.NetworkResources with a seq id — // Resource must appear in components.NetworkResources with a seq id —
// encoder uses that to translate the xid map key to uint32. // encoder uses that to translate the xid map key to uint32.
c.NetworkResources = []*resourceTypes.NetworkResource{ c.NetworkResources = []*types.ComponentResource{
{ID: "resource-x", PublicID: "77", Name: "res-x", Enabled: true}, {ID: "resource-x", PublicID: "77", Name: "res-x", Enabled: true},
} }
@@ -625,11 +621,11 @@ func TestEncodeNetworkMapEnvelope_PostureFailedPeers(t *testing.T) {
func TestEncodeNetworkMapEnvelope_RoutersMap(t *testing.T) { func TestEncodeNetworkMapEnvelope_RoutersMap(t *testing.T) {
c := newTestComponents() c := newTestComponents()
c.NetworkXIDToPublicID = map[string]string{"net-1": "5"} c.NetworkXIDToPublicID = map[string]string{"net-1": "5"}
c.RoutersMap = map[string]map[string]*routerTypes.NetworkRouter{ c.RoutersMap = map[string]map[string]*types.ComponentRouter{
"net-1": { "net-1": {
"peer-c": { "peer-c": {
ID: "router-1", PublicID: "200", PublicID: "200",
Peer: "peer-c", Masquerade: true, Metric: 10, Enabled: true, Peer: "peer-c", Masquerade: true, Metric: 10, Enabled: true,
}, },
}, },
} }
@@ -655,14 +651,14 @@ func TestEncodeNetworkMapEnvelope_RouterPeerNotInComponentsPeers(t *testing.T) {
// peer_index reference must still resolve. // peer_index reference must still resolve.
c := newTestComponents() c := newTestComponents()
delete(c.Peers, "peer-c") delete(c.Peers, "peer-c")
routerPeer := &nbpeer.Peer{ routerPeer := &types.ComponentPeer{
ID: "peer-c", Key: testWgKeyC, IP: netip.AddrFrom4([4]byte{100, 64, 0, 3}), ID: "peer-c", Key: testWgKeyC, IP: netip.AddrFrom4([4]byte{100, 64, 0, 3}),
DNSLabel: "peerc", Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, DNSLabel: "peerc", AgentVersion: "0.40.0",
} }
c.RouterPeers = map[string]*nbpeer.Peer{"peer-c": routerPeer} c.RouterPeers = map[string]*types.ComponentPeer{"peer-c": routerPeer}
c.NetworkXIDToPublicID = map[string]string{"net-1": "5"} c.NetworkXIDToPublicID = map[string]string{"net-1": "5"}
c.RoutersMap = map[string]map[string]*routerTypes.NetworkRouter{ c.RoutersMap = map[string]map[string]*types.ComponentRouter{
"net-1": {"peer-c": {ID: "r-1", PublicID: "1", Peer: "peer-c", Enabled: true}}, "net-1": {"peer-c": {PublicID: "1", Peer: "peer-c", Enabled: true}},
} }
full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull() full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull()
@@ -695,9 +691,9 @@ func TestToProxyPatch_EmptyInputReturnsNil(t *testing.T) {
func TestToProxyPatch_PopulatesAllFields(t *testing.T) { func TestToProxyPatch_PopulatesAllFields(t *testing.T) {
nm := &types.NetworkMap{ nm := &types.NetworkMap{
Peers: []*nbpeer.Peer{{ Peers: []*types.ComponentPeer{{
ID: "ext-peer", Key: testWgKeyA, IP: netip.AddrFrom4([4]byte{100, 64, 0, 9}), ID: "ext-peer", Key: testWgKeyA, IP: netip.AddrFrom4([4]byte{100, 64, 0, 9}),
DNSLabel: "extpeer", Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, DNSLabel: "extpeer", AgentVersion: "0.40.0",
}}, }},
FirewallRules: []*types.FirewallRule{{ FirewallRules: []*types.FirewallRule{{
PeerIP: "100.64.0.9", Action: "accept", Direction: 0, Protocol: "tcp", PeerIP: "100.64.0.9", Action: "accept", Direction: 0, Protocol: "tcp",
@@ -780,6 +776,6 @@ func TestEncodeNetworkMapEnvelope_AccountSettingsAlwaysEmitted(t *testing.T) {
func emptyNetworkMapComponents() *types.NetworkMapComponents { func emptyNetworkMapComponents() *types.NetworkMapComponents {
return types.EmptyNetworkMapComponents( return types.EmptyNetworkMapComponents(
&types.NetworkMapComponents{ &types.NetworkMapComponents{
PeerID: "peer-id", Peers: map[string]*nbpeer.Peer{"peer-id": {}}}, PeerID: "peer-id", Peers: map[string]*types.ComponentPeer{"peer-id": {}}},
) )
} }
@@ -19,10 +19,10 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
mkComponents := func(rule *types.PolicyRule, sshEnabled bool) (*types.NetworkMapComponents, *nbpeer.Peer) { mkComponents := func(rule *types.PolicyRule, sshEnabled bool) (*types.NetworkMapComponents, *nbpeer.Peer) {
peer := &nbpeer.Peer{ID: targetPeerID, SSHEnabled: sshEnabled} peer := &nbpeer.Peer{ID: targetPeerID, SSHEnabled: sshEnabled}
group := &types.Group{ID: targetGroupID, Name: "dst", Peers: []string{targetPeerID}} group := &types.ComponentGroup{ID: targetGroupID, Name: "dst", Peers: []string{targetPeerID}}
return &types.NetworkMapComponents{ return &types.NetworkMapComponents{
Peers: map[string]*nbpeer.Peer{targetPeerID: peer}, Peers: map[string]*types.ComponentPeer{targetPeerID: peer.ToComponent()},
Groups: map[string]*types.Group{targetGroupID: group}, Groups: map[string]*types.ComponentGroup{targetGroupID: group},
Policies: []*types.Policy{{ Policies: []*types.Policy{{
ID: "p", ID: "p",
Enabled: true, Enabled: true,
@@ -158,8 +158,8 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
func TestComputeSSHEnabledForPeer_TargetMissingFromComponents(t *testing.T) { func TestComputeSSHEnabledForPeer_TargetMissingFromComponents(t *testing.T) {
peer := &nbpeer.Peer{ID: "missing", SSHEnabled: true} peer := &nbpeer.Peer{ID: "missing", SSHEnabled: true}
c := &types.NetworkMapComponents{ c := &types.NetworkMapComponents{
Peers: map[string]*nbpeer.Peer{}, // target peer NOT present Peers: map[string]*types.ComponentPeer{}, // target peer NOT present
Groups: map[string]*types.Group{ Groups: map[string]*types.ComponentGroup{
"g": {ID: "g", Peers: []string{"missing"}}, "g": {ID: "g", Peers: []string{"missing"}},
}, },
Policies: []*types.Policy{{ Policies: []*types.Policy{{
+7 -2
View File
@@ -6,6 +6,7 @@ import (
"github.com/netbirdio/netbird/management/server/account" "github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/activity"
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
"github.com/netbirdio/netbird/management/server/permissions" "github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/permissions/modules" "github.com/netbirdio/netbird/management/server/permissions/modules"
"github.com/netbirdio/netbird/management/server/permissions/operations" "github.com/netbirdio/netbird/management/server/permissions/operations"
@@ -30,6 +31,10 @@ type managerImpl struct {
accountManager account.Manager accountManager account.Manager
} }
func eventMetaResource(group *types.Group, resource *resourceTypes.NetworkResource) map[string]any {
return map[string]any{"name": group.Name, "id": group.ID, "resource_name": resource.Name, "resource_id": resource.ID, "resource_type": resource.Type}
}
type mockManager struct { type mockManager struct {
} }
@@ -109,7 +114,7 @@ func (m *managerImpl) AddResourceToGroupInTransaction(ctx context.Context, trans
} }
event := func() { event := func() {
m.accountManager.StoreEvent(ctx, userID, groupID, accountID, activity.ResourceAddedToGroup, group.EventMetaResource(networkResource)) m.accountManager.StoreEvent(ctx, userID, groupID, accountID, activity.ResourceAddedToGroup, eventMetaResource(group, networkResource))
} }
return event, nil return event, nil
@@ -133,7 +138,7 @@ func (m *managerImpl) RemoveResourceFromGroupInTransaction(ctx context.Context,
} }
event := func() { event := func() {
m.accountManager.StoreEvent(ctx, userID, groupID, accountID, activity.ResourceRemovedFromGroup, group.EventMetaResource(networkResource)) m.accountManager.StoreEvent(ctx, userID, groupID, accountID, activity.ResourceRemovedFromGroup, eventMetaResource(group, networkResource))
} }
return event, nil return event, nil
@@ -446,7 +446,7 @@ func (h *Handler) GetAccessiblePeers(w http.ResponseWriter, r *http.Request) {
netMap := account.GetPeerNetworkMapFromComponents(ctx, peerID, dns.CustomZone{}, nil, validPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil, account.GetActiveGroupUsers()) netMap := account.GetPeerNetworkMapFromComponents(ctx, peerID, dns.CustomZone{}, nil, validPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil, account.GetActiveGroupUsers())
util.WriteJSONObject(ctx, w, toAccessiblePeers(netMap, dnsDomain)) util.WriteJSONObject(ctx, w, toAccessiblePeers(netMap, account.Peers, dnsDomain))
} }
func (h *Handler) CreateTemporaryAccess(w http.ResponseWriter, r *http.Request) { func (h *Handler) CreateTemporaryAccess(w http.ResponseWriter, r *http.Request) {
@@ -534,15 +534,20 @@ func (h *Handler) CreateTemporaryAccess(w http.ResponseWriter, r *http.Request)
util.WriteJSONObject(r.Context(), w, resp) util.WriteJSONObject(r.Context(), w, resp)
} }
func toAccessiblePeers(netMap *types.NetworkMap, dnsDomain string) []api.AccessiblePeer { // toAccessiblePeers rehydrates the calculated map's component peers into the
// account's full peer objects, which carry the location/status/meta fields
// the API response needs.
func toAccessiblePeers(netMap *types.NetworkMap, accountPeers map[string]*nbpeer.Peer, dnsDomain string) []api.AccessiblePeer {
accessiblePeers := make([]api.AccessiblePeer, 0, len(netMap.Peers)+len(netMap.OfflinePeers)) accessiblePeers := make([]api.AccessiblePeer, 0, len(netMap.Peers)+len(netMap.OfflinePeers))
for _, p := range netMap.Peers { add := func(peers []*types.ComponentPeer) {
accessiblePeers = append(accessiblePeers, peerToAccessiblePeer(p, dnsDomain)) for _, p := range peers {
} if peer := accountPeers[p.ID]; peer != nil {
accessiblePeers = append(accessiblePeers, peerToAccessiblePeer(peer, dnsDomain))
for _, p := range netMap.OfflinePeers { }
accessiblePeers = append(accessiblePeers, peerToAccessiblePeer(p, dnsDomain)) }
} }
add(netMap.Peers)
add(netMap.OfflinePeers)
return accessiblePeers return accessiblePeers
} }
@@ -14,6 +14,7 @@ import (
nbDomain "github.com/netbirdio/netbird/shared/management/domain" nbDomain "github.com/netbirdio/netbird/shared/management/domain"
"github.com/netbirdio/netbird/shared/management/http/api" "github.com/netbirdio/netbird/shared/management/http/api"
sharedTypes "github.com/netbirdio/netbird/shared/management/types"
) )
type NetworkResourceType string type NetworkResourceType string
@@ -64,6 +65,27 @@ func NewNetworkResource(accountID, networkID, name, description, address string,
}, nil }, nil
} }
// ToComponent converts the resource to its self-contained components
// representation. Returns nil for a nil resource.
func (n *NetworkResource) ToComponent() *sharedTypes.ComponentResource {
if n == nil {
return nil
}
return &sharedTypes.ComponentResource{
ID: n.ID,
PublicID: n.PublicID,
NetworkID: n.NetworkID,
AccountID: n.AccountID,
Name: n.Name,
Description: n.Description,
Type: sharedTypes.ComponentResourceType(n.Type),
Address: n.Address,
Domain: n.Domain,
Prefix: n.Prefix,
Enabled: n.Enabled,
}
}
func (n *NetworkResource) ToAPIResponse(groups []api.GroupMinimum) *api.NetworkResource { func (n *NetworkResource) ToAPIResponse(groups []api.GroupMinimum) *api.NetworkResource {
addr := n.Prefix.String() addr := n.Prefix.String()
if n.Type == Domain { if n.Type == Domain {
@@ -7,6 +7,7 @@ import (
"github.com/netbirdio/netbird/management/server/networks/types" "github.com/netbirdio/netbird/management/server/networks/types"
"github.com/netbirdio/netbird/shared/management/http/api" "github.com/netbirdio/netbird/shared/management/http/api"
sharedTypes "github.com/netbirdio/netbird/shared/management/types"
) )
type NetworkRouter struct { type NetworkRouter struct {
@@ -21,6 +22,36 @@ type NetworkRouter struct {
Enabled bool Enabled bool
} }
// ToComponent converts the router to its self-contained components
// representation. Returns nil for a nil router.
func (n *NetworkRouter) ToComponent() *sharedTypes.ComponentRouter {
if n == nil {
return nil
}
return &sharedTypes.ComponentRouter{
NetworkID: n.NetworkID,
PublicID: n.PublicID,
Peer: n.Peer,
PeerGroups: n.PeerGroups,
Masquerade: n.Masquerade,
Metric: n.Metric,
Enabled: n.Enabled,
}
}
// ToComponentMap converts a peer-keyed router map to its components
// representation.
func ToComponentMap(routers map[string]*NetworkRouter) map[string]*sharedTypes.ComponentRouter {
if routers == nil {
return nil
}
out := make(map[string]*sharedTypes.ComponentRouter, len(routers))
for id, r := range routers {
out[id] = r.ToComponent()
}
return out
}
func NewNetworkRouter(accountID string, networkID string, peer string, peerGroups []string, masquerade bool, metric int, enabled bool) (*NetworkRouter, error) { func NewNetworkRouter(accountID string, networkID string, peer string, peerGroups []string, masquerade bool, metric int, enabled bool) (*NetworkRouter, error) {
r := &NetworkRouter{ r := &NetworkRouter{
ID: xid.New().String(), ID: xid.New().String(),
+2 -2
View File
@@ -405,7 +405,7 @@ func (am *DefaultAccountManager) CreatePeerJob(ctx context.Context, accountID, p
return status.NewPeerNotPartOfAccountError() return status.NewPeerNotPartOfAccountError()
} }
meetMinVer, err := posture.MeetsMinVersion(remoteJobsMinVer, p.Meta.WtVersion) meetMinVer, err := version.MeetsMinVersion(remoteJobsMinVer, p.Meta.WtVersion)
if !version.IsDevelopmentVersion(p.Meta.WtVersion) && (!meetMinVer || err != nil) { if !version.IsDevelopmentVersion(p.Meta.WtVersion) && (!meetMinVer || err != nil) {
return status.Errorf(status.PreconditionFailed, "peer version %s does not meet the minimum required version %s for remote jobs", p.Meta.WtVersion, remoteJobsMinVer) return status.Errorf(status.PreconditionFailed, "peer version %s does not meet the minimum required version %s for remote jobs", p.Meta.WtVersion, remoteJobsMinVer)
} }
@@ -1588,7 +1588,7 @@ func affectedPeerIDsFromNetworkMap(nmap *types.NetworkMap, selfPeerID string) []
} }
seen := make(map[string]struct{}, len(nmap.Peers)+len(nmap.OfflinePeers)) seen := make(map[string]struct{}, len(nmap.Peers)+len(nmap.OfflinePeers))
ids := make([]string, 0, len(nmap.Peers)+len(nmap.OfflinePeers)) ids := make([]string, 0, len(nmap.Peers)+len(nmap.OfflinePeers))
add := func(peers []*nbpeer.Peer) { add := func(peers []*types.ComponentPeer) {
for _, p := range peers { for _, p := range peers {
if p == nil || p.ID == "" || p.ID == selfPeerID { if p == nil || p.ID == "" || p.ID == selfPeerID {
continue continue
+30
View File
@@ -13,6 +13,7 @@ import (
"github.com/netbirdio/netbird/management/server/util" "github.com/netbirdio/netbird/management/server/util"
"github.com/netbirdio/netbird/shared/management/http/api" "github.com/netbirdio/netbird/shared/management/http/api"
sharedTypes "github.com/netbirdio/netbird/shared/management/types"
) )
// Peer capability constants mirror the proto enum values. // Peer capability constants mirror the proto enum values.
@@ -205,6 +206,35 @@ func (p *Peer) AddedWithSSOLogin() bool {
return p.UserID != "" return p.UserID != ""
} }
// ToComponent converts the peer to its self-contained components
// representation, carrying exactly the subset of peer data that crosses the
// components wire format. Returns nil for a nil peer so callers can convert
// possibly-missing peers without guarding.
func (p *Peer) ToComponent() *sharedTypes.ComponentPeer {
if p == nil {
return nil
}
cp := &sharedTypes.ComponentPeer{
ID: p.ID,
Key: p.Key,
IP: p.IP,
IPv6: p.IPv6,
DNSLabel: p.DNSLabel,
SSHKey: p.SSHKey,
SSHEnabled: p.SSHEnabled,
ServerSSHAllowed: p.Meta.Flags.ServerSSHAllowed,
AgentVersion: p.Meta.WtVersion,
SupportsSourcePrefixes: p.SupportsSourcePrefixes(),
SupportsIPv6: p.SupportsIPv6(),
LoginExpirationEnabled: p.LoginExpirationEnabled,
AddedWithSSOLogin: p.AddedWithSSOLogin(),
}
if p.LastLogin != nil {
cp.LastLogin = *p.LastLogin
}
return cp
}
// HasCapability reports whether the peer has the given capability. // HasCapability reports whether the peer has the given capability.
func (p *Peer) HasCapability(capability int32) bool { func (p *Peer) HasCapability(capability int32) bool {
return slices.Contains(p.Meta.Capabilities, capability) return slices.Contains(p.Meta.Capabilities, capability)
+2 -2
View File
@@ -1092,14 +1092,14 @@ func TestToSyncResponse(t *testing.T) {
} }
networkMap := &types.NetworkMap{ networkMap := &types.NetworkMap{
Network: &types.Network{Net: *ipnet, Serial: 1000}, Network: &types.Network{Net: *ipnet, Serial: 1000},
Peers: []*nbpeer.Peer{{ Peers: []*types.ComponentPeer{{
IP: netip.MustParseAddr("192.168.1.2"), IP: netip.MustParseAddr("192.168.1.2"),
IPv6: netip.MustParseAddr("fd00::2"), IPv6: netip.MustParseAddr("fd00::2"),
Key: "peer2-key", Key: "peer2-key",
DNSLabel: "peer2", DNSLabel: "peer2",
SSHEnabled: true, SSHEnabled: true,
SSHKey: "peer2-ssh-key"}}, SSHKey: "peer2-ssh-key"}},
OfflinePeers: []*nbpeer.Peer{{ OfflinePeers: []*types.ComponentPeer{{
IP: netip.MustParseAddr("192.168.1.3"), IP: netip.MustParseAddr("192.168.1.3"),
IPv6: netip.MustParseAddr("fd00::3"), IPv6: netip.MustParseAddr("fd00::3"),
Key: "peer3-key", Key: "peer3-key",
+2 -28
View File
@@ -3,11 +3,9 @@ package posture
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"github.com/hashicorp/go-version"
nbpeer "github.com/netbirdio/netbird/management/server/peer" nbpeer "github.com/netbirdio/netbird/management/server/peer"
nbversion "github.com/netbirdio/netbird/version"
) )
type NBVersionCheck struct { type NBVersionCheck struct {
@@ -16,14 +14,8 @@ type NBVersionCheck struct {
var _ Check = (*NBVersionCheck)(nil) var _ Check = (*NBVersionCheck)(nil)
// sanitizeVersion removes anything after the pre-release tag (e.g., "-dev", "-alpha", etc.)
func sanitizeVersion(version string) string {
parts := strings.Split(version, "-")
return parts[0]
}
func (n *NBVersionCheck) Check(ctx context.Context, peer nbpeer.Peer) (bool, error) { func (n *NBVersionCheck) Check(ctx context.Context, peer nbpeer.Peer) (bool, error) {
meetsMin, err := MeetsMinVersion(n.MinVersion, peer.Meta.WtVersion) meetsMin, err := nbversion.MeetsMinVersion(n.MinVersion, peer.Meta.WtVersion)
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -48,21 +40,3 @@ func (n *NBVersionCheck) Validate() error {
} }
return nil return nil
} }
// MeetsMinVersion checks if the peer's version meets or exceeds the minimum required version
func MeetsMinVersion(minVer, peerVer string) (bool, error) {
peerVer = sanitizeVersion(peerVer)
minVer = sanitizeVersion(minVer)
peerNBVer, err := version.NewVersion(peerVer)
if err != nil {
return false, err
}
constraints, err := version.NewConstraint(">= " + minVer)
if err != nil {
return false, err
}
return constraints.Check(peerNBVer), nil
}
@@ -139,68 +139,3 @@ func TestNBVersionCheck_Validate(t *testing.T) {
}) })
} }
} }
func TestMeetsMinVersion(t *testing.T) {
tests := []struct {
name string
minVer string
peerVer string
want bool
wantErr bool
}{
{
name: "Peer version greater than min version",
minVer: "0.26.0",
peerVer: "0.60.1",
want: true,
wantErr: false,
},
{
name: "Peer version equals min version",
minVer: "1.0.0",
peerVer: "1.0.0",
want: true,
wantErr: false,
},
{
name: "Peer version less than min version",
minVer: "1.0.0",
peerVer: "0.9.9",
want: false,
wantErr: false,
},
{
name: "Peer version with pre-release tag greater than min version",
minVer: "1.0.0",
peerVer: "1.0.1-alpha",
want: true,
wantErr: false,
},
{
name: "Invalid peer version format",
minVer: "1.0.0",
peerVer: "dev",
want: false,
wantErr: true,
},
{
name: "Invalid min version format",
minVer: "invalid.version",
peerVer: "1.0.0",
want: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := MeetsMinVersion(tt.minVer, tt.peerVer)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.want, got)
})
}
}
+8 -7
View File
@@ -283,8 +283,8 @@ func (a *Account) SynthesizePrivateServiceZones(peerID string) []nbdns.CustomZon
// it, adding a single private service would black-hole every // it, adding a single private service would black-hole every
// other name under the zone apex. // other name under the zone apex.
zone = &nbdns.CustomZone{ zone = &nbdns.CustomZone{
Domain: dns.Fqdn(serviceDomainZone), Domain: dns.Fqdn(serviceDomainZone),
Records: []nbdns.SimpleRecord{}, Records: []nbdns.SimpleRecord{},
NonAuthoritative: true, NonAuthoritative: true,
SearchDomainDisabled: true, SearchDomainDisabled: true,
} }
@@ -1082,6 +1082,7 @@ func (a *Account) connResourcesGenerator(ctx context.Context, targetPeer *nbpeer
peersExists := make(map[string]struct{}) peersExists := make(map[string]struct{})
rules := make([]*FirewallRule, 0) rules := make([]*FirewallRule, 0)
peers := make([]*nbpeer.Peer, 0) peers := make([]*nbpeer.Peer, 0)
targetComponent := targetPeer.ToComponent()
return func(rule *PolicyRule, groupPeers []*nbpeer.Peer, direction int) { return func(rule *PolicyRule, groupPeers []*nbpeer.Peer, direction int) {
for _, peer := range groupPeers { for _, peer := range groupPeers {
@@ -1117,10 +1118,10 @@ func (a *Account) connResourcesGenerator(ctx context.Context, targetPeer *nbpeer
if len(rule.Ports) == 0 && len(rule.PortRanges) == 0 { if len(rule.Ports) == 0 && len(rule.PortRanges) == 0 {
rules = append(rules, &fr) rules = append(rules, &fr)
} else { } else {
rules = append(rules, ExpandPortsAndRanges(fr, rule, targetPeer)...) rules = append(rules, ExpandPortsAndRanges(fr, rule, targetComponent)...)
} }
rules = AppendIPv6FirewallRule(rules, rulesExists, peer, targetPeer, rule, FirewallRuleContext{ rules = AppendIPv6FirewallRule(rules, rulesExists, peer.ToComponent(), targetComponent, rule, FirewallRuleContext{
Direction: direction, Direction: direction,
DirStr: strconv.Itoa(direction), DirStr: strconv.Itoa(direction),
ProtocolStr: string(protocol), ProtocolStr: string(protocol),
@@ -1280,7 +1281,7 @@ func (a *Account) getRouteFirewallRules(ctx context.Context, peerID string, poli
return fwRules return fwRules
} }
func (a *Account) getRulePeers(rule *PolicyRule, postureChecks []string, peerID string, distributionPeers map[string]struct{}, validatedPeersMap map[string]struct{}) []*nbpeer.Peer { func (a *Account) getRulePeers(rule *PolicyRule, postureChecks []string, peerID string, distributionPeers map[string]struct{}, validatedPeersMap map[string]struct{}) []*ComponentPeer {
distPeersWithPolicy := make(map[string]struct{}) distPeersWithPolicy := make(map[string]struct{})
for _, id := range rule.Sources { for _, id := range rule.Sources {
group := a.Groups[id] group := a.Groups[id]
@@ -1307,13 +1308,13 @@ func (a *Account) getRulePeers(rule *PolicyRule, postureChecks []string, peerID
} }
} }
distributionGroupPeers := make([]*nbpeer.Peer, 0, len(distPeersWithPolicy)) distributionGroupPeers := make([]*ComponentPeer, 0, len(distPeersWithPolicy))
for pID := range distPeersWithPolicy { for pID := range distPeersWithPolicy {
peer := a.Peers[pID] peer := a.Peers[pID]
if peer == nil { if peer == nil {
continue continue
} }
distributionGroupPeers = append(distributionGroupPeers, peer) distributionGroupPeers = append(distributionGroupPeers, peer.ToComponent())
} }
return distributionGroupPeers return distributionGroupPeers
} }
+32 -28
View File
@@ -9,9 +9,7 @@ import (
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/internals/modules/zones" "github.com/netbirdio/netbird/management/internals/modules/zones"
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"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/telemetry" "github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/route" "github.com/netbirdio/netbird/route"
) )
@@ -113,7 +111,7 @@ func (a *Account) GetPeerNetworkMapComponents(
PeerID: peerID, PeerID: peerID,
Network: a.Network.Copy(), Network: a.Network.Copy(),
// must include the target peer as it's required on the client // must include the target peer as it's required on the client
Peers: map[string]*nbpeer.Peer{peerID: peer}, Peers: map[string]*ComponentPeer{peerID: peer.ToComponent()},
}) })
} }
@@ -126,7 +124,7 @@ func (a *Account) GetPeerNetworkMapComponents(
PeerID: peerID, PeerID: peerID,
Network: a.Network.Copy(), Network: a.Network.Copy(),
// must include the target peer as it's required on the client // must include the target peer as it's required on the client
Peers: map[string]*nbpeer.Peer{peerID: peer}, Peers: map[string]*ComponentPeer{peerID: peer.ToComponent()},
}) })
} }
@@ -136,10 +134,10 @@ func (a *Account) GetPeerNetworkMapComponents(
NameServerGroups: make([]*nbdns.NameServerGroup, 0), NameServerGroups: make([]*nbdns.NameServerGroup, 0),
CustomZoneDomain: peersCustomZone.Domain, CustomZoneDomain: peersCustomZone.Domain,
ResourcePoliciesMap: make(map[string][]*Policy), ResourcePoliciesMap: make(map[string][]*Policy),
RoutersMap: make(map[string]map[string]*routerTypes.NetworkRouter), RoutersMap: make(map[string]map[string]*ComponentRouter),
NetworkResources: make([]*resourceTypes.NetworkResource, 0), NetworkResources: make([]*ComponentResource, 0),
PostureFailedPeers: make(map[string]map[string]struct{}, len(a.PostureChecks)), PostureFailedPeers: make(map[string]map[string]struct{}, len(a.PostureChecks)),
RouterPeers: make(map[string]*nbpeer.Peer), RouterPeers: make(map[string]*ComponentPeer),
NetworkXIDToPublicID: make(map[string]string, len(a.Networks)), NetworkXIDToPublicID: make(map[string]string, len(a.Networks)),
PostureCheckXIDToPublicID: make(map[string]string, len(a.PostureChecks)), PostureCheckXIDToPublicID: make(map[string]string, len(a.PostureChecks)),
} }
@@ -174,7 +172,7 @@ func (a *Account) GetPeerNetworkMapComponents(
} }
components.Peers = relevantPeers components.Peers = relevantPeers
components.Groups = relevantGroups components.Groups = GroupsToComponent(relevantGroups)
components.Policies = relevantPolicies components.Policies = relevantPolicies
components.Routes = relevantRoutes components.Routes = relevantRoutes
components.AllDNSRecords = filterDNSRecordsByPeers(peersCustomZone.Records, relevantPeers, peer.SupportsIPv6() && peer.IPv6.IsValid()) components.AllDNSRecords = filterDNSRecordsByPeers(peersCustomZone.Records, relevantPeers, peer.SupportsIPv6() && peer.IPv6.IsValid())
@@ -223,7 +221,7 @@ func (a *Account) GetPeerNetworkMapComponents(
} }
for _, pID := range a.getPostureValidPeersSaveFailed(peers, policy.SourcePostureChecks, validatedPeersMap, &components.PostureFailedPeers) { for _, pID := range a.getPostureValidPeersSaveFailed(peers, policy.SourcePostureChecks, validatedPeersMap, &components.PostureFailedPeers) {
if _, exists := components.Peers[pID]; !exists { if _, exists := components.Peers[pID]; !exists {
components.Peers[pID] = a.GetPeer(pID) components.Peers[pID] = a.GetPeer(pID).ToComponent()
} }
} }
} else { } else {
@@ -256,14 +254,14 @@ func (a *Account) GetPeerNetworkMapComponents(
for _, srcGroupID := range rule.Sources { for _, srcGroupID := range rule.Sources {
if g := a.Groups[srcGroupID]; g != nil { if g := a.Groups[srcGroupID]; g != nil {
if _, exists := components.Groups[srcGroupID]; !exists { if _, exists := components.Groups[srcGroupID]; !exists {
components.Groups[srcGroupID] = g components.Groups[srcGroupID] = g.ToComponent()
} }
} }
} }
for _, dstGroupID := range rule.Destinations { for _, dstGroupID := range rule.Destinations {
if g := a.Groups[dstGroupID]; g != nil { if g := a.Groups[dstGroupID]; g != nil {
if _, exists := components.Groups[dstGroupID]; !exists { if _, exists := components.Groups[dstGroupID]; !exists {
components.Groups[dstGroupID] = g components.Groups[dstGroupID] = g.ToComponent()
} }
} }
} }
@@ -278,20 +276,22 @@ func (a *Account) GetPeerNetworkMapComponents(
// network in the account — accounts with many tenants/networks // network in the account — accounts with many tenants/networks
// shipped tens of unrelated peers in `peers[]` and `routers_map`. // shipped tens of unrelated peers in `peers[]` and `routers_map`.
if addSourcePeers { if addSourcePeers {
components.RoutersMap[resource.NetworkID] = networkRoutingPeers components.RoutersMap[resource.NetworkID] = routerTypes.ToComponentMap(networkRoutingPeers)
for peerIDKey := range networkRoutingPeers { for peerIDKey := range networkRoutingPeers {
if p := a.Peers[peerIDKey]; p != nil { if p := a.Peers[peerIDKey]; p != nil {
if _, exists := components.RouterPeers[peerIDKey]; !exists { cp := components.RouterPeers[peerIDKey]
components.RouterPeers[peerIDKey] = p if cp == nil {
cp = p.ToComponent()
components.RouterPeers[peerIDKey] = cp
} }
if _, exists := components.Peers[peerIDKey]; !exists { if _, exists := components.Peers[peerIDKey]; !exists {
if _, validated := validatedPeersMap[peerIDKey]; validated { if _, validated := validatedPeersMap[peerIDKey]; validated {
components.Peers[peerIDKey] = p components.Peers[peerIDKey] = cp
} }
} }
} }
} }
components.NetworkResources = append(components.NetworkResources, resource) components.NetworkResources = append(components.NetworkResources, resource.ToComponent())
} }
} }
@@ -312,14 +312,14 @@ func (a *Account) getPeersGroupsPoliciesRoutes(
peerSSHEnabled bool, peerSSHEnabled bool,
validatedPeersMap map[string]struct{}, validatedPeersMap map[string]struct{},
postureFailedPeers *map[string]map[string]struct{}, postureFailedPeers *map[string]map[string]struct{},
) (map[string]*nbpeer.Peer, map[string]*Group, []*Policy, []*route.Route, sshRequirements) { ) (map[string]*ComponentPeer, map[string]*Group, []*Policy, []*route.Route, sshRequirements) {
relevantPeerIDs := make(map[string]*nbpeer.Peer, len(a.Peers)/4) relevantPeerIDs := make(map[string]*ComponentPeer, len(a.Peers)/4)
relevantGroupIDs := make(map[string]*Group, len(a.Groups)/4) relevantGroupIDs := make(map[string]*Group, len(a.Groups)/4)
relevantPolicies := make([]*Policy, 0, len(a.Policies)) relevantPolicies := make([]*Policy, 0, len(a.Policies))
relevantRoutes := make([]*route.Route, 0, len(a.Routes)) relevantRoutes := make([]*route.Route, 0, len(a.Routes))
sshReqs := sshRequirements{neededGroupIDs: make(map[string]struct{})} sshReqs := sshRequirements{neededGroupIDs: make(map[string]struct{})}
relevantPeerIDs[peerID] = a.GetPeer(peerID) relevantPeerIDs[peerID] = a.GetPeer(peerID).ToComponent()
peerGroupSet := make(map[string]struct{}, 8) peerGroupSet := make(map[string]struct{}, 8)
for groupID, group := range a.Groups { for groupID, group := range a.Groups {
@@ -384,7 +384,7 @@ func (a *Account) getPeersGroupsPoliciesRoutes(
if r.Peer != "" { if r.Peer != "" {
if _, ok := validatedPeersMap[r.Peer]; ok { if _, ok := validatedPeersMap[r.Peer]; ok {
if p := a.GetPeer(r.Peer); p != nil { if p := a.GetPeer(r.Peer); p != nil {
relevantPeerIDs[r.Peer] = p relevantPeerIDs[r.Peer] = p.ToComponent()
} }
} }
} }
@@ -401,7 +401,7 @@ func (a *Account) getPeersGroupsPoliciesRoutes(
continue continue
} }
if p := a.GetPeer(pid); p != nil { if p := a.GetPeer(pid); p != nil {
relevantPeerIDs[pid] = p relevantPeerIDs[pid] = p.ToComponent()
} }
} }
} }
@@ -458,7 +458,9 @@ func (a *Account) getPeersGroupsPoliciesRoutes(
if peerInSources { if peerInSources {
policyRelevant = true policyRelevant = true
for _, pid := range destinationPeers { for _, pid := range destinationPeers {
relevantPeerIDs[pid] = a.GetPeer(pid) if _, exists := relevantPeerIDs[pid]; !exists {
relevantPeerIDs[pid] = a.GetPeer(pid).ToComponent()
}
} }
for _, dstGroupID := range rule.Destinations { for _, dstGroupID := range rule.Destinations {
relevantGroupIDs[dstGroupID] = a.GetGroup(dstGroupID) relevantGroupIDs[dstGroupID] = a.GetGroup(dstGroupID)
@@ -468,7 +470,9 @@ func (a *Account) getPeersGroupsPoliciesRoutes(
if peerInDestinations { if peerInDestinations {
policyRelevant = true policyRelevant = true
for _, pid := range sourcePeers { for _, pid := range sourcePeers {
relevantPeerIDs[pid] = a.GetPeer(pid) if _, exists := relevantPeerIDs[pid]; !exists {
relevantPeerIDs[pid] = a.GetPeer(pid).ToComponent()
}
} }
for _, srcGroupID := range rule.Sources { for _, srcGroupID := range rule.Sources {
relevantGroupIDs[srcGroupID] = a.GetGroup(srcGroupID) relevantGroupIDs[srcGroupID] = a.GetGroup(srcGroupID)
@@ -624,7 +628,7 @@ func (a *Account) getPostureValidPeersSaveFailed(inputPeers []string, postureChe
// that name them. Calculate() tolerates groups with empty Peers (the inner // that name them. Calculate() tolerates groups with empty Peers (the inner
// loops simply iterate zero times), so retaining them is behaviourally a // loops simply iterate zero times), so retaining them is behaviourally a
// no-op for the legacy path that consumes the same NetworkMapComponents. // no-op for the legacy path that consumes the same NetworkMapComponents.
func filterGroupPeers(groups *map[string]*Group, peers map[string]*nbpeer.Peer) { func filterGroupPeers(groups *map[string]*ComponentGroup, peers map[string]*ComponentPeer) {
for groupID, groupInfo := range *groups { for groupID, groupInfo := range *groups {
filteredPeers := make([]string, 0, len(groupInfo.Peers)) filteredPeers := make([]string, 0, len(groupInfo.Peers))
for _, pid := range groupInfo.Peers { for _, pid := range groupInfo.Peers {
@@ -634,14 +638,14 @@ func filterGroupPeers(groups *map[string]*Group, peers map[string]*nbpeer.Peer)
} }
if len(filteredPeers) != len(groupInfo.Peers) { if len(filteredPeers) != len(groupInfo.Peers) {
ng := groupInfo.Copy() ng := *groupInfo
ng.Peers = filteredPeers ng.Peers = filteredPeers
(*groups)[groupID] = ng (*groups)[groupID] = &ng
} }
} }
} }
func filterPostureFailedPeers(postureFailedPeers *map[string]map[string]struct{}, policies []*Policy, resourcePoliciesMap map[string][]*Policy, peers map[string]*nbpeer.Peer) { func filterPostureFailedPeers(postureFailedPeers *map[string]map[string]struct{}, policies []*Policy, resourcePoliciesMap map[string][]*Policy, peers map[string]*ComponentPeer) {
if len(*postureFailedPeers) == 0 { if len(*postureFailedPeers) == 0 {
return return
} }
@@ -676,7 +680,7 @@ func filterPostureFailedPeers(postureFailedPeers *map[string]map[string]struct{}
} }
} }
func filterDNSRecordsByPeers(records []nbdns.SimpleRecord, peers map[string]*nbpeer.Peer, includeIPv6 bool) []nbdns.SimpleRecord { func filterDNSRecordsByPeers(records []nbdns.SimpleRecord, peers map[string]*ComponentPeer, includeIPv6 bool) []nbdns.SimpleRecord {
if len(records) == 0 || len(peers) == 0 { if len(records) == 0 || len(peers) == 0 {
return nil return nil
} }
@@ -9,7 +9,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
) )
func TestPrivateService_NetworkMap_UserPeer_AndProxyPeer(t *testing.T) { func TestPrivateService_NetworkMap_UserPeer_AndProxyPeer(t *testing.T) {
@@ -49,7 +48,7 @@ func TestPrivateService_NetworkMap_UserPeer_AndProxyPeer(t *testing.T) {
}) })
} }
func netmapPeerIDs(peers []*nbpeer.Peer) []string { func netmapPeerIDs(peers []*ComponentPeer) []string {
ids := make([]string, 0, len(peers)) ids := make([]string, 0, len(peers))
for _, p := range peers { for _, p := range peers {
if p == nil { if p == nil {
+1 -1
View File
@@ -666,7 +666,7 @@ func Test_ExpandPortsAndRanges_SSHRuleExpansion(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
result := ExpandPortsAndRanges(tt.base, tt.rule, tt.peer) result := ExpandPortsAndRanges(tt.base, tt.rule, tt.peer.ToComponent())
var ports []string var ports []string
for _, fr := range result { for _, fr := range result {
+16 -13
View File
@@ -6,7 +6,6 @@ import (
"net" "net"
"net/netip" "net/netip"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
nbroute "github.com/netbirdio/netbird/route" nbroute "github.com/netbirdio/netbird/route"
sharedtypes "github.com/netbirdio/netbird/shared/management/types" sharedtypes "github.com/netbirdio/netbird/shared/management/types"
) )
@@ -18,9 +17,6 @@ type DNSSettings = sharedtypes.DNSSettings
type FirewallRule = sharedtypes.FirewallRule type FirewallRule = sharedtypes.FirewallRule
type Group = sharedtypes.Group
type GroupPeer = sharedtypes.GroupPeer
type Network = sharedtypes.Network type Network = sharedtypes.Network
type NetworkMap = sharedtypes.NetworkMap type NetworkMap = sharedtypes.NetworkMap
type ForwardingRule = sharedtypes.ForwardingRule type ForwardingRule = sharedtypes.ForwardingRule
@@ -42,6 +38,18 @@ type RouteFirewallRule = sharedtypes.RouteFirewallRule
type NetworkMapComponents = sharedtypes.NetworkMapComponents type NetworkMapComponents = sharedtypes.NetworkMapComponents
type ComponentPeer = sharedtypes.ComponentPeer
type ComponentGroup = sharedtypes.ComponentGroup
type ComponentRouter = sharedtypes.ComponentRouter
type ComponentResource = sharedtypes.ComponentResource
type ComponentResourceType = sharedtypes.ComponentResourceType
const (
ComponentResourceHost = sharedtypes.ComponentResourceHost
ComponentResourceSubnet = sharedtypes.ComponentResourceSubnet
ComponentResourceDomain = sharedtypes.ComponentResourceDomain
)
var EmptyNetworkMapComponents = sharedtypes.EmptyNetworkMapComponents var EmptyNetworkMapComponents = sharedtypes.EmptyNetworkMapComponents
type AccountSettingsInfo = sharedtypes.AccountSettingsInfo type AccountSettingsInfo = sharedtypes.AccountSettingsInfo
@@ -52,12 +60,7 @@ type NetworkMapComponentsCompact = sharedtypes.NetworkMapComponentsCompact
type LookupMap = sharedtypes.LookupMap type LookupMap = sharedtypes.LookupMap
type FirewallRuleContext = sharedtypes.FirewallRuleContext type FirewallRuleContext = sharedtypes.FirewallRuleContext
const ( const GroupAllName = sharedtypes.GroupAllName
GroupIssuedAPI = sharedtypes.GroupIssuedAPI
GroupIssuedJWT = sharedtypes.GroupIssuedJWT
GroupIssuedIntegration = sharedtypes.GroupIssuedIntegration
GroupAllName = sharedtypes.GroupAllName
)
// Function forwarders preserve types.X(...) call sites that previously // Function forwarders preserve types.X(...) call sites that previously
// resolved to package-local funcs. Plain forwarders (not var aliases) keep // resolved to package-local funcs. Plain forwarders (not var aliases) keep
@@ -67,11 +70,11 @@ func PolicyRuleImpliesLegacySSH(rule *PolicyRule) bool {
return sharedtypes.PolicyRuleImpliesLegacySSH(rule) return sharedtypes.PolicyRuleImpliesLegacySSH(rule)
} }
func ExpandPortsAndRanges(base FirewallRule, rule *PolicyRule, peer *nbpeer.Peer) []*FirewallRule { func ExpandPortsAndRanges(base FirewallRule, rule *PolicyRule, peer *ComponentPeer) []*FirewallRule {
return sharedtypes.ExpandPortsAndRanges(base, rule, peer) return sharedtypes.ExpandPortsAndRanges(base, rule, peer)
} }
func AppendIPv6FirewallRule(rules []*FirewallRule, rulesExists map[string]struct{}, peer, targetPeer *nbpeer.Peer, rule *PolicyRule, rc FirewallRuleContext) []*FirewallRule { func AppendIPv6FirewallRule(rules []*FirewallRule, rulesExists map[string]struct{}, peer, targetPeer *ComponentPeer, rule *PolicyRule, rc FirewallRuleContext) []*FirewallRule {
return sharedtypes.AppendIPv6FirewallRule(rules, rulesExists, peer, targetPeer, rule, rc) return sharedtypes.AppendIPv6FirewallRule(rules, rulesExists, peer, targetPeer, rule, rc)
} }
@@ -79,7 +82,7 @@ func CalculateNetworkMapFromComponents(ctx context.Context, components *NetworkM
return sharedtypes.CalculateNetworkMapFromComponents(ctx, components) return sharedtypes.CalculateNetworkMapFromComponents(ctx, components)
} }
func GenerateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule *PolicyRule, groupPeers []*nbpeer.Peer, direction int, includeIPv6 bool) []*RouteFirewallRule { func GenerateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule *PolicyRule, groupPeers []*ComponentPeer, direction int, includeIPv6 bool) []*RouteFirewallRule {
return sharedtypes.GenerateRouteFirewallRules(ctx, route, rule, groupPeers, direction, includeIPv6) return sharedtypes.GenerateRouteFirewallRules(ctx, route, rule, groupPeers, direction, includeIPv6)
} }
@@ -2,7 +2,6 @@ package types
import ( import (
"github.com/netbirdio/netbird/management/server/integration_reference" "github.com/netbirdio/netbird/management/server/integration_reference"
"github.com/netbirdio/netbird/management/server/networks/resources/types"
) )
const ( const (
@@ -68,10 +67,6 @@ func (g *Group) EventMeta() map[string]any {
return map[string]any{"name": g.Name} return map[string]any{"name": g.Name}
} }
func (g *Group) EventMetaResource(resource *types.NetworkResource) map[string]any {
return map[string]any{"name": g.Name, "id": g.ID, "resource_name": resource.Name, "resource_id": resource.ID, "resource_type": resource.Type}
}
func (g *Group) Copy() *Group { func (g *Group) Copy() *Group {
group := &Group{ group := &Group{
ID: g.ID, ID: g.ID,
@@ -95,14 +90,39 @@ func (g *Group) HasPeers() bool {
return len(g.Peers) > 0 return len(g.Peers) > 0
} }
// GroupAllName is the reserved name of the default group that contains every peer in an account.
const GroupAllName = "All"
// IsGroupAll checks if the group is a default "All" group. // IsGroupAll checks if the group is a default "All" group.
func (g *Group) IsGroupAll() bool { func (g *Group) IsGroupAll() bool {
return g.Name == GroupAllName return g.Name == GroupAllName
} }
// ToComponent converts the group to its self-contained components
// representation. The Peers slice is shared, not copied — components are
// treated as immutable snapshots. Returns nil for a nil group.
func (g *Group) ToComponent() *ComponentGroup {
if g == nil {
return nil
}
return &ComponentGroup{
ID: g.ID,
PublicID: g.PublicID,
Name: g.Name,
Peers: g.Peers,
}
}
// GroupsToComponent converts an id-keyed group map to its components
// representation, preserving nil entries.
func GroupsToComponent(groups map[string]*Group) map[string]*ComponentGroup {
if groups == nil {
return nil
}
out := make(map[string]*ComponentGroup, len(groups))
for id, g := range groups {
out[id] = g.ToComponent()
}
return out
}
// AddPeer adds peerID to Peers if not present, returning true if added. // AddPeer adds peerID to Peers if not present, returning true if added.
func (g *Group) AddPeer(peerID string) bool { func (g *Group) AddPeer(peerID string) bool {
if peerID == "" { if peerID == "" {
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
nbpeer "github.com/netbirdio/netbird/management/server/peer" nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/types"
) )
func TestNetworkMapComponents_IPv6EndToEnd(t *testing.T) { func TestNetworkMapComponents_IPv6EndToEnd(t *testing.T) {
@@ -104,7 +105,7 @@ func TestNetworkMapComponents_RemotePeerWithoutCapability(t *testing.T) {
require.NotNil(t, nm) require.NotNil(t, nm)
t.Run("AllowedIPs include remote v6", func(t *testing.T) { t.Run("AllowedIPs include remote v6", func(t *testing.T) {
var dst *nbpeer.Peer var dst *types.ComponentPeer
for _, p := range nm.Peers { for _, p := range nm.Peers {
if p.ID == "peer-dst-1" { if p.ID == "peer-dst-1" {
dst = p dst = p
@@ -49,7 +49,7 @@ func allPeersValidated(account *types.Account, excludePeerIDs ...string) map[str
return validated return validated
} }
func peerIDs(peers []*nbpeer.Peer) []string { func peerIDs(peers []*types.ComponentPeer) []string {
ids := make([]string, len(peers)) ids := make([]string, len(peers))
for i, p := range peers { for i, p := range peers {
ids[i] = p.ID ids[i] = p.ID
-31
View File
@@ -19,34 +19,3 @@ func Difference(a, b []string) []string {
func ToPtr[T any](value T) *T { func ToPtr[T any](value T) *T {
return &value return &value
} }
type comparableObject[T any] interface {
Equal(other T) bool
}
func MergeUnique[T comparableObject[T]](arr1, arr2 []T) []T {
var result []T
for _, item := range arr1 {
if !contains(result, item) {
result = append(result, item)
}
}
for _, item := range arr2 {
if !contains(result, item) {
result = append(result, item)
}
}
return result
}
func contains[T comparableObject[T]](slice []T, element T) bool {
for _, item := range slice {
if item.Equal(element) {
return true
}
}
return false
}
+19 -41
View File
@@ -11,9 +11,6 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
nbroute "github.com/netbirdio/netbird/route" nbroute "github.com/netbirdio/netbird/route"
"github.com/netbirdio/netbird/shared/management/domain" "github.com/netbirdio/netbird/shared/management/domain"
"github.com/netbirdio/netbird/shared/management/proto" "github.com/netbirdio/netbird/shared/management/proto"
@@ -38,17 +35,17 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents,
Network: decodeAccountNetwork(full.Network), Network: decodeAccountNetwork(full.Network),
AccountSettings: decodeAccountSettings(full.AccountSettings), AccountSettings: decodeAccountSettings(full.AccountSettings),
CustomZoneDomain: full.CustomZoneDomain, CustomZoneDomain: full.CustomZoneDomain,
Peers: make(map[string]*nbpeer.Peer, len(full.Peers)), Peers: make(map[string]*types.ComponentPeer, len(full.Peers)),
Groups: make(map[string]*types.Group, len(full.Groups)), Groups: make(map[string]*types.ComponentGroup, len(full.Groups)),
Policies: make([]*types.Policy, 0, len(full.Policies)), Policies: make([]*types.Policy, 0, len(full.Policies)),
Routes: make([]*nbroute.Route, 0, len(full.Routes)), Routes: make([]*nbroute.Route, 0, len(full.Routes)),
NameServerGroups: make([]*nbdns.NameServerGroup, 0, len(full.NameserverGroups)), NameServerGroups: make([]*nbdns.NameServerGroup, 0, len(full.NameserverGroups)),
AllDNSRecords: decodeSimpleRecords(full.AllDnsRecords), AllDNSRecords: decodeSimpleRecords(full.AllDnsRecords),
AccountZones: decodeCustomZones(full.AccountZones), AccountZones: decodeCustomZones(full.AccountZones),
ResourcePoliciesMap: make(map[string][]*types.Policy), ResourcePoliciesMap: make(map[string][]*types.Policy),
RoutersMap: make(map[string]map[string]*routerTypes.NetworkRouter), RoutersMap: make(map[string]map[string]*types.ComponentRouter),
NetworkResources: make([]*resourceTypes.NetworkResource, 0, len(full.NetworkResources)), NetworkResources: make([]*types.ComponentResource, 0, len(full.NetworkResources)),
RouterPeers: make(map[string]*nbpeer.Peer), RouterPeers: make(map[string]*types.ComponentPeer),
AllowedUserIDs: stringSliceToSet(full.AllowedUserIds), AllowedUserIDs: stringSliceToSet(full.AllowedUserIds),
PostureFailedPeers: make(map[string]map[string]struct{}, len(full.PostureFailedPeers)), PostureFailedPeers: make(map[string]map[string]struct{}, len(full.PostureFailedPeers)),
GroupIDToUserIDs: make(map[string][]string, len(full.GroupIdToUserIds)), GroupIDToUserIDs: make(map[string][]string, len(full.GroupIdToUserIds)),
@@ -101,7 +98,7 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents,
log.WithField("peer idx", idx).Error("unrecognized peer idx during decoding") log.WithField("peer idx", idx).Error("unrecognized peer idx during decoding")
} }
} }
group := &types.Group{ group := &types.ComponentGroup{
ID: groupID, ID: groupID,
PublicID: gc.Id, PublicID: gc.Id,
Peers: peerIDs, Peers: peerIDs,
@@ -151,7 +148,7 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents,
// Phase 7: routers_map (outer key = network seq id, inner key = peer-id // Phase 7: routers_map (outer key = network seq id, inner key = peer-id
// reconstructed from peer_index). Synthesized network id is "net_<seq>". // reconstructed from peer_index). Synthesized network id is "net_<seq>".
for networkID, list := range full.RoutersMap { for networkID, list := range full.RoutersMap {
inner := make(map[string]*routerTypes.NetworkRouter, len(list.Entries)) inner := make(map[string]*types.ComponentRouter, len(list.Entries))
for _, entry := range list.Entries { for _, entry := range list.Entries {
if !entry.PeerIndexSet { if !entry.PeerIndexSet {
continue continue
@@ -161,8 +158,7 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents,
continue continue
} }
peerID := peerIDByIndex[entry.PeerIndex] peerID := peerIDByIndex[entry.PeerIndex]
inner[peerID] = &routerTypes.NetworkRouter{ inner[peerID] = &types.ComponentRouter{
ID: "",
NetworkID: networkID, NetworkID: networkID,
PublicID: entry.Id, PublicID: entry.Id,
Peer: peerID, Peer: peerID,
@@ -264,40 +260,22 @@ func decodeAccountSettings(as *proto.AccountSettingsCompact) *types.AccountSetti
} }
} }
func decodePeerCompact(pc *proto.PeerCompact, peerID string) *nbpeer.Peer { func decodePeerCompact(pc *proto.PeerCompact, peerID string) *types.ComponentPeer {
var caps []int32 peer := &types.ComponentPeer{
if pc.SupportsSourcePrefixes {
caps = append(caps, nbpeer.PeerCapabilitySourcePrefixes)
}
if pc.SupportsIpv6 {
caps = append(caps, nbpeer.PeerCapabilityIPv6Overlay)
}
peer := &nbpeer.Peer{
ID: peerID, ID: peerID,
Key: peerID, Key: peerID,
SSHKey: string(pc.SshPubKey), SSHKey: string(pc.SshPubKey),
SSHEnabled: pc.SshEnabled, SSHEnabled: pc.SshEnabled,
DNSLabel: pc.DnsLabel, DNSLabel: pc.DnsLabel,
LoginExpirationEnabled: pc.LoginExpirationEnabled, LoginExpirationEnabled: pc.LoginExpirationEnabled,
Meta: nbpeer.PeerSystemMeta{ AgentVersion: pc.AgentVersion,
WtVersion: pc.AgentVersion, SupportsSourcePrefixes: pc.SupportsSourcePrefixes,
Capabilities: caps, SupportsIPv6: pc.SupportsIpv6,
Flags: nbpeer.Flags{ ServerSSHAllowed: pc.ServerSshAllowed,
ServerSSHAllowed: pc.ServerSshAllowed, AddedWithSSOLogin: pc.AddedWithSsoLogin,
},
},
}
if pc.AddedWithSsoLogin {
// Set a non-empty UserID so (*Peer).AddedWithSSOLogin() returns true.
// The original UserID isn't on the wire; the value is intentionally
// visibly synthetic so any future consumer that mistakes UserID for a
// real account user xid won't silently match (or worse, write the
// sentinel into a downstream record).
peer.UserID = "<env-sso>"
} }
if pc.LastLoginUnixNano != 0 { if pc.LastLoginUnixNano != 0 {
t := time.Unix(0, pc.LastLoginUnixNano) peer.LastLogin = time.Unix(0, pc.LastLoginUnixNano)
peer.LastLogin = &t
} }
switch len(pc.Ip) { switch len(pc.Ip) {
case 4: case 4:
@@ -424,14 +402,14 @@ func decodeNameServerGroupRaw(nsg *proto.NameServerGroupRaw) *nbdns.NameServerGr
return out return out
} }
func decodeNetworkResource(nr *proto.NetworkResourceRaw) *resourceTypes.NetworkResource { func decodeNetworkResource(nr *proto.NetworkResourceRaw) *types.ComponentResource {
out := &resourceTypes.NetworkResource{ out := &types.ComponentResource{
ID: nr.Id, ID: nr.Id,
PublicID: nr.Id, PublicID: nr.Id,
NetworkID: nr.NetworkSeq, NetworkID: nr.NetworkSeq,
Name: nr.Name, Name: nr.Name,
Description: nr.Description, Description: nr.Description,
Type: resourceTypes.NetworkResourceType(nr.Type), Type: types.ComponentResourceType(nr.Type),
Address: nr.Address, Address: nr.Address,
Domain: nr.DomainValue, Domain: nr.DomainValue,
Enabled: nr.Enabled, Enabled: nr.Enabled,
+3 -4
View File
@@ -20,10 +20,9 @@ import (
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
"net/netip" "net/netip"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/shared/management/types"
nbroute "github.com/netbirdio/netbird/route" nbroute "github.com/netbirdio/netbird/route"
"github.com/netbirdio/netbird/shared/management/proto" "github.com/netbirdio/netbird/shared/management/proto"
"github.com/netbirdio/netbird/shared/management/types"
"github.com/netbirdio/netbird/shared/netiputil" "github.com/netbirdio/netbird/shared/netiputil"
"github.com/netbirdio/netbird/shared/sshauth" "github.com/netbirdio/netbird/shared/sshauth"
) )
@@ -274,7 +273,7 @@ func ToProtocolDNSConfig(update nbdns.Config, cache DNSConfigCache, forwardPort
// AppendRemotePeerConfig appends typed peers as proto.RemotePeerConfig // AppendRemotePeerConfig appends typed peers as proto.RemotePeerConfig
// entries to dst and returns the result. // entries to dst and returns the result.
func AppendRemotePeerConfig(dst []*proto.RemotePeerConfig, peers []*nbpeer.Peer, dnsName string, includeIPv6 bool) []*proto.RemotePeerConfig { func AppendRemotePeerConfig(dst []*proto.RemotePeerConfig, peers []*types.ComponentPeer, dnsName string, includeIPv6 bool) []*proto.RemotePeerConfig {
for _, rPeer := range peers { for _, rPeer := range peers {
allowedIPs := []string{rPeer.IP.String() + "/32"} allowedIPs := []string{rPeer.IP.String() + "/32"}
if includeIPv6 && rPeer.IPv6.IsValid() { if includeIPv6 && rPeer.IPv6.IsValid() {
@@ -285,7 +284,7 @@ func AppendRemotePeerConfig(dst []*proto.RemotePeerConfig, peers []*nbpeer.Peer,
AllowedIps: allowedIPs, AllowedIps: allowedIPs,
SshConfig: &proto.SSHConfig{SshPubKey: []byte(rPeer.SSHKey)}, SshConfig: &proto.SSHConfig{SshPubKey: []byte(rPeer.SSHKey)},
Fqdn: rPeer.FQDN(dnsName), Fqdn: rPeer.FQDN(dnsName),
AgentVersion: rPeer.Meta.WtVersion, AgentVersion: rPeer.AgentVersion,
}) })
} }
return dst return dst
+2 -2
View File
@@ -54,8 +54,8 @@ func EnvelopeToNetworkMap(ctx context.Context, env *proto.NetworkMapEnvelope, lo
} }
components.PeerID = canonicalKey components.PeerID = canonicalKey
includeIPv6 := localPeer.SupportsIPv6() && localPeer.IPv6.IsValid() includeIPv6 := localPeer.SupportsIPv6 && localPeer.IPv6.IsValid()
useSourcePrefixes := localPeer.SupportsSourcePrefixes() useSourcePrefixes := localPeer.SupportsSourcePrefixes
typedNM := components.Calculate(ctx) typedNM := components.Calculate(ctx)
+23 -24
View File
@@ -13,7 +13,6 @@ import (
goproto "google.golang.org/protobuf/proto" goproto "google.golang.org/protobuf/proto"
mgmtgrpc "github.com/netbirdio/netbird/management/internals/shared/grpc" mgmtgrpc "github.com/netbirdio/netbird/management/internals/shared/grpc"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/types" "github.com/netbirdio/netbird/management/server/types"
nbnetworkmap "github.com/netbirdio/netbird/shared/management/networkmap" nbnetworkmap "github.com/netbirdio/netbird/shared/management/networkmap"
"github.com/netbirdio/netbird/shared/management/proto" "github.com/netbirdio/netbird/shared/management/proto"
@@ -144,14 +143,14 @@ func TestDecodeEnvelope_MalformedWgKeyPeerSkipped(t *testing.T) {
func TestEnvelopeRoundTrip_AllGroupShortCircuitParity(t *testing.T) { func TestEnvelopeRoundTrip_AllGroupShortCircuitParity(t *testing.T) {
ctx := context.Background() ctx := context.Background()
peers := map[string]*nbpeer.Peer{} peers := map[string]*types.ComponentPeer{}
for i, id := range []string{"peer-T", "peer-S", "peer-ALL", "peer-O"} { for i, id := range []string{"peer-T", "peer-S", "peer-ALL", "peer-O"} {
peers[id] = &nbpeer.Peer{ peers[id] = &types.ComponentPeer{
ID: id, ID: id,
Key: randomWgKey(t), Key: randomWgKey(t),
IP: netip.AddrFrom4([4]byte{100, 64, 0, byte(i + 1)}), IP: netip.AddrFrom4([4]byte{100, 64, 0, byte(i + 1)}),
DNSLabel: id, DNSLabel: id,
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, AgentVersion: "0.40.0",
} }
} }
@@ -165,7 +164,7 @@ func TestEnvelopeRoundTrip_AllGroupShortCircuitParity(t *testing.T) {
AccountSettings: &types.AccountSettingsInfo{}, AccountSettings: &types.AccountSettingsInfo{},
DNSSettings: &types.DNSSettings{}, DNSSettings: &types.DNSSettings{},
Peers: peers, Peers: peers,
Groups: map[string]*types.Group{ Groups: map[string]*types.ComponentGroup{
"g-src": {ID: "g-src", PublicID: "1", Name: "staff", Peers: []string{"peer-T", "peer-S"}}, "g-src": {ID: "g-src", PublicID: "1", Name: "staff", Peers: []string{"peer-T", "peer-S"}},
"g-all": {ID: "g-all", PublicID: "2", Name: "All", Peers: []string{"peer-ALL"}}, "g-all": {ID: "g-all", PublicID: "2", Name: "All", Peers: []string{"peer-ALL"}},
"g-two": {ID: "g-two", PublicID: "3", Name: "second", Peers: []string{"peer-T", "peer-O"}}, "g-two": {ID: "g-two", PublicID: "3", Name: "second", Peers: []string{"peer-T", "peer-O"}},
@@ -232,22 +231,22 @@ func buildSmokeComponents(t *testing.T) (*types.NetworkMapComponents, string) {
peerAKey := randomWgKey(t) peerAKey := randomWgKey(t)
peerBKey := randomWgKey(t) peerBKey := randomWgKey(t)
peerA := &nbpeer.Peer{ peerA := &types.ComponentPeer{
ID: "peer-A", ID: "peer-A",
Key: peerAKey, Key: peerAKey,
IP: netip.AddrFrom4([4]byte{100, 64, 0, 1}), IP: netip.AddrFrom4([4]byte{100, 64, 0, 1}),
DNSLabel: "peerA", DNSLabel: "peerA",
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, AgentVersion: "0.40.0",
} }
peerB := &nbpeer.Peer{ peerB := &types.ComponentPeer{
ID: "peer-B", ID: "peer-B",
Key: peerBKey, Key: peerBKey,
IP: netip.AddrFrom4([4]byte{100, 64, 0, 2}), IP: netip.AddrFrom4([4]byte{100, 64, 0, 2}),
DNSLabel: "peerB", DNSLabel: "peerB",
Meta: nbpeer.PeerSystemMeta{WtVersion: "0.40.0"}, AgentVersion: "0.40.0",
} }
group := &types.Group{ group := &types.ComponentGroup{
ID: "group-all", PublicID: "1", Name: "All", ID: "group-all", PublicID: "1", Name: "All",
Peers: []string{"peer-A", "peer-B"}, Peers: []string{"peer-A", "peer-B"},
} }
@@ -274,11 +273,11 @@ func buildSmokeComponents(t *testing.T) (*types.NetworkMapComponents, string) {
}, },
AccountSettings: &types.AccountSettingsInfo{}, AccountSettings: &types.AccountSettingsInfo{},
DNSSettings: &types.DNSSettings{}, DNSSettings: &types.DNSSettings{},
Peers: map[string]*nbpeer.Peer{ Peers: map[string]*types.ComponentPeer{
"peer-A": peerA, "peer-A": peerA,
"peer-B": peerB, "peer-B": peerB,
}, },
Groups: map[string]*types.Group{ Groups: map[string]*types.ComponentGroup{
"group-all": group, "group-all": group,
}, },
Policies: []*types.Policy{policy}, Policies: []*types.Policy{policy},
+103
View File
@@ -0,0 +1,103 @@
package types
import (
"net/netip"
"time"
)
// ComponentPeer is the self-contained peer representation used by
// NetworkMapComponents and the calculated NetworkMap. It carries exactly the
// subset of peer data that crosses the components wire format, so the shared
// calculation layer stays independent of the management server's domain
// types.
type ComponentPeer struct {
ID string
Key string
IP netip.Addr
IPv6 netip.Addr
DNSLabel string
SSHKey string
SSHEnabled bool
ServerSSHAllowed bool
AgentVersion string
SupportsSourcePrefixes bool
SupportsIPv6 bool
LoginExpirationEnabled bool
AddedWithSSOLogin bool
LastLogin time.Time
}
// FQDN returns the peer's FQDN combined of the peer's DNS label and the system's DNS domain.
func (p *ComponentPeer) FQDN(dnsDomain string) string {
if dnsDomain == "" {
return ""
}
return p.DNSLabel + "." + dnsDomain
}
// LoginExpired indicates whether the peer's login has expired, mirroring the
// server-side peer semantics: only SSO-added peers with login expiration
// enabled can expire.
func (p *ComponentPeer) LoginExpired(expiresIn time.Duration) (bool, time.Duration) {
if !p.AddedWithSSOLogin || !p.LoginExpirationEnabled {
return false, 0
}
timeLeft := time.Until(p.LastLogin.Add(expiresIn))
return timeLeft <= 0, timeLeft
}
// GroupAllName is the reserved name of the default group that contains every peer in an account.
const GroupAllName = "All"
// ComponentGroup is the self-contained group representation used by
// NetworkMapComponents: just the membership view the network-map calculation
// needs, without the server's storage fields.
type ComponentGroup struct {
ID string
PublicID string
Name string
Peers []string
}
// IsGroupAll checks if the group is a default "All" group.
func (g *ComponentGroup) IsGroupAll() bool {
return g.Name == GroupAllName
}
// ComponentRouter is the self-contained network-router representation used by
// NetworkMapComponents.
type ComponentRouter struct {
NetworkID string
PublicID string
Peer string
PeerGroups []string
Masquerade bool
Metric int
Enabled bool
}
// ComponentResourceType mirrors the network-resource type enum on the
// components wire format.
type ComponentResourceType string
const (
ComponentResourceHost ComponentResourceType = "host"
ComponentResourceSubnet ComponentResourceType = "subnet"
ComponentResourceDomain ComponentResourceType = "domain"
)
// ComponentResource is the self-contained network-resource representation
// used by NetworkMapComponents.
type ComponentResource struct {
ID string
PublicID string
NetworkID string
AccountID string
Name string
Description string
Type ComponentResourceType
Address string
Domain string
Prefix netip.Prefix
Enabled bool
}
+6 -8
View File
@@ -3,8 +3,6 @@ package types
import ( import (
"strconv" "strconv"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/posture"
"github.com/netbirdio/netbird/version" "github.com/netbirdio/netbird/version"
) )
@@ -48,8 +46,8 @@ func portsIncludesSSH(ports []string) bool {
} }
// ExpandPortsAndRanges expands Ports and PortRanges of a rule into individual firewall rules. // ExpandPortsAndRanges expands Ports and PortRanges of a rule into individual firewall rules.
func ExpandPortsAndRanges(base FirewallRule, rule *PolicyRule, peer *nbpeer.Peer) []*FirewallRule { func ExpandPortsAndRanges(base FirewallRule, rule *PolicyRule, peer *ComponentPeer) []*FirewallRule {
features := peerSupportedFirewallFeatures(peer.Meta.WtVersion) features := peerSupportedFirewallFeatures(peer.AgentVersion)
var expanded []*FirewallRule var expanded []*FirewallRule
@@ -106,8 +104,8 @@ func isPortInRule(portString string, portInt uint16, rule *FirewallRule) bool {
return rule.Port == portString || (rule.PortRange.Start <= portInt && portInt <= rule.PortRange.End) return rule.Port == portString || (rule.PortRange.Start <= portInt && portInt <= rule.PortRange.End)
} }
func shouldCheckRulesForNativeSSH(supportsNative bool, rule *PolicyRule, peer *nbpeer.Peer) bool { func shouldCheckRulesForNativeSSH(supportsNative bool, rule *PolicyRule, peer *ComponentPeer) bool {
return supportsNative && peer.SSHEnabled && peer.Meta.Flags.ServerSSHAllowed && rule.Protocol == PolicyRuleProtocolTCP return supportsNative && peer.SSHEnabled && peer.ServerSSHAllowed && rule.Protocol == PolicyRuleProtocolTCP
} }
func peerSupportedFirewallFeatures(peerVer string) supportedFeatures { func peerSupportedFirewallFeatures(peerVer string) supportedFeatures {
@@ -117,13 +115,13 @@ func peerSupportedFirewallFeatures(peerVer string) supportedFeatures {
var features supportedFeatures var features supportedFeatures
meetMinVer, err := posture.MeetsMinVersion(firewallRuleMinNativeSSHVer, peerVer) meetMinVer, err := version.MeetsMinVersion(firewallRuleMinNativeSSHVer, peerVer)
features.nativeSSH = err == nil && meetMinVer features.nativeSSH = err == nil && meetMinVer
if features.nativeSSH { if features.nativeSSH {
features.portRanges = true features.portRanges = true
} else { } else {
meetMinVer, err = posture.MeetsMinVersion(firewallRuleMinPortRangesVer, peerVer) meetMinVer, err = version.MeetsMinVersion(firewallRuleMinPortRangesVer, peerVer)
features.portRanges = err == nil && meetMinVer features.portRanges = err == nil && meetMinVer
} }
+2 -3
View File
@@ -9,7 +9,6 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
nbroute "github.com/netbirdio/netbird/route" nbroute "github.com/netbirdio/netbird/route"
) )
@@ -51,7 +50,7 @@ func (r *FirewallRule) Equal(other *FirewallRule) bool {
// For static routes, source ranges match the destination family (v4 or v6). // For static routes, source ranges match the destination family (v4 or v6).
// For dynamic routes (domain-based), separate v4 and v6 rules are generated // For dynamic routes (domain-based), separate v4 and v6 rules are generated
// so the routing peer's forwarding chain allows both address families. // so the routing peer's forwarding chain allows both address families.
func GenerateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule *PolicyRule, groupPeers []*nbpeer.Peer, direction int, includeIPv6 bool) []*RouteFirewallRule { func GenerateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule *PolicyRule, groupPeers []*ComponentPeer, direction int, includeIPv6 bool) []*RouteFirewallRule {
rulesExists := make(map[string]struct{}) rulesExists := make(map[string]struct{})
rules := make([]*RouteFirewallRule, 0) rules := make([]*RouteFirewallRule, 0)
@@ -107,7 +106,7 @@ func GenerateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule
} }
// splitPeerSourcesByFamily separates peer IPs into v4 (/32) and v6 (/128) source ranges. // splitPeerSourcesByFamily separates peer IPs into v4 (/32) and v6 (/128) source ranges.
func splitPeerSourcesByFamily(groupPeers []*nbpeer.Peer) (v4, v6 []string) { func splitPeerSourcesByFamily(groupPeers []*ComponentPeer) (v4, v6 []string) {
v4 = make([]string, 0, len(groupPeers)) v4 = make([]string, 0, len(groupPeers))
v6 = make([]string, 0, len(groupPeers)) v6 = make([]string, 0, len(groupPeers))
for _, peer := range groupPeers { for _, peer := range groupPeers {
@@ -8,13 +8,12 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/route" "github.com/netbirdio/netbird/route"
"github.com/netbirdio/netbird/shared/management/domain" "github.com/netbirdio/netbird/shared/management/domain"
) )
func TestSplitPeerSourcesByFamily(t *testing.T) { func TestSplitPeerSourcesByFamily(t *testing.T) {
peers := []*nbpeer.Peer{ peers := []*ComponentPeer{
{ {
IP: netip.MustParseAddr("100.64.0.1"), IP: netip.MustParseAddr("100.64.0.1"),
IPv6: netip.MustParseAddr("fd00::1"), IPv6: netip.MustParseAddr("fd00::1"),
@@ -36,7 +35,7 @@ func TestSplitPeerSourcesByFamily(t *testing.T) {
} }
func TestGenerateRouteFirewallRules_V4Route(t *testing.T) { func TestGenerateRouteFirewallRules_V4Route(t *testing.T) {
peers := []*nbpeer.Peer{ peers := []*ComponentPeer{
{ {
IP: netip.MustParseAddr("100.64.0.1"), IP: netip.MustParseAddr("100.64.0.1"),
IPv6: netip.MustParseAddr("fd00::1"), IPv6: netip.MustParseAddr("fd00::1"),
@@ -65,7 +64,7 @@ func TestGenerateRouteFirewallRules_V4Route(t *testing.T) {
} }
func TestGenerateRouteFirewallRules_V6Route(t *testing.T) { func TestGenerateRouteFirewallRules_V6Route(t *testing.T) {
peers := []*nbpeer.Peer{ peers := []*ComponentPeer{
{ {
IP: netip.MustParseAddr("100.64.0.1"), IP: netip.MustParseAddr("100.64.0.1"),
IPv6: netip.MustParseAddr("fd00::1"), IPv6: netip.MustParseAddr("fd00::1"),
@@ -93,7 +92,7 @@ func TestGenerateRouteFirewallRules_V6Route(t *testing.T) {
} }
func TestGenerateRouteFirewallRules_DynamicRoute_DualStack(t *testing.T) { func TestGenerateRouteFirewallRules_DynamicRoute_DualStack(t *testing.T) {
peers := []*nbpeer.Peer{ peers := []*ComponentPeer{
{ {
IP: netip.MustParseAddr("100.64.0.1"), IP: netip.MustParseAddr("100.64.0.1"),
IPv6: netip.MustParseAddr("fd00::1"), IPv6: netip.MustParseAddr("fd00::1"),
@@ -126,7 +125,7 @@ func TestGenerateRouteFirewallRules_DynamicRoute_DualStack(t *testing.T) {
} }
func TestGenerateRouteFirewallRules_DynamicRoute_NoV6Peers(t *testing.T) { func TestGenerateRouteFirewallRules_DynamicRoute_NoV6Peers(t *testing.T) {
peers := []*nbpeer.Peer{ peers := []*ComponentPeer{
{IP: netip.MustParseAddr("100.64.0.1")}, {IP: netip.MustParseAddr("100.64.0.1")},
{IP: netip.MustParseAddr("100.64.0.2")}, {IP: netip.MustParseAddr("100.64.0.2")},
} }
@@ -150,7 +149,7 @@ func TestGenerateRouteFirewallRules_DynamicRoute_NoV6Peers(t *testing.T) {
} }
func TestGenerateRouteFirewallRules_IncludeIPv6False(t *testing.T) { func TestGenerateRouteFirewallRules_IncludeIPv6False(t *testing.T) {
peers := []*nbpeer.Peer{ peers := []*ComponentPeer{
{ {
IP: netip.MustParseAddr("100.64.0.1"), IP: netip.MustParseAddr("100.64.0.1"),
IPv6: netip.MustParseAddr("fd00::1"), IPv6: netip.MustParseAddr("fd00::1"),
+39 -10
View File
@@ -15,8 +15,6 @@ import (
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/util"
"github.com/netbirdio/netbird/route" "github.com/netbirdio/netbird/route"
"github.com/netbirdio/netbird/shared/management/proto" "github.com/netbirdio/netbird/shared/management/proto"
"github.com/netbirdio/netbird/shared/management/status" "github.com/netbirdio/netbird/shared/management/status"
@@ -39,11 +37,11 @@ const (
) )
type NetworkMap struct { type NetworkMap struct {
Peers []*nbpeer.Peer Peers []*ComponentPeer
Network *Network Network *Network
Routes []*route.Route Routes []*route.Route
DNSConfig nbdns.Config DNSConfig nbdns.Config
OfflinePeers []*nbpeer.Peer OfflinePeers []*ComponentPeer
FirewallRules []*FirewallRule FirewallRules []*FirewallRule
RoutesFirewallRules []*RouteFirewallRule RoutesFirewallRules []*RouteFirewallRule
ForwardingRules []*ForwardingRule ForwardingRules []*ForwardingRule
@@ -53,15 +51,46 @@ type NetworkMap struct {
func (nm *NetworkMap) Merge(other *NetworkMap) { func (nm *NetworkMap) Merge(other *NetworkMap) {
nm.Peers = mergeUniquePeersByID(nm.Peers, other.Peers) nm.Peers = mergeUniquePeersByID(nm.Peers, other.Peers)
nm.Routes = util.MergeUnique(nm.Routes, other.Routes) nm.Routes = mergeUnique(nm.Routes, other.Routes)
nm.OfflinePeers = mergeUniquePeersByID(nm.OfflinePeers, other.OfflinePeers) nm.OfflinePeers = mergeUniquePeersByID(nm.OfflinePeers, other.OfflinePeers)
nm.FirewallRules = util.MergeUnique(nm.FirewallRules, other.FirewallRules) nm.FirewallRules = mergeUnique(nm.FirewallRules, other.FirewallRules)
nm.RoutesFirewallRules = util.MergeUnique(nm.RoutesFirewallRules, other.RoutesFirewallRules) nm.RoutesFirewallRules = mergeUnique(nm.RoutesFirewallRules, other.RoutesFirewallRules)
nm.ForwardingRules = util.MergeUnique(nm.ForwardingRules, other.ForwardingRules) nm.ForwardingRules = mergeUnique(nm.ForwardingRules, other.ForwardingRules)
} }
func mergeUniquePeersByID(peers1, peers2 []*nbpeer.Peer) []*nbpeer.Peer { type comparableObject[T any] interface {
result := make(map[string]*nbpeer.Peer) Equal(other T) bool
}
func mergeUnique[T comparableObject[T]](arr1, arr2 []T) []T {
var result []T
for _, item := range arr1 {
if !containsEqual(result, item) {
result = append(result, item)
}
}
for _, item := range arr2 {
if !containsEqual(result, item) {
result = append(result, item)
}
}
return result
}
func containsEqual[T comparableObject[T]](slice []T, element T) bool {
for _, item := range slice {
if item.Equal(element) {
return true
}
}
return false
}
func mergeUniquePeersByID(peers1, peers2 []*ComponentPeer) []*ComponentPeer {
result := make(map[string]*ComponentPeer)
for _, peer := range peers1 { for _, peer := range peers1 {
result[peer.ID] = peer result[peer.ID] = peer
} }
@@ -1,4 +1,4 @@
package util package types
import ( import (
"testing" "testing"
@@ -17,7 +17,7 @@ func (t testObject) Equal(other testObject) bool {
func Test_MergeUniqueArraysWithoutDuplicates(t *testing.T) { func Test_MergeUniqueArraysWithoutDuplicates(t *testing.T) {
arr1 := []testObject{{value: 1}, {value: 2}} arr1 := []testObject{{value: 1}, {value: 2}}
arr2 := []testObject{{value: 2}, {value: 3}} arr2 := []testObject{{value: 2}, {value: 3}}
result := MergeUnique(arr1, arr2) result := mergeUnique(arr1, arr2)
assert.Len(t, result, 3) assert.Len(t, result, 3)
assert.Contains(t, result, testObject{value: 1}) assert.Contains(t, result, testObject{value: 1})
assert.Contains(t, result, testObject{value: 2}) assert.Contains(t, result, testObject{value: 2})
@@ -27,14 +27,14 @@ func Test_MergeUniqueArraysWithoutDuplicates(t *testing.T) {
func Test_MergeUniqueHandlesEmptyArrays(t *testing.T) { func Test_MergeUniqueHandlesEmptyArrays(t *testing.T) {
arr1 := []testObject{} arr1 := []testObject{}
arr2 := []testObject{} arr2 := []testObject{}
result := MergeUnique(arr1, arr2) result := mergeUnique(arr1, arr2)
assert.Empty(t, result) assert.Empty(t, result)
} }
func Test_MergeUniqueHandlesOneEmptyArray(t *testing.T) { func Test_MergeUniqueHandlesOneEmptyArray(t *testing.T) {
arr1 := []testObject{{value: 1}, {value: 2}} arr1 := []testObject{{value: 1}, {value: 2}}
arr2 := []testObject{} arr2 := []testObject{}
result := MergeUnique(arr1, arr2) result := mergeUnique(arr1, arr2)
assert.Len(t, result, 2) assert.Len(t, result, 2)
assert.Contains(t, result, testObject{value: 1}) assert.Contains(t, result, testObject{value: 1})
assert.Contains(t, result, testObject{value: 2}) assert.Contains(t, result, testObject{value: 2})
@@ -12,9 +12,6 @@ import (
"github.com/netbirdio/netbird/client/ssh/auth" "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"
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/route" "github.com/netbirdio/netbird/route"
"github.com/netbirdio/netbird/shared/management/domain" "github.com/netbirdio/netbird/shared/management/domain"
) )
@@ -27,22 +24,22 @@ type NetworkMapComponents struct {
DNSSettings *DNSSettings DNSSettings *DNSSettings
CustomZoneDomain string CustomZoneDomain string
Peers map[string]*nbpeer.Peer Peers map[string]*ComponentPeer
Groups map[string]*Group Groups map[string]*ComponentGroup
Policies []*Policy Policies []*Policy
Routes []*route.Route Routes []*route.Route
NameServerGroups []*nbdns.NameServerGroup NameServerGroups []*nbdns.NameServerGroup
AllDNSRecords []nbdns.SimpleRecord AllDNSRecords []nbdns.SimpleRecord
AccountZones []nbdns.CustomZone AccountZones []nbdns.CustomZone
ResourcePoliciesMap map[string][]*Policy ResourcePoliciesMap map[string][]*Policy
RoutersMap map[string]map[string]*routerTypes.NetworkRouter RoutersMap map[string]map[string]*ComponentRouter
NetworkResources []*resourceTypes.NetworkResource NetworkResources []*ComponentResource
GroupIDToUserIDs map[string][]string GroupIDToUserIDs map[string][]string
AllowedUserIDs map[string]struct{} AllowedUserIDs map[string]struct{}
PostureFailedPeers map[string]map[string]struct{} PostureFailedPeers map[string]map[string]struct{}
RouterPeers map[string]*nbpeer.Peer RouterPeers map[string]*ComponentPeer
// NetworkXIDToPublicID maps Network.ID (xid) → PublicID. // NetworkXIDToPublicID maps Network.ID (xid) → PublicID.
// Consumed by the envelope encoder to // Consumed by the envelope encoder to
@@ -78,15 +75,15 @@ func EmptyNetworkMapComponents(nm *NetworkMapComponents) *NetworkMapComponents {
return nm return nm
} }
func (c *NetworkMapComponents) GetPeerInfo(peerID string) *nbpeer.Peer { func (c *NetworkMapComponents) GetPeerInfo(peerID string) *ComponentPeer {
return c.Peers[peerID] return c.Peers[peerID]
} }
func (c *NetworkMapComponents) GetRouterPeerInfo(peerID string) *nbpeer.Peer { func (c *NetworkMapComponents) GetRouterPeerInfo(peerID string) *ComponentPeer {
return c.RouterPeers[peerID] return c.RouterPeers[peerID]
} }
func (c *NetworkMapComponents) GetGroupInfo(groupID string) *Group { func (c *NetworkMapComponents) GetGroupInfo(groupID string) *ComponentGroup {
return c.Groups[groupID] return c.Groups[groupID]
} }
@@ -142,7 +139,7 @@ func (c *NetworkMapComponents) Calculate(ctx context.Context) *NetworkMap {
includeIPv6 := false includeIPv6 := false
if p := c.Peers[targetPeerID]; p != nil { if p := c.Peers[targetPeerID]; p != nil {
includeIPv6 = p.SupportsIPv6() && p.IPv6.IsValid() includeIPv6 = p.SupportsIPv6 && p.IPv6.IsValid()
} }
routesUpdate := filterAndExpandRoutes(c.getRoutesToSync(targetPeerID, peersToConnect, peerGroups), includeIPv6) routesUpdate := filterAndExpandRoutes(c.getRoutesToSync(targetPeerID, peersToConnect, peerGroups), includeIPv6)
routesFirewallRules := c.getPeerRoutesFirewallRules(ctx, targetPeerID, includeIPv6) routesFirewallRules := c.getPeerRoutesFirewallRules(ctx, targetPeerID, includeIPv6)
@@ -200,7 +197,7 @@ func (c *NetworkMapComponents) IsEmpty() bool {
return c.empty return c.empty
} }
func (c *NetworkMapComponents) getPeerConnectionResources(targetPeerID string) ([]*nbpeer.Peer, []*FirewallRule, map[string]map[string]struct{}, bool) { func (c *NetworkMapComponents) getPeerConnectionResources(targetPeerID string) ([]*ComponentPeer, []*FirewallRule, map[string]map[string]struct{}, bool) {
targetPeer := c.GetPeerInfo(targetPeerID) targetPeer := c.GetPeerInfo(targetPeerID)
if targetPeer == nil { if targetPeer == nil {
return nil, nil, nil, false return nil, nil, nil, false
@@ -220,7 +217,7 @@ func (c *NetworkMapComponents) getPeerConnectionResources(targetPeerID string) (
continue continue
} }
var sourcePeers, destinationPeers []*nbpeer.Peer var sourcePeers, destinationPeers []*ComponentPeer
var peerInSources, peerInDestinations bool var peerInSources, peerInDestinations bool
if rule.SourceResource.Type == ResourceTypePeer && rule.SourceResource.ID != "" { if rule.SourceResource.Type == ResourceTypePeer && rule.SourceResource.ID != "" {
@@ -303,13 +300,13 @@ func (c *NetworkMapComponents) getAllowedUserIDs() map[string]struct{} {
return make(map[string]struct{}) return make(map[string]struct{})
} }
func (c *NetworkMapComponents) connResourcesGenerator(targetPeer *nbpeer.Peer) (func(*PolicyRule, []*nbpeer.Peer, int), func() ([]*nbpeer.Peer, []*FirewallRule)) { func (c *NetworkMapComponents) connResourcesGenerator(targetPeer *ComponentPeer) (func(*PolicyRule, []*ComponentPeer, int), func() ([]*ComponentPeer, []*FirewallRule)) {
rulesExists := make(map[string]struct{}) rulesExists := make(map[string]struct{})
peersExists := make(map[string]struct{}) peersExists := make(map[string]struct{})
rules := make([]*FirewallRule, 0) rules := make([]*FirewallRule, 0)
peers := make([]*nbpeer.Peer, 0) peers := make([]*ComponentPeer, 0)
return func(rule *PolicyRule, groupPeers []*nbpeer.Peer, direction int) { return func(rule *PolicyRule, groupPeers []*ComponentPeer, direction int) {
protocol := rule.Protocol protocol := rule.Protocol
if protocol == PolicyRuleProtocolNetbirdSSH { if protocol == PolicyRuleProtocolNetbirdSSH {
protocol = PolicyRuleProtocolTCP protocol = PolicyRuleProtocolTCP
@@ -361,15 +358,15 @@ func (c *NetworkMapComponents) connResourcesGenerator(targetPeer *nbpeer.Peer) (
PortsJoined: portsJoined, PortsJoined: portsJoined,
}) })
} }
}, func() ([]*nbpeer.Peer, []*FirewallRule) { }, func() ([]*ComponentPeer, []*FirewallRule) {
return peers, rules return peers, rules
} }
} }
func (c *NetworkMapComponents) getAllPeersFromGroups(groups []string, peerID string, sourcePostureChecksIDs []string) ([]*nbpeer.Peer, bool) { func (c *NetworkMapComponents) getAllPeersFromGroups(groups []string, peerID string, sourcePostureChecksIDs []string) ([]*ComponentPeer, bool) {
peerInGroups := false peerInGroups := false
uniquePeerIDs := c.getUniquePeerIDsFromGroupsIDs(groups) uniquePeerIDs := c.getUniquePeerIDsFromGroupsIDs(groups)
filteredPeers := make([]*nbpeer.Peer, 0, len(uniquePeerIDs)) filteredPeers := make([]*ComponentPeer, 0, len(uniquePeerIDs))
for _, p := range uniquePeerIDs { for _, p := range uniquePeerIDs {
peerInfo := c.GetPeerInfo(p) peerInfo := c.GetPeerInfo(p)
@@ -421,22 +418,22 @@ func (c *NetworkMapComponents) getUniquePeerIDsFromGroupsIDs(groups []string) []
return ids return ids
} }
func (c *NetworkMapComponents) getPeerFromResource(resource Resource, peerID string) ([]*nbpeer.Peer, bool) { func (c *NetworkMapComponents) getPeerFromResource(resource Resource, peerID string) ([]*ComponentPeer, bool) {
if resource.ID == peerID { if resource.ID == peerID {
return []*nbpeer.Peer{}, true return []*ComponentPeer{}, true
} }
peerInfo := c.GetPeerInfo(resource.ID) peerInfo := c.GetPeerInfo(resource.ID)
if peerInfo == nil { if peerInfo == nil {
return []*nbpeer.Peer{}, false return []*ComponentPeer{}, false
} }
return []*nbpeer.Peer{peerInfo}, false return []*ComponentPeer{peerInfo}, false
} }
func (c *NetworkMapComponents) filterPeersByLoginExpiration(aclPeers []*nbpeer.Peer) ([]*nbpeer.Peer, []*nbpeer.Peer) { func (c *NetworkMapComponents) filterPeersByLoginExpiration(aclPeers []*ComponentPeer) ([]*ComponentPeer, []*ComponentPeer) {
peersToConnect := make([]*nbpeer.Peer, 0, len(aclPeers)) peersToConnect := make([]*ComponentPeer, 0, len(aclPeers))
var expiredPeers []*nbpeer.Peer var expiredPeers []*ComponentPeer
for _, p := range aclPeers { for _, p := range aclPeers {
expired, _ := p.LoginExpired(c.AccountSettings.PeerLoginExpiration) expired, _ := p.LoginExpired(c.AccountSettings.PeerLoginExpiration)
@@ -518,7 +515,7 @@ func filterAndExpandRoutes(routes []*route.Route, includeIPv6 bool) []*route.Rou
return filtered return filtered
} }
func (c *NetworkMapComponents) getRoutesToSync(peerID string, aclPeers []*nbpeer.Peer, peerGroups LookupMap) []*route.Route { func (c *NetworkMapComponents) getRoutesToSync(peerID string, aclPeers []*ComponentPeer, peerGroups LookupMap) []*route.Route {
routes, peerDisabledRoutes := c.getRoutingPeerRoutes(peerID) routes, peerDisabledRoutes := c.getRoutingPeerRoutes(peerID)
peerRoutesMembership := make(LookupMap) peerRoutesMembership := make(LookupMap)
for _, r := range append(routes, peerDisabledRoutes...) { for _, r := range append(routes, peerDisabledRoutes...) {
@@ -732,7 +729,7 @@ func (c *NetworkMapComponents) getRouteFirewallRules(ctx context.Context, peerID
return fwRules return fwRules
} }
func (c *NetworkMapComponents) getRulePeers(rule *PolicyRule, postureChecks []string, peerID string, distributionPeers map[string]struct{}) []*nbpeer.Peer { func (c *NetworkMapComponents) getRulePeers(rule *PolicyRule, postureChecks []string, peerID string, distributionPeers map[string]struct{}) []*ComponentPeer {
distPeersWithPolicy := make(map[string]struct{}) distPeersWithPolicy := make(map[string]struct{})
for _, id := range rule.Sources { for _, id := range rule.Sources {
group := c.GetGroupInfo(id) group := c.GetGroupInfo(id)
@@ -759,7 +756,7 @@ func (c *NetworkMapComponents) getRulePeers(rule *PolicyRule, postureChecks []st
} }
} }
distributionGroupPeers := make([]*nbpeer.Peer, 0, len(distPeersWithPolicy)) distributionGroupPeers := make([]*ComponentPeer, 0, len(distPeersWithPolicy))
for pID := range distPeersWithPolicy { for pID := range distPeersWithPolicy {
peerInfo := c.GetPeerInfo(pID) peerInfo := c.GetPeerInfo(pID)
if peerInfo == nil { if peerInfo == nil {
@@ -799,8 +796,8 @@ func (c *NetworkMapComponents) getNetworkResourcesRoutesToSync(peerID string) (b
func (c *NetworkMapComponents) processResourcePolicies( func (c *NetworkMapComponents) processResourcePolicies(
peerID string, peerID string,
resource *resourceTypes.NetworkResource, resource *ComponentResource,
networkRoutingPeers map[string]*routerTypes.NetworkRouter, networkRoutingPeers map[string]*ComponentRouter,
addSourcePeers bool, addSourcePeers bool,
allSourcePeers map[string]struct{}, allSourcePeers map[string]struct{},
) []*route.Route { ) []*route.Route {
@@ -833,7 +830,7 @@ func (c *NetworkMapComponents) getResourcePolicyPeers(policy *Policy) []string {
return c.getUniquePeerIDsFromGroupsIDs(policy.SourceGroups()) return c.getUniquePeerIDsFromGroupsIDs(policy.SourceGroups())
} }
func (c *NetworkMapComponents) getNetworkResourcesRoutes(resource *resourceTypes.NetworkResource, peerID string, router *routerTypes.NetworkRouter) []*route.Route { func (c *NetworkMapComponents) getNetworkResourcesRoutes(resource *ComponentResource, peerID string, router *ComponentRouter) []*route.Route {
resourceAppliedPolicies := c.ResourcePoliciesMap[resource.ID] resourceAppliedPolicies := c.ResourcePoliciesMap[resource.ID]
var routes []*route.Route var routes []*route.Route
@@ -847,7 +844,7 @@ func (c *NetworkMapComponents) getNetworkResourcesRoutes(resource *resourceTypes
return routes return routes
} }
func (c *NetworkMapComponents) networkResourceToRoute(resource *resourceTypes.NetworkResource, peer *nbpeer.Peer, router *routerTypes.NetworkRouter) *route.Route { func (c *NetworkMapComponents) networkResourceToRoute(resource *ComponentResource, peer *ComponentPeer, router *ComponentRouter) *route.Route {
r := &route.Route{ r := &route.Route{
ID: route.ID(resource.ID + ":" + peer.ID), ID: route.ID(resource.ID + ":" + peer.ID),
AccountID: resource.AccountID, AccountID: resource.AccountID,
@@ -861,7 +858,7 @@ func (c *NetworkMapComponents) networkResourceToRoute(resource *resourceTypes.Ne
Description: resource.Description, Description: resource.Description,
} }
if resource.Type == resourceTypes.Host || resource.Type == resourceTypes.Subnet { if resource.Type == ComponentResourceHost || resource.Type == ComponentResourceSubnet {
r.Network = resource.Prefix r.Network = resource.Prefix
r.NetworkType = route.IPv4Network r.NetworkType = route.IPv4Network
@@ -870,7 +867,7 @@ func (c *NetworkMapComponents) networkResourceToRoute(resource *resourceTypes.Ne
} }
} }
if resource.Type == resourceTypes.Domain { if resource.Type == ComponentResourceDomain {
domainList, err := domain.FromStringList([]string{resource.Domain}) domainList, err := domain.FromStringList([]string{resource.Domain})
if err == nil { if err == nil {
r.Domains = domainList r.Domains = domainList
@@ -948,11 +945,11 @@ func (c *NetworkMapComponents) getPoliciesSourcePeers(policies []*Policy) map[st
func (c *NetworkMapComponents) addNetworksRoutingPeers( func (c *NetworkMapComponents) addNetworksRoutingPeers(
networkResourcesRoutes []*route.Route, networkResourcesRoutes []*route.Route,
peerID string, peerID string,
peersToConnect []*nbpeer.Peer, peersToConnect []*ComponentPeer,
expiredPeers []*nbpeer.Peer, expiredPeers []*ComponentPeer,
isRouter bool, isRouter bool,
sourcePeers map[string]struct{}, sourcePeers map[string]struct{},
) []*nbpeer.Peer { ) []*ComponentPeer {
networkRoutesPeers := make(map[string]struct{}, len(networkResourcesRoutes)) networkRoutesPeers := make(map[string]struct{}, len(networkResourcesRoutes))
for _, r := range networkResourcesRoutes { for _, r := range networkResourcesRoutes {
@@ -1002,8 +999,8 @@ type FirewallRuleContext struct {
PortsJoined string PortsJoined string
} }
func AppendIPv6FirewallRule(rules []*FirewallRule, rulesExists map[string]struct{}, peer, targetPeer *nbpeer.Peer, rule *PolicyRule, rc FirewallRuleContext) []*FirewallRule { func AppendIPv6FirewallRule(rules []*FirewallRule, rulesExists map[string]struct{}, peer, targetPeer *ComponentPeer, rule *PolicyRule, rc FirewallRuleContext) []*FirewallRule {
if !peer.IPv6.IsValid() || !targetPeer.SupportsIPv6() || !targetPeer.IPv6.IsValid() { if !peer.IPv6.IsValid() || !targetPeer.SupportsIPv6 || !targetPeer.IPv6.IsValid() {
return rules return rules
} }
@@ -2,9 +2,6 @@ package types
import ( import (
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/route" "github.com/netbirdio/netbird/route"
) )
@@ -21,7 +18,7 @@ type NetworkMapComponentsCompact struct {
DNSSettings *DNSSettings DNSSettings *DNSSettings
CustomZoneDomain string CustomZoneDomain string
AllPeers []*nbpeer.Peer AllPeers []*ComponentPeer
PeerIndexes []int PeerIndexes []int
RouterPeerIndexes []int RouterPeerIndexes []int
@@ -34,8 +31,8 @@ type NetworkMapComponentsCompact struct {
AllDNSRecords []nbdns.SimpleRecord AllDNSRecords []nbdns.SimpleRecord
AccountZones []nbdns.CustomZone AccountZones []nbdns.CustomZone
RoutersMap map[string]map[string]*routerTypes.NetworkRouter RoutersMap map[string]map[string]*ComponentRouter
NetworkResources []*resourceTypes.NetworkResource NetworkResources []*ComponentResource
GroupIDToUserIDs map[string][]string GroupIDToUserIDs map[string][]string
AllowedUserIDs map[string]struct{} AllowedUserIDs map[string]struct{}
@@ -44,7 +41,7 @@ type NetworkMapComponentsCompact struct {
func (c *NetworkMapComponents) ToCompact() *NetworkMapComponentsCompact { func (c *NetworkMapComponents) ToCompact() *NetworkMapComponentsCompact {
peerToIndex := make(map[string]int) peerToIndex := make(map[string]int)
var allPeers []*nbpeer.Peer var allPeers []*ComponentPeer
for id, peer := range c.Peers { for id, peer := range c.Peers {
if _, exists := peerToIndex[id]; !exists { if _, exists := peerToIndex[id]; !exists {
@@ -150,7 +147,7 @@ func (c *NetworkMapComponents) ToCompact() *NetworkMapComponentsCompact {
} }
func (c *NetworkMapComponentsCompact) ToFull() *NetworkMapComponents { func (c *NetworkMapComponentsCompact) ToFull() *NetworkMapComponents {
peers := make(map[string]*nbpeer.Peer, len(c.PeerIndexes)) peers := make(map[string]*ComponentPeer, len(c.PeerIndexes))
for _, idx := range c.PeerIndexes { for _, idx := range c.PeerIndexes {
if idx >= 0 && idx < len(c.AllPeers) { if idx >= 0 && idx < len(c.AllPeers) {
peer := c.AllPeers[idx] peer := c.AllPeers[idx]
@@ -158,7 +155,7 @@ func (c *NetworkMapComponentsCompact) ToFull() *NetworkMapComponents {
} }
} }
routerPeers := make(map[string]*nbpeer.Peer, len(c.RouterPeerIndexes)) routerPeers := make(map[string]*ComponentPeer, len(c.RouterPeerIndexes))
for _, idx := range c.RouterPeerIndexes { for _, idx := range c.RouterPeerIndexes {
if idx >= 0 && idx < len(c.AllPeers) { if idx >= 0 && idx < len(c.AllPeers) {
peer := c.AllPeers[idx] peer := c.AllPeers[idx]
@@ -166,7 +163,7 @@ func (c *NetworkMapComponentsCompact) ToFull() *NetworkMapComponents {
} }
} }
groups := make(map[string]*Group, len(c.Groups)) groups := make(map[string]*ComponentGroup, len(c.Groups))
for id, gc := range c.Groups { for id, gc := range c.Groups {
peerIDs := make([]string, 0, len(gc.PeerIndexes)) peerIDs := make([]string, 0, len(gc.PeerIndexes))
for _, idx := range gc.PeerIndexes { for _, idx := range gc.PeerIndexes {
@@ -174,7 +171,7 @@ func (c *NetworkMapComponentsCompact) ToFull() *NetworkMapComponents {
peerIDs = append(peerIDs, c.AllPeers[idx].ID) peerIDs = append(peerIDs, c.AllPeers[idx].ID)
} }
} }
groups[id] = &Group{ groups[id] = &ComponentGroup{
ID: id, ID: id,
Name: gc.Name, Name: gc.Name,
Peers: peerIDs, Peers: peerIDs,
+24
View File
@@ -71,6 +71,30 @@ func NetbirdCommit() string {
return revision return revision
} }
// sanitizeVersion removes anything after the pre-release tag (e.g., "-dev", "-alpha", etc.)
func sanitizeVersion(version string) string {
parts := strings.Split(version, "-")
return parts[0]
}
// MeetsMinVersion checks if the peer's version meets or exceeds the minimum required version
func MeetsMinVersion(minVer, peerVer string) (bool, error) {
peerVer = sanitizeVersion(peerVer)
minVer = sanitizeVersion(minVer)
peerNBVer, err := v.NewVersion(peerVer)
if err != nil {
return false, err
}
constraints, err := v.NewConstraint(">= " + minVer)
if err != nil {
return false, err
}
return constraints.Check(peerNBVer), nil
}
// IsDevelopmentVersion reports whether the given version string identifies // IsDevelopmentVersion reports whether the given version string identifies
// a non-release / development build. It is the single source of truth for // a non-release / development build. It is the single source of truth for
// "is this a dev build" checks across the codebase; use it instead of // "is this a dev build" checks across the codebase; use it instead of
+70 -1
View File
@@ -1,6 +1,10 @@
package version package version
import "testing" import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsDevelopmentVersion(t *testing.T) { func TestIsDevelopmentVersion(t *testing.T) {
tests := []struct { tests := []struct {
@@ -26,3 +30,68 @@ func TestIsDevelopmentVersion(t *testing.T) {
}) })
} }
} }
func TestMeetsMinVersion(t *testing.T) {
tests := []struct {
name string
minVer string
peerVer string
want bool
wantErr bool
}{
{
name: "Peer version greater than min version",
minVer: "0.26.0",
peerVer: "0.60.1",
want: true,
wantErr: false,
},
{
name: "Peer version equals min version",
minVer: "1.0.0",
peerVer: "1.0.0",
want: true,
wantErr: false,
},
{
name: "Peer version less than min version",
minVer: "1.0.0",
peerVer: "0.9.9",
want: false,
wantErr: false,
},
{
name: "Peer version with pre-release tag greater than min version",
minVer: "1.0.0",
peerVer: "1.0.1-alpha",
want: true,
wantErr: false,
},
{
name: "Invalid peer version format",
minVer: "1.0.0",
peerVer: "dev",
want: false,
wantErr: true,
},
{
name: "Invalid min version format",
minVer: "invalid.version",
peerVer: "1.0.0",
want: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := MeetsMinVersion(tt.minVer, tt.peerVer)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.want, got)
})
}
}