mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-24 16:41:30 +02:00
Merge remote-tracking branch 'origin/revert/component-types' into revert/component-types
Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
This commit is contained in:
@@ -372,6 +372,8 @@ func (c *Controller) sendUpdatesFromData(ctx context.Context, accountID string,
|
||||
return fmt.Errorf("failed to get flow enabled status: %v", err)
|
||||
}
|
||||
|
||||
nmData.PrecomputePostureValidation()
|
||||
|
||||
dnsCache := &cache.DNSConfigCache{}
|
||||
dnsDomain := c.getDNSDomainFromData(nmData.AccountSettings)
|
||||
peersCustomZone := networkmap.PeersCustomZone(ctx, accountID, dnsDomain, nmData.Peers, ipv6AllowedPeersFromData(nmData))
|
||||
|
||||
@@ -465,6 +465,13 @@ func (nmd *NetworkMapData) validatePostureChecksOnPeerGetFailed(sourcePostureChe
|
||||
}
|
||||
|
||||
for _, postureChecksID := range sourcePostureChecksID {
|
||||
if valid, cached := nmd.cachedPostureCheckResult(postureChecksID, peerID); cached {
|
||||
if !valid {
|
||||
return false, postureChecksID
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
postureChecks := nmd.PostureChecks[postureChecksID]
|
||||
if postureChecks == nil {
|
||||
continue
|
||||
@@ -476,6 +483,76 @@ func (nmd *NetworkMapData) validatePostureChecksOnPeerGetFailed(sourcePostureChe
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (nmd *NetworkMapData) PrecomputePostureValidation() {
|
||||
if len(nmd.PostureChecks) == 0 {
|
||||
nmd.PostureValidation = nil
|
||||
return
|
||||
}
|
||||
|
||||
checkPeerIDs := make(map[string]map[string]struct{})
|
||||
for _, policy := range nmd.Policies {
|
||||
if policy == nil || !policy.Enabled || len(policy.SourcePostureChecks) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
groupPeerIDs := nmd.getUniquePeerIDsFromGroupsIDs(policy.SourceGroups())
|
||||
for _, postureChecksID := range policy.SourcePostureChecks {
|
||||
set := checkPeerIDs[postureChecksID]
|
||||
if set == nil {
|
||||
set = make(map[string]struct{}, len(groupPeerIDs))
|
||||
checkPeerIDs[postureChecksID] = set
|
||||
}
|
||||
for _, pid := range groupPeerIDs {
|
||||
set[pid] = struct{}{}
|
||||
}
|
||||
for _, rule := range policy.Rules {
|
||||
if rule == nil {
|
||||
continue
|
||||
}
|
||||
if rule.SourceResource.Type == string(types.ResourceTypePeer) && rule.SourceResource.ID != "" {
|
||||
set[rule.SourceResource.ID] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results := make(map[string]map[string]bool, len(checkPeerIDs))
|
||||
for postureChecksID, peerIDs := range checkPeerIDs {
|
||||
results[postureChecksID] = nmd.evaluatePostureChecksForPeers(postureChecksID, peerIDs)
|
||||
}
|
||||
nmd.PostureValidation = results
|
||||
}
|
||||
|
||||
func (nmd *NetworkMapData) evaluatePostureChecksForPeers(postureChecksID string, peerIDs map[string]struct{}) map[string]bool {
|
||||
postureChecks := nmd.PostureChecks[postureChecksID]
|
||||
if postureChecks == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
checks := postureChecks.GetChecks()
|
||||
results := make(map[string]bool, len(peerIDs))
|
||||
for peerID := range peerIDs {
|
||||
peer := nmd.Peers[peerID]
|
||||
if peer == nil {
|
||||
continue
|
||||
}
|
||||
results[peerID] = nmdata.PassesChecks(checks, peer)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func (nmd *NetworkMapData) cachedPostureCheckResult(postureChecksID, peerID string) (bool, bool) {
|
||||
results, ok := nmd.PostureValidation[postureChecksID]
|
||||
if !ok {
|
||||
return false, false
|
||||
}
|
||||
if results == nil {
|
||||
return true, true
|
||||
}
|
||||
valid, found := results[peerID]
|
||||
return valid, found
|
||||
}
|
||||
|
||||
func (nmd *NetworkMapData) getPostureValidPeersSaveFailed(inputPeers []string, postureChecksIDs []string, postureFailedPeers *map[string]map[string]struct{}) []string {
|
||||
var dest []string
|
||||
for _, peerID := range inputPeers {
|
||||
|
||||
@@ -1475,6 +1475,126 @@ func TestGetPeerNetworkMapComponents_StoreImmutableAndDeterministic(t *testing.T
|
||||
assert.Equal(t, first.GroupIDToUserIDs, second.GroupIDToUserIDs)
|
||||
}
|
||||
|
||||
func TestPrecomputePostureValidation(t *testing.T) {
|
||||
newFixture := func() *networkmap.NetworkMapData {
|
||||
target := newPeer(targetID, 1)
|
||||
srcPass := newPeer("peer-src-pass", 2)
|
||||
srcFail := newPeer("peer-src-fail", 3)
|
||||
srcFail.Meta.WtVersion = failingVersion
|
||||
other := newPeer("peer-other", 4)
|
||||
other.Meta.WtVersion = failingVersion
|
||||
|
||||
nmd := newNMD(target, srcPass, srcFail, other)
|
||||
addVersionCheck(nmd, "pc-1", postureMinVersion)
|
||||
addGroup(nmd, "g-src", srcPass.ID, srcFail.ID)
|
||||
addGroup(nmd, "g-dst", targetID)
|
||||
addGroup(nmd, "g-open", srcPass.ID, srcFail.ID, other.ID)
|
||||
|
||||
checked := newPolicy("p-checked", newRule([]string{"g-src"}, []string{"g-dst"}))
|
||||
checked.SourcePostureChecks = []string{"pc-1"}
|
||||
open := newPolicy("p-open", newRule([]string{"g-open"}, []string{"g-dst"}))
|
||||
disabled := newPolicy("p-disabled", newRule([]string{"g-open"}, []string{"g-dst"}))
|
||||
disabled.Enabled = false
|
||||
disabled.SourcePostureChecks = []string{"pc-1"}
|
||||
nmd.Policies = []*nmdata.Policy{checked, open, disabled}
|
||||
|
||||
return nmd
|
||||
}
|
||||
|
||||
type snapshot struct {
|
||||
peers []string
|
||||
postureFailedPeers map[string]map[string]struct{}
|
||||
}
|
||||
snapshotAll := func(nmd *networkmap.NetworkMapData) map[string]snapshot {
|
||||
out := make(map[string]snapshot, len(nmd.Peers))
|
||||
for peerID := range nmd.Peers {
|
||||
c := compute(nmd, peerID)
|
||||
out[peerID] = snapshot{peers: peerIDSet(c.Peers), postureFailedPeers: c.PostureFailedPeers}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
t.Run("memoized results match direct evaluation", func(t *testing.T) {
|
||||
nmd := newFixture()
|
||||
direct := snapshotAll(nmd)
|
||||
|
||||
nmd.PrecomputePostureValidation()
|
||||
memoized := snapshotAll(nmd)
|
||||
|
||||
require.Len(t, memoized, len(direct))
|
||||
for peerID, want := range direct {
|
||||
assert.ElementsMatch(t, want.peers, memoized[peerID].peers, "visible peers changed for %s", peerID)
|
||||
assert.Equal(t, want.postureFailedPeers, memoized[peerID].postureFailedPeers, "posture failures changed for %s", peerID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("only source peers of enabled checked policies are evaluated", func(t *testing.T) {
|
||||
nmd := newFixture()
|
||||
nmd.PrecomputePostureValidation()
|
||||
|
||||
assert.Equal(t, map[string]map[string]bool{
|
||||
"pc-1": {"peer-src-pass": true, "peer-src-fail": false},
|
||||
}, nmd.PostureValidation)
|
||||
})
|
||||
|
||||
t.Run("peer source resources are evaluated", func(t *testing.T) {
|
||||
nmd := newFixture()
|
||||
resourcePolicy := newPolicy("p-resource", newRule(nil, []string{"g-dst"}))
|
||||
resourcePolicy.Rules[0].SourceResource = nmdata.Resource{ID: "peer-other", Type: string(nbtypes.ResourceTypePeer)}
|
||||
resourcePolicy.SourcePostureChecks = []string{"pc-1"}
|
||||
nmd.Policies = append(nmd.Policies, resourcePolicy)
|
||||
|
||||
nmd.PrecomputePostureValidation()
|
||||
|
||||
assert.Equal(t, map[string]bool{"peer-src-pass": true, "peer-src-fail": false, "peer-other": false},
|
||||
nmd.PostureValidation["pc-1"])
|
||||
})
|
||||
|
||||
t.Run("memoized result wins over direct evaluation", func(t *testing.T) {
|
||||
nmd := newFixture()
|
||||
nmd.PostureValidation = map[string]map[string]bool{
|
||||
"pc-1": {"peer-src-pass": false, "peer-src-fail": true},
|
||||
}
|
||||
|
||||
c := compute(nmd, targetID)
|
||||
|
||||
assert.Equal(t, map[string]map[string]struct{}{"pc-1": {"peer-src-pass": {}}}, c.PostureFailedPeers)
|
||||
})
|
||||
|
||||
t.Run("no posture checks clears the memo", func(t *testing.T) {
|
||||
nmd := newFixture()
|
||||
nmd.PrecomputePostureValidation()
|
||||
require.NotEmpty(t, nmd.PostureValidation)
|
||||
|
||||
nmd.PostureChecks = nil
|
||||
nmd.PrecomputePostureValidation()
|
||||
|
||||
assert.Nil(t, nmd.PostureValidation)
|
||||
})
|
||||
|
||||
t.Run("unresolvable check id memoized as passing", func(t *testing.T) {
|
||||
nmd := newFixture()
|
||||
nmd.Policies[0].SourcePostureChecks = []string{"pc-ghost"}
|
||||
nmd.PrecomputePostureValidation()
|
||||
|
||||
require.Contains(t, nmd.PostureValidation, "pc-ghost")
|
||||
assert.Nil(t, nmd.PostureValidation["pc-ghost"])
|
||||
|
||||
c := compute(nmd, targetID)
|
||||
assert.ElementsMatch(t, []string{targetID, "peer-src-pass", "peer-src-fail", "peer-other"}, peerIDSet(c.Peers))
|
||||
assert.Empty(t, c.PostureFailedPeers)
|
||||
})
|
||||
|
||||
t.Run("peers missing from the memo fall back to direct evaluation", func(t *testing.T) {
|
||||
nmd := newFixture()
|
||||
nmd.PostureValidation = map[string]map[string]bool{"pc-1": {"peer-src-pass": true}}
|
||||
|
||||
c := compute(nmd, targetID)
|
||||
|
||||
assert.Equal(t, map[string]map[string]struct{}{"pc-1": {"peer-src-fail": {}}}, c.PostureFailedPeers)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNetworkMapData_GetPeerGroups(t *testing.T) {
|
||||
target := newPeer(targetID, 1)
|
||||
other := newPeer("peer-other", 2)
|
||||
|
||||
@@ -25,6 +25,12 @@ type NetworkMapData struct { //nolint:revive // established name across the code
|
||||
|
||||
PostureChecks map[string]*nmdata.PostureChecks
|
||||
|
||||
// PostureValidation holds the precomputed posture-check results, keyed by
|
||||
// posture check ID then peer ID. Filled by PrecomputePostureValidation; a
|
||||
// present but nil inner map marks a check ID that resolves to no posture
|
||||
// check, which the calc treats as passing.
|
||||
PostureValidation map[string]map[string]bool
|
||||
|
||||
AllowedUserIDs map[string]struct{}
|
||||
NetworkXIDToPublicID map[string]string
|
||||
PostureCheckXIDToPublicID map[string]string
|
||||
|
||||
@@ -20,7 +20,9 @@ type ChecksDefinition struct {
|
||||
ProcessCheck *ProcessCheck
|
||||
}
|
||||
|
||||
type postureCheck interface {
|
||||
// Check is the slim twin of posture.Check. It is sealed: only the check types
|
||||
// in this package implement it.
|
||||
type Check interface {
|
||||
check(peer *Peer) (bool, error)
|
||||
}
|
||||
|
||||
@@ -28,7 +30,13 @@ type postureCheck interface {
|
||||
// mirrors the server posture path: a check returning (false, _) — including on
|
||||
// an evaluation error — fails the bundle.
|
||||
func (pc *PostureChecks) Passes(peer *Peer) bool {
|
||||
for _, c := range pc.GetChecks() {
|
||||
return PassesChecks(pc.GetChecks(), peer)
|
||||
}
|
||||
|
||||
// PassesChecks is Passes over an already built check set, for callers that
|
||||
// evaluate many peers against the same bundle.
|
||||
func PassesChecks(checks []Check, peer *Peer) bool {
|
||||
for _, c := range checks {
|
||||
valid, _ := c.check(peer)
|
||||
if !valid {
|
||||
return false
|
||||
@@ -38,8 +46,8 @@ func (pc *PostureChecks) Passes(peer *Peer) bool {
|
||||
}
|
||||
|
||||
// GetChecks returns the initialized checks in the same order as posture.Checks.GetChecks.
|
||||
func (pc *PostureChecks) GetChecks() []postureCheck {
|
||||
var checks []postureCheck
|
||||
func (pc *PostureChecks) GetChecks() []Check {
|
||||
var checks []Check
|
||||
if pc.Checks.NBVersionCheck != nil {
|
||||
checks = append(checks, pc.Checks.NBVersionCheck)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user