mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-29 02:51:29 +02:00
do not send resource policies map over the wire
Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
This commit is contained in:
@@ -81,6 +81,7 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel
|
||||
enc := newComponentEncoder(c)
|
||||
enc.indexAllPeers()
|
||||
routerIdxs := enc.indexRouterPeers(c.RouterPeers)
|
||||
enc.indexAllNetworkResources()
|
||||
|
||||
// Phase 2: gather every policy that any consumer references (peer-pair
|
||||
// policies + resource-only policies) so encodeResourcePoliciesMap can
|
||||
@@ -102,7 +103,6 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel
|
||||
DnsSettings: enc.encodeDNSSettings(c.DNSSettings),
|
||||
DnsDomain: in.DNSDomain,
|
||||
CustomZoneDomain: c.CustomZoneDomain,
|
||||
AgentVersions: enc.agentVersions,
|
||||
Peers: enc.peers,
|
||||
RouterPeerIndexes: routerIdxs,
|
||||
Policies: policies,
|
||||
@@ -140,16 +140,15 @@ type componentEncoder struct {
|
||||
peerOrder map[string]uint32
|
||||
peers []*proto.PeerCompact
|
||||
|
||||
agentVersionOrder map[string]uint32
|
||||
agentVersions []string
|
||||
networkIdToPublicId map[string]string
|
||||
}
|
||||
|
||||
func newComponentEncoder(c *types.NetworkMapComponents) *componentEncoder {
|
||||
return &componentEncoder{
|
||||
components: c,
|
||||
peerOrder: make(map[string]uint32, len(c.Peers)),
|
||||
peers: make([]*proto.PeerCompact, 0, len(c.Peers)),
|
||||
agentVersionOrder: make(map[string]uint32),
|
||||
components: c,
|
||||
peerOrder: make(map[string]uint32, len(c.Peers)),
|
||||
peers: make([]*proto.PeerCompact, 0, len(c.Peers)),
|
||||
networkIdToPublicId: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +189,15 @@ func (e *componentEncoder) indexRouterPeers(routers map[string]*nmdata.Peer) []u
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *componentEncoder) indexAllNetworkResources() {
|
||||
for _, r := range e.components.NetworkResources {
|
||||
if !r.Enabled {
|
||||
continue
|
||||
}
|
||||
e.networkIdToPublicId[r.ID] = r.PublicID
|
||||
}
|
||||
}
|
||||
|
||||
func (e *componentEncoder) encodeGroups() []*proto.GroupCompact {
|
||||
if len(e.components.Groups) == 0 {
|
||||
return nil
|
||||
@@ -203,10 +211,20 @@ func (e *componentEncoder) encodeGroups() []*proto.GroupCompact {
|
||||
peerIdxs = append(peerIdxs, idx)
|
||||
}
|
||||
}
|
||||
|
||||
groupCompactResources := func() []*proto.ResourceCompact {
|
||||
var toret []*proto.ResourceCompact
|
||||
for _, r := range g.Resources {
|
||||
toret = append(toret, e.resourceToProto(r))
|
||||
}
|
||||
return toret
|
||||
}
|
||||
|
||||
out = append(out, &proto.GroupCompact{
|
||||
Id: g.PublicID,
|
||||
PeerIndexes: peerIdxs,
|
||||
IsAll: g.IsGroupAll(),
|
||||
Resources: groupCompactResources(),
|
||||
})
|
||||
}
|
||||
return out
|
||||
@@ -343,17 +361,30 @@ func (e *componentEncoder) groupPublicXid(groupID string) (string, bool) {
|
||||
// today (Calculate's resource-typed rule path consults SourceResource only
|
||||
// for "peer" — other types fall through to group-based lookup).
|
||||
func (e *componentEncoder) resourceToProto(r nmdata.Resource) *proto.ResourceCompact {
|
||||
if r.ID == "" && r.Type == "" {
|
||||
t, ok := proto.ResourceCompactType_value[string(r.Type)]
|
||||
if !ok || t == 0 || r.ID == "" {
|
||||
return nil
|
||||
}
|
||||
out := &proto.ResourceCompact{Type: r.Type}
|
||||
if r.Type == string(types.ResourceTypePeer) && r.ID != "" {
|
||||
if idx, ok := e.peerOrder[r.ID]; ok {
|
||||
out.PeerIndexSet = true
|
||||
out.PeerIndex = idx
|
||||
if t == int32(proto.ResourceCompactType_peer) {
|
||||
idx, ok := e.peerOrder[r.ID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return &proto.ResourceCompact{
|
||||
Type: proto.ResourceCompactType_peer,
|
||||
ResourceId: &proto.ResourceCompact_PeerIndex{PeerIndex: idx},
|
||||
}
|
||||
}
|
||||
return out
|
||||
|
||||
publicID, ok := e.networkIdToPublicId[r.ID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &proto.ResourceCompact{
|
||||
Type: proto.ResourceCompactType(t),
|
||||
ResourceId: &proto.ResourceCompact_Id{Id: publicID},
|
||||
}
|
||||
}
|
||||
|
||||
// postureCheckSeqs translates a slice of posture-check xids to their
|
||||
|
||||
@@ -304,6 +304,31 @@ func TestEncodeNetworkMapEnvelope_GroupsByAccountPublicId(t *testing.T) {
|
||||
assert.Len(t, groupByID["2"].PeerIndexes, 2)
|
||||
}
|
||||
|
||||
func TestEncodePolicy(t *testing.T) {
|
||||
encoder := componentEncoder{peerOrder: map[string]uint32{"peerId": uint32(1234)}, networkIdToPublicId: map[string]string{"domain": "publicDomain", "host": "publicHost", "subnet": "publicSubnet"}}
|
||||
assert.Equal(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "peer", ID: "peerId"}),
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_peer, ResourceId: &proto.ResourceCompact_PeerIndex{PeerIndex: uint32(1234)}})
|
||||
// verify invalid peer id results in nil
|
||||
assert.Nil(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "peer", ID: "boom"}))
|
||||
assert.Equal(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "domain", ID: "domain"}),
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_domain, ResourceId: &proto.ResourceCompact_Id{Id: "publicDomain"}})
|
||||
assert.Equal(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "host", ID: "host"}),
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_host, ResourceId: &proto.ResourceCompact_Id{Id: "publicHost"}})
|
||||
assert.Equal(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "subnet", ID: "subnet"}),
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_subnet, ResourceId: &proto.ResourceCompact_Id{Id: "publicSubnet"}})
|
||||
// verify invalid resource type results in nil
|
||||
assert.Nil(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "boom", ID: "boom"}))
|
||||
// verify invalid networkresource id results in nil
|
||||
assert.Nil(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "host", ID: "boom"}))
|
||||
}
|
||||
|
||||
func TestEncodeNetworkMapEnvelope_PolicyExpansion(t *testing.T) {
|
||||
c := newTestComponents()
|
||||
|
||||
@@ -562,19 +587,8 @@ func TestEncodeNetworkMapEnvelope_ResourceOnlyPolicyShippedAndIndexed(t *testing
|
||||
require.Len(t, full.Policies, 2, "encoded policies must include both peer-traffic and resource-only")
|
||||
|
||||
policyByID := map[string]*proto.PolicyCompact{}
|
||||
policyIds := make([]string, 0)
|
||||
for _, p := range full.Policies {
|
||||
policyByID[p.Id] = p
|
||||
policyIds = append(policyIds, p.Id)
|
||||
}
|
||||
require.Contains(t, policyByID, "10", "original peer-traffic policy id 10")
|
||||
require.Contains(t, policyByID, "99", "resource-only policy id 99")
|
||||
|
||||
require.Contains(t, full.ResourcePoliciesMap, "77")
|
||||
ids := full.ResourcePoliciesMap["77"].Ids
|
||||
require.Len(t, ids, 2)
|
||||
assert.ElementsMatch(t, policyIds, ids,
|
||||
"resource policies map must reference both wire policy indexes")
|
||||
}
|
||||
|
||||
func TestEncodeNetworkMapEnvelope_NameServerGroups(t *testing.T) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -98,9 +99,21 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents,
|
||||
log.WithField("peer idx", idx).Error("unrecognized peer idx during decoding")
|
||||
}
|
||||
}
|
||||
|
||||
fromCompactResources := func() []nmdata.Resource {
|
||||
var toret []nmdata.Resource
|
||||
|
||||
for _, r := range gc.Resources {
|
||||
toret = append(toret, resourceFromProto(r, peerIDByIndex))
|
||||
}
|
||||
|
||||
return toret
|
||||
}
|
||||
|
||||
group := &nmdata.Group{
|
||||
PublicID: gc.Id,
|
||||
Peers: peerIDs,
|
||||
PublicID: gc.Id,
|
||||
Peers: peerIDs,
|
||||
Resources: fromCompactResources(),
|
||||
}
|
||||
if gc.IsAll {
|
||||
group.Name = types.GroupAllName
|
||||
@@ -190,6 +203,15 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents,
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 8: rebuild resource_policies_map
|
||||
for _, r := range c.NetworkResources {
|
||||
policies := policiesForNetworkResource(r.ID, c.Policies, c.Groups)
|
||||
if len(policies) == 0 {
|
||||
continue
|
||||
}
|
||||
c.ResourcePoliciesMap[r.ID] = policies
|
||||
}
|
||||
|
||||
// Phase 9: group_id_to_user_ids — wire keys are seq ids, synth to strings.
|
||||
for groupId, list := range full.GroupIdToUserIds {
|
||||
c.GroupIDToUserIDs[groupId] = append([]string(nil), list.UserIds...)
|
||||
@@ -225,6 +247,43 @@ func DecodeEnvelope(env *proto.NetworkMapEnvelope) (*types.NetworkMapComponents,
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func networkResourceGroups(resourceId string, groups map[string]*nmdata.Group) []string {
|
||||
var toret []string
|
||||
for _, group := range groups {
|
||||
for _, resource := range group.Resources {
|
||||
if resource.ID == resourceId {
|
||||
toret = append(toret, group.PublicID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return toret
|
||||
}
|
||||
|
||||
func policiesForNetworkResource(resourceId string, allPolicies []*nmdata.Policy, groups map[string]*nmdata.Group) []*nmdata.Policy {
|
||||
var toret []*nmdata.Policy
|
||||
|
||||
networkResourceGroups := networkResourceGroups(resourceId, groups)
|
||||
for _, p := range allPolicies {
|
||||
if p == nil || !p.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
// there's always only one rule in each policy
|
||||
if p.Rules[0].DestinationResource.ID == resourceId {
|
||||
toret = append(toret, p)
|
||||
continue
|
||||
}
|
||||
for _, groupId := range networkResourceGroups {
|
||||
if slices.Contains(p.Rules[0].Destinations, groupId) {
|
||||
toret = append(toret, p)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return toret
|
||||
}
|
||||
|
||||
func decodeAccountNetwork(an *proto.AccountNetwork) *nmdata.Network {
|
||||
if an == nil {
|
||||
return nil
|
||||
@@ -341,11 +400,27 @@ func resourceFromProto(r *proto.ResourceCompact, peerIDByIndex []string) nmdata.
|
||||
if r == nil {
|
||||
return nmdata.Resource{}
|
||||
}
|
||||
out := nmdata.Resource{Type: r.Type}
|
||||
if r.PeerIndexSet && int(r.PeerIndex) < len(peerIDByIndex) {
|
||||
out.ID = peerIDByIndex[r.PeerIndex]
|
||||
|
||||
t, ok := proto.ResourceCompactType_name[int32(r.Type)]
|
||||
if !ok || r.Type == proto.ResourceCompactType_unknown_type {
|
||||
return nmdata.Resource{}
|
||||
}
|
||||
|
||||
if r.Type == proto.ResourceCompactType_peer && int(r.GetPeerIndex()) >= len(peerIDByIndex) {
|
||||
return nmdata.Resource{}
|
||||
}
|
||||
|
||||
if r.Type == proto.ResourceCompactType_peer && int(r.GetPeerIndex()) < len(peerIDByIndex) {
|
||||
return nmdata.Resource{
|
||||
Type: "peer",
|
||||
ID: peerIDByIndex[int(r.GetPeerIndex())],
|
||||
}
|
||||
}
|
||||
|
||||
return nmdata.Resource{
|
||||
Type: t,
|
||||
ID: r.GetId(),
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// authorizedGroupsFromProto inverts encodeAuthorizedGroups: the wire form
|
||||
|
||||
40
shared/management/networkmap/decode_test.go
Normal file
40
shared/management/networkmap/decode_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package networkmap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDecodePolicy(t *testing.T) {
|
||||
assert.Equal(t,
|
||||
resourceFromProto(
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_peer, ResourceId: &proto.ResourceCompact_PeerIndex{PeerIndex: uint32(1)}},
|
||||
[]string{"invalid-id-0", "valid-id", "invalid-id-2"}),
|
||||
nmdata.Resource{Type: "peer", ID: "valid-id"})
|
||||
// check invalid peer index returns an empty resource
|
||||
assert.Equal(t,
|
||||
resourceFromProto(
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_peer, ResourceId: &proto.ResourceCompact_PeerIndex{PeerIndex: uint32(100)}},
|
||||
[]string{"invalid-id-0", "valid-id", "invalid-id-2"}),
|
||||
nmdata.Resource{})
|
||||
assert.Equal(t,
|
||||
resourceFromProto(
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_domain, ResourceId: &proto.ResourceCompact_Id{Id: "domain"}}, []string{}),
|
||||
nmdata.Resource{Type: "domain", ID: "domain"})
|
||||
assert.Equal(t,
|
||||
resourceFromProto(
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_host, ResourceId: &proto.ResourceCompact_Id{Id: "host"}}, []string{}),
|
||||
nmdata.Resource{Type: "host", ID: "host"})
|
||||
assert.Equal(t,
|
||||
resourceFromProto(
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_subnet, ResourceId: &proto.ResourceCompact_Id{Id: "subnet"}}, []string{}),
|
||||
nmdata.Resource{Type: "subnet", ID: "subnet"})
|
||||
// an unknown resource type return an empty resource
|
||||
assert.Equal(t,
|
||||
resourceFromProto(
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_unknown_type, ResourceId: &proto.ResourceCompact_Id{Id: "boom"}}, []string{}),
|
||||
nmdata.Resource{})
|
||||
}
|
||||
@@ -6,9 +6,10 @@ const groupAllName = "All"
|
||||
|
||||
// Group is the slim twin of types.Group.
|
||||
type Group struct {
|
||||
Name string
|
||||
PublicID string
|
||||
Peers []string
|
||||
Name string
|
||||
PublicID string
|
||||
Peers []string
|
||||
Resources []Resource
|
||||
}
|
||||
|
||||
func (g *Group) IsGroupAll() bool {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1066,16 +1066,23 @@ message PolicyCompact {
|
||||
repeated string source_posture_check_ids = 13;
|
||||
}
|
||||
|
||||
enum ResourceCompactType {
|
||||
unknown_type = 0; //placeholder
|
||||
peer = 1;
|
||||
domain = 2;
|
||||
host = 3;
|
||||
subnet = 4;
|
||||
}
|
||||
|
||||
// ResourceCompact mirrors types.Resource. Used by PolicyCompact to carry
|
||||
// rule.SourceResource / rule.DestinationResource when the rule targets a
|
||||
// specific resource (typically a peer) rather than groups.
|
||||
// peer_index_set tells whether peer_index is valid (proto3 uint32 cannot
|
||||
// disambiguate "0" from "unset"); set only when type == "peer".
|
||||
message ResourceCompact {
|
||||
string type = 1;
|
||||
bool peer_index_set = 2;
|
||||
uint32 peer_index = 3;
|
||||
reserved 4; // future: host/subnet/domain references when needed
|
||||
ResourceCompactType type = 1;
|
||||
oneof resource_id {
|
||||
string id = 2; // for domain/host/subnet resources
|
||||
uint32 peer_index = 3; // for peers
|
||||
}
|
||||
}
|
||||
|
||||
// UserNameList is a list of local-user names — used as the value type in
|
||||
@@ -1099,6 +1106,8 @@ message GroupCompact {
|
||||
// groups lose that property and the two sides expand policy
|
||||
// destinations differently.
|
||||
bool is_all = 3;
|
||||
|
||||
repeated ResourceCompact resources = 4;
|
||||
}
|
||||
|
||||
// DNSSettingsCompact mirrors types.DNSSettings.
|
||||
|
||||
Reference in New Issue
Block a user