- Add docstrings to `mdm_integration`
- refactor for cognitive complexity
- mod tidy
This commit is contained in:
riccardom
2026-06-09 12:15:49 +02:00
parent 46602e4176
commit d806f25b33
14 changed files with 360 additions and 200 deletions
+25 -8
View File
@@ -96,7 +96,9 @@ type Policy struct {
}
// NewPolicy constructs a Policy from a key→value map. Pass nil or an empty
// map to construct an empty (no-enforcement) Policy.
// NewPolicy constructs a Policy backed by the provided key→value map.
// If values is nil it is replaced with an empty map so the returned *Policy
// is always non-nil and represents no active MDM enforcement when empty.
func NewPolicy(values map[string]any) *Policy {
if values == nil {
values = map[string]any{}
@@ -111,7 +113,9 @@ func NewPolicy(values map[string]any) *Policy {
// Diagnostic logging differentiates the three states:
// - source absent / unsupported platform: trace log only
// - source present, zero keys: info "MDM enrolled (no managed keys)"
// - source present, N keys: info "MDM enrolled with N managed keys: [...]"
// LoadPolicy loads MDM-managed configuration from the platform and returns a Policy representing the managed settings.
// If the platform loader fails or returns nil, LoadPolicy returns a non-nil empty Policy.
// When the loaded map contains zero keys it logs that MDM is enrolled with no managed keys; when it contains keys it logs the count and a stable, sorted list of key names.
func LoadPolicy() *Policy {
values, err := loadPlatformPolicy()
if err != nil {
@@ -169,6 +173,19 @@ func (p *Policy) GetString(key string) (string, bool) {
return s, true
}
// boolStringLiterals enumerates the textual boolean encodings the
// platform loaders may produce (Windows REG_SZ "true", iOS / Android
// managed-config booleans-as-strings, etc.). Lookup keeps GetBool flat
// (no nested switch on the string case).
var boolStringLiterals = map[string]bool{
"true": true,
"1": true,
"yes": true,
"false": false,
"0": false,
"no": false,
}
// GetBool returns the managed value for key coerced to bool, and whether the
// key was set. Accepts native bool and string literals "true"/"false"/"1"/"0".
func (p *Policy) GetBool(key string) (bool, bool) {
@@ -183,12 +200,8 @@ func (p *Policy) GetBool(key string) (bool, bool) {
case bool:
return t, true
case string:
switch t {
case "true", "1", "yes":
return true, true
case "false", "0", "no":
return false, true
}
b, known := boolStringLiterals[t]
return b, known
case int:
return t != 0, true
case int64:
@@ -257,6 +270,10 @@ func (p *Policy) GetStringSlice(key string) ([]string, bool) {
return nil, false
}
// sortedKeys returns the keys of m as a deterministic, lexicographically
// sorted slice. Used internally by Policy.ManagedKeys and LoadPolicy's
// diagnostic log line so callers see a stable key order across runs
// It produces a deterministic ordering for a map regardless of Go's randomized iteration.
func sortedKeys(m map[string]any) []string {
out := make([]string, 0, len(m))
for k := range m {
+5 -1
View File
@@ -37,7 +37,11 @@ const policyPlistPath = "/Library/Managed Preferences/io.netbird.client.plist"
// map naturally onto the Policy accessor expectations (GetString /
// GetBool / GetInt / GetStringSlice). Unknown top-level keys are
// logged and skipped so a stray entry in the payload does not block
// startup.
// loadPlatformPolicy reads the managed-preferences plist at policyPlistPath and returns recognised MDM key/value pairs.
//
// If the plist file does not exist, it returns (nil, nil). It returns a wrapped error on open/stat/decode failures.
// The function refuses to read a world-writable plist and returns an error in that case.
// Top-level plist keys are canonicalized (case-insensitive) to the internal MDM key names; unknown keys are logged and skipped.
func loadPlatformPolicy() (map[string]any, error) {
f, err := os.Open(policyPlistPath)
if err != nil {
+2 -1
View File
@@ -6,7 +6,8 @@ package mdm
// Kotlin/Java on Android) reads the OS managed-config store and pushes the
// resulting dictionary in-process via a gomobile entry point that lands in
// Phase 5 / Phase 6. The stub keeps the package compilable for mobile
// build targets.
// loadPlatformPolicy is a stub used on mobile (iOS/Android) builds that returns a nil policy map and no error.
// The actual managed-config policy is supplied by the native platform layer.
func loadPlatformPolicy() (map[string]any, error) {
return nil, nil
}
+2 -1
View File
@@ -4,7 +4,8 @@ package mdm
// loadPlatformPolicy returns no policy on platforms without an MDM channel
// (Linux, FreeBSD). MDM enforcement is off and the client behaves as if
// the feature did not exist.
// loadPlatformPolicy reports that no platform MDM policy is available on non-Windows/Darwin/iOS/Android builds.
// It returns a nil policy map and a nil error to indicate MDM enforcement is not present on this platform.
func loadPlatformPolicy() (map[string]any, error) {
return nil, nil
}
+39 -32
View File
@@ -31,7 +31,44 @@ const policyRegistryPath = `Software\Policies\NetBird`
// - REG_MULTI_SZ -> []string
//
// Unsupported value types (REG_BINARY, REG_NONE, ...) are skipped with a
// warning so a malformed deployment does not block startup.
// loadPlatformPolicy reads managed NetBird policy values from HKLM\Software\Policies\NetBird.
// If the registry key does not exist it returns (nil, nil).
// It returns a map whose keys are canonical policy names and whose values are coerced from registry types:
// REG_SZ/REG_EXPAND_SZ -> string, REG_DWORD/REG_QWORD -> int64, REG_MULTI_SZ -> []string.
// Unknown value names, unsupported value types, and per-value read errors are skipped and logged; failures opening the key or enumerating values are returned as errors.
func readRegistryValue(k registry.Key, name, canonical string, out map[string]any) {
_, valType, err := k.GetValue(name, nil)
if err != nil {
log.Warnf("MDM stat %s\\%s: %v", policyRegistryPath, name, err)
return
}
switch valType {
case registry.SZ, registry.EXPAND_SZ:
if v, _, err := k.GetStringValue(name); err == nil {
out[canonical] = v
} else {
log.Warnf("MDM read string %s\\%s: %v", policyRegistryPath, name, err)
}
case registry.DWORD, registry.QWORD:
if v, _, err := k.GetIntegerValue(name); err == nil {
// uint64 from the registry API; Policy.GetBool / GetInt
// helpers consume int64, so narrow safely.
out[canonical] = int64(v)
} else {
log.Warnf("MDM read int %s\\%s: %v", policyRegistryPath, name, err)
}
case registry.MULTI_SZ:
if v, _, err := k.GetStringsValue(name); err == nil {
out[canonical] = v
} else {
log.Warnf("MDM read multi-string %s\\%s: %v", policyRegistryPath, name, err)
}
default:
log.Warnf("MDM ignoring unsupported registry value type %d at %s\\%s",
valType, policyRegistryPath, name)
}
}
func loadPlatformPolicy() (map[string]any, error) {
k, err := registry.OpenKey(registry.LOCAL_MACHINE, policyRegistryPath, registry.QUERY_VALUE)
if err != nil {
@@ -63,37 +100,7 @@ func loadPlatformPolicy() (map[string]any, error) {
log.Warnf("MDM ignoring unknown registry value %s\\%s", policyRegistryPath, name)
continue
}
_, valType, err := k.GetValue(name, nil)
if err != nil {
log.Warnf("MDM stat %s\\%s: %v", policyRegistryPath, name, err)
continue
}
switch valType {
case registry.SZ, registry.EXPAND_SZ:
if v, _, err := k.GetStringValue(name); err == nil {
out[canonical] = v
} else {
log.Warnf("MDM read string %s\\%s: %v", policyRegistryPath, name, err)
}
case registry.DWORD, registry.QWORD:
if v, _, err := k.GetIntegerValue(name); err == nil {
// uint64 from the registry API; Policy.GetBool / GetInt
// helpers consume int64, so narrow safely.
out[canonical] = int64(v)
} else {
log.Warnf("MDM read int %s\\%s: %v", policyRegistryPath, name, err)
}
case registry.MULTI_SZ:
if v, _, err := k.GetStringsValue(name); err == nil {
out[canonical] = v
} else {
log.Warnf("MDM read multi-string %s\\%s: %v", policyRegistryPath, name, err)
}
default:
log.Warnf("MDM ignoring unsupported registry value type %d at %s\\%s",
valType, policyRegistryPath, name)
}
readRegistryValue(k, name, canonical, out)
}
return out, nil
}
+14 -5
View File
@@ -25,7 +25,8 @@ const testReloadInterval = 1 * time.Second
// reloadInterval returns the production cadence, or the accelerated test
// cadence when running under `go test`. Centralising the choice here keeps
// the prod/test split in one place and out of the ticker's call sites.
// reloadInterval selects the polling interval used to re-read the OS-native MDM policy.
// It returns testReloadInterval when tests are running (testing.Testing() == true) and defaultReloadInterval otherwise.
func reloadInterval() time.Duration {
if testing.Testing() {
return testReloadInterval
@@ -52,7 +53,9 @@ type Ticker struct {
// NewTicker constructs a Ticker that re-reads the OS-native policy every
// reloadInterval() and invokes onChange on any diff. The cadence is owned by
// reloadInterval (production default, accelerated under `go test`); callers
// do not supply it. onChange may be nil for a log-only ticker.
// NewTicker creates a Ticker that polls the OS-native MDM policy at the package reload interval and invokes onChange when a policy change is detected.
// If onChange is nil the ticker will only log detected changes.
// The ticker's initial snapshot is populated by loading the current policy.
func NewTicker(onChange func(prev, curr *Policy)) *Ticker {
return &Ticker{
interval: reloadInterval(),
@@ -91,7 +94,7 @@ func (t *Ticker) Run(ctx context.Context) {
}
// PoliciesEqual reports whether two Policy instances carry the same managed
// key set with identical values. Nil and empty policies compare equal.
// value maps for deep equality.
func PoliciesEqual(a, b *Policy) bool {
if a.IsEmpty() && b.IsEmpty() {
return true
@@ -103,7 +106,11 @@ func PoliciesEqual(a, b *Policy) bool {
}
// diffPolicies returns the keys added in curr, removed from prev, and whose
// value changed. Returned slices are sorted for stable log output.
// diffPolicies reports keys that were added, removed, or changed between two policies.
// The returned slices contain keys present only in `curr` (added), only in `prev` (removed),
// and present in both but whose values differ (changed). Each slice is sorted
// lexicographically for stable logging output; value differences are determined
// using deep equality.
func diffPolicies(prev, curr *Policy) (added, removed, changed []string) {
prevKeys := mapOf(prev)
currKeys := mapOf(curr)
@@ -127,7 +134,9 @@ func diffPolicies(prev, curr *Policy) (added, removed, changed []string) {
// mapOf returns a (possibly empty, never nil) copy of the underlying values
// map of a Policy so callers outside this package can compare across the
// public Policy boundary without touching unexported state.
// mapOf returns a non-nil copy of the given Policy's key/value map.
// If p is nil, mapOf returns an empty map; otherwise it returns a newly
// allocated map containing the same key/value pairs as p.values.
func mapOf(p *Policy) map[string]any {
if p == nil {
return map[string]any{}