mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-26 14:29:08 +01: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)
|
return fmt.Errorf("failed to get flow enabled status: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nmData.PrecomputePostureValidation()
|
||||||
|
|
||||||
dnsCache := &cache.DNSConfigCache{}
|
dnsCache := &cache.DNSConfigCache{}
|
||||||
dnsDomain := c.getDNSDomainFromData(nmData.AccountSettings)
|
dnsDomain := c.getDNSDomainFromData(nmData.AccountSettings)
|
||||||
peersCustomZone := networkmap.PeersCustomZone(ctx, accountID, dnsDomain, nmData.Peers, ipv6AllowedPeersFromData(nmData))
|
peersCustomZone := networkmap.PeersCustomZone(ctx, accountID, dnsDomain, nmData.Peers, ipv6AllowedPeersFromData(nmData))
|
||||||
|
|||||||
@@ -465,6 +465,13 @@ func (nmd *NetworkMapData) validatePostureChecksOnPeerGetFailed(sourcePostureChe
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, postureChecksID := range sourcePostureChecksID {
|
for _, postureChecksID := range sourcePostureChecksID {
|
||||||
|
if valid, cached := nmd.cachedPostureCheckResult(postureChecksID, peerID); cached {
|
||||||
|
if !valid {
|
||||||
|
return false, postureChecksID
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
postureChecks := nmd.PostureChecks[postureChecksID]
|
postureChecks := nmd.PostureChecks[postureChecksID]
|
||||||
if postureChecks == nil {
|
if postureChecks == nil {
|
||||||
continue
|
continue
|
||||||
@@ -476,6 +483,76 @@ func (nmd *NetworkMapData) validatePostureChecksOnPeerGetFailed(sourcePostureChe
|
|||||||
return true, ""
|
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 {
|
func (nmd *NetworkMapData) getPostureValidPeersSaveFailed(inputPeers []string, postureChecksIDs []string, postureFailedPeers *map[string]map[string]struct{}) []string {
|
||||||
var dest []string
|
var dest []string
|
||||||
for _, peerID := range inputPeers {
|
for _, peerID := range inputPeers {
|
||||||
|
|||||||
@@ -1475,6 +1475,126 @@ func TestGetPeerNetworkMapComponents_StoreImmutableAndDeterministic(t *testing.T
|
|||||||
assert.Equal(t, first.GroupIDToUserIDs, second.GroupIDToUserIDs)
|
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) {
|
func TestNetworkMapData_GetPeerGroups(t *testing.T) {
|
||||||
target := newPeer(targetID, 1)
|
target := newPeer(targetID, 1)
|
||||||
other := newPeer("peer-other", 2)
|
other := newPeer("peer-other", 2)
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ type NetworkMapData struct { //nolint:revive // established name across the code
|
|||||||
|
|
||||||
PostureChecks map[string]*nmdata.PostureChecks
|
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{}
|
AllowedUserIDs map[string]struct{}
|
||||||
NetworkXIDToPublicID map[string]string
|
NetworkXIDToPublicID map[string]string
|
||||||
PostureCheckXIDToPublicID map[string]string
|
PostureCheckXIDToPublicID map[string]string
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ type ChecksDefinition struct {
|
|||||||
ProcessCheck *ProcessCheck
|
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)
|
check(peer *Peer) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +30,13 @@ type postureCheck interface {
|
|||||||
// mirrors the server posture path: a check returning (false, _) — including on
|
// mirrors the server posture path: a check returning (false, _) — including on
|
||||||
// an evaluation error — fails the bundle.
|
// an evaluation error — fails the bundle.
|
||||||
func (pc *PostureChecks) Passes(peer *Peer) bool {
|
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)
|
valid, _ := c.check(peer)
|
||||||
if !valid {
|
if !valid {
|
||||||
return false
|
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.
|
// GetChecks returns the initialized checks in the same order as posture.Checks.GetChecks.
|
||||||
func (pc *PostureChecks) GetChecks() []postureCheck {
|
func (pc *PostureChecks) GetChecks() []Check {
|
||||||
var checks []postureCheck
|
var checks []Check
|
||||||
if pc.Checks.NBVersionCheck != nil {
|
if pc.Checks.NBVersionCheck != nil {
|
||||||
checks = append(checks, pc.Checks.NBVersionCheck)
|
checks = append(checks, pc.Checks.NBVersionCheck)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user