mdm

package
v0.80.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2026 License: BSD-3-Clause Imports: 11 Imported by: 0

Documentation

Overview

Package mdm reads MDM-managed configuration from platform-native sources (plist on macOS, registry on Windows, UserDefaults on iOS, RestrictionsManager on Android). The returned Policy is consumed by profilemanager.Config.apply() as the highest-priority override layer.

An empty Policy (no source present, or source present with zero keys) means no MDM enforcement is active and the client behaves as if the feature did not exist.

Index

Constants

View Source
const (
	KeyManagementURL         = "managementURL"
	KeyDisableUpdateSettings = "disableUpdateSettings"
	KeyDisableProfiles       = "disableProfiles"
	KeyDisableNetworks       = "disableNetworks"
	// KeyDisableAdvancedView gates the advanced-view section in the
	// upcoming UI revision. UI-only: NOT stored on Config, not
	// applied by applyMDMPolicy, not rejectable via SetConfig. The
	// daemon surfaces it through GetFeatures (tristate: present
	// true / present false / absent) and the same key appears in
	// GetConfigResponse.mDMManagedFields when set.
	KeyDisableAdvancedView      = "disableAdvancedView"
	KeyDisableClientRoutes      = "disableClientRoutes"
	KeyDisableServerRoutes      = "disableServerRoutes"
	KeyBlockInbound             = "blockInbound"
	KeyDisableMetricsCollection = "disableMetricsCollection"
	KeyAllowServerSSH           = "allowServerSSH"
	KeyDisableAutoConnect       = "disableAutoConnect"
	// KeyDisableAutostart suppresses the GUI's fresh-install
	// launch-on-login default and marks the Settings toggle as
	// MDM-managed. UI-only: NOT stored on Config and not applied by
	// applyMDMPolicy; the GUI reads it directly and it appears in
	// GetConfigResponse.mDMManagedFields when set.
	KeyDisableAutostart    = "disableAutostart"
	KeyPreSharedKey        = "preSharedKey"
	KeyRosenpassEnabled    = "rosenpassEnabled"
	KeyRosenpassPermissive = "rosenpassPermissive"
	KeyWireguardPort       = "wireguardPort"
	KeyEnableLocalMetrics  = "enableLocalMetrics"
	KeyLocalMetricsAddress = "localMetricsAddress"

	// Split tunnel is modeled as a single conceptual policy with two
	// registry/plist values. KeySplitTunnelMode is the discriminator
	// ("allow" or "disallow"); KeySplitTunnelApps is a comma-separated
	// list of package names. The values are mutually exclusive by
	// construction — only one mode can be set at a time.
	KeySplitTunnelMode = "splitTunnelMode"
	KeySplitTunnelApps = "splitTunnelApps"

	// KeyLazyConnection forces the lazy-connection feature on or off, overriding
	// the management feature flag. Read as a bool (native bool, or on/off,
	// true/false, 1/0, yes/no); absent = defer to management.
	KeyLazyConnection = "lazyConnection"

	// KeyRemoteJobsAllowed opts the peer into management-requested remote jobs
	// (e.g. debug bundles). Read as a bool; absent = defer to the local config
	// (which defaults to disabled). Stored on Config as RemoteJobsAllowed.
	KeyRemoteJobsAllowed = "allowRemoteJobs"

	// KeyBundleUploadURL overrides the debug-bundle upload service URL for
	// remote jobs, taking precedence over the management-supplied value. Read
	// as a string; must be an https URL with a host. Absent = defer to the
	// management-supplied URL (or the default upload server).
	KeyBundleUploadURL = "debugBundleUploadURL"
)

Well-known policy keys. Names mirror the corresponding ConfigInput Go field names (lowerCamelCase) so the daemon can map a Policy key directly to a configuration field.

View Source
const (
	SplitTunnelModeAllow    = "allow"
	SplitTunnelModeDisallow = "disallow"
)

Split-tunnel mode literals (KeySplitTunnelMode values).

View Source
const DefaultReloadInterval = 1 * time.Minute

DefaultReloadInterval is the production cadence at which the desktop daemon re-reads the OS-native MDM policy. Picked to balance responsiveness against registry/plist I/O overhead. Mobile builds use OS-side notifications instead, hence anticipating the ticker mechanism entirely.

View Source
const PreSharedKeyRedactedSentinel = "**********"

PreSharedKeyRedactedSentinel is the redaction mask returned in place of a real pre-shared key; an incoming value equal to it is a round-trip echo, never an override.

Variables

View Source
var SecretKeys = map[string]struct{}{
	KeyPreSharedKey: {},

	KeyBundleUploadURL: {},
}

SecretKeys lists keys whose values must be redacted in logs.

Functions

func CanonicalURL added in v0.79.0

func CanonicalURL(s string) string

CanonicalURL normalizes a service URL by appending the scheme default port when none is present; unparseable input is returned unchanged.

func ResolveConflicts added in v0.79.0

func ResolveConflicts(policy *Policy, checks []ConflictCheck) []string

ResolveConflicts returns the names of keys whose requested value diverges from the policy-enforced value; keys the policy does not manage are skipped, a managed key without a Check counts as a conflict.

Types

type ChangeDetector added in v0.79.0

type ChangeDetector struct {
	// contains filtered or unexported fields
}

ChangeDetector tracks the last observed policy of a Loader so an OS-notification-driven caller can ask whether the managed configuration actually changed before restarting anything.

func NewChangeDetector added in v0.79.0

func NewChangeDetector(loader *Loader) *ChangeDetector

NewChangeDetector constructs a ChangeDetector seeded with the loader's current policy, so only a later change reports as changed.

func (*ChangeDetector) Changed added in v0.79.0

func (d *ChangeDetector) Changed() bool

Changed re-reads the policy, logs the per-key diff, and reports whether it diverged from the last observation; the new snapshot becomes the baseline.

type ConflictCheck added in v0.79.0

type ConflictCheck struct {
	Key   string
	Check func(*Policy) bool
}

ConflictCheck is a value-aware comparison between a single requested field and the corresponding MDM-enforced value.

func ConflictBool added in v0.79.0

func ConflictBool(key string, p *bool) ConflictCheck

ConflictBool builds a ConflictCheck for a boolean MDM key.

func ConflictInt64 added in v0.79.0

func ConflictInt64(key string, p *int64) ConflictCheck

ConflictInt64 builds a ConflictCheck for an integer MDM key.

func ConflictStringPtr added in v0.79.0

func ConflictStringPtr(key string, p *string) ConflictCheck

ConflictStringPtr builds a ConflictCheck for an optional string MDM key, where an explicit empty value is still a request to change the setting. A nil p means "field not set" (no override requested).

func ConflictURL added in v0.79.0

func ConflictURL(key, got string) ConflictCheck

ConflictURL builds a ConflictCheck for a URL-typed MDM key. The two sides are compared as the endpoints they address, not as strings: see util.SameServiceURL.

type Features added in v0.79.0

type Features struct {
	DisableProfiles       bool `json:"disableProfiles"`
	DisableNetworks       bool `json:"disableNetworks"`
	DisableUpdateSettings bool `json:"disableUpdateSettings"`
}

Features carries the feature gates a UI must honor.

type Fields added in v0.79.0

type Fields struct {
	ManagementURL            string `json:"managementURL"`
	PreSharedKey             bool   `json:"preSharedKey"`
	WireguardPort            bool   `json:"wireguardPort"`
	RosenpassEnabled         bool   `json:"rosenpassEnabled"`
	RosenpassPermissive      bool   `json:"rosenpassPermissive"`
	DisableClientRoutes      bool   `json:"disableClientRoutes"`
	DisableServerRoutes      bool   `json:"disableServerRoutes"`
	AllowServerSSH           *bool  `json:"allowServerSSH"`
	DisableAutoConnect       bool   `json:"disableAutoConnect"`
	DisableAutostart         bool   `json:"disableAutostart"`
	BlockInbound             bool   `json:"blockInbound"`
	DisableMetricsCollection bool   `json:"disableMetricsCollection"`
	SplitTunnelMode          bool   `json:"splitTunnelMode"`
	SplitTunnelApps          bool   `json:"splitTunnelApps"`
	RemoteJobsAllowed        bool   `json:"allowRemoteJobs"`
	DisableAdvancedView      *bool  `json:"disableAdvancedView"`
}

Fields carries the per-key MDM enforcement state for a UI: value-typed fields hold the enforced value (nil pointer = not managed), boolean fields report that the key is managed.

type Loader added in v0.79.0

type Loader struct {
	// contains filtered or unexported fields
}

Loader is the DI-friendly entry point for reading the active MDM policy. Construct one at the daemon's lifecycle owner (Server on desktop, gomobile-exposed bridge on mobile) and pass it to anything that needs to read MDM state (the reload ticker, profilemanager's Config). Each callsite has the Loader handed in instead of looking up package-level state.

func NewJSONLoader added in v0.79.0

func NewJSONLoader(fetch func() string) *Loader

NewJSONLoader constructs a Loader whose policy source is a JSON-encoded object string, as produced by the mobile native layers; a nil fetch disables MDM enforcement.

func NewLoader added in v0.79.0

func NewLoader(f PolicyFetcher) *Loader

NewLoader constructs a Loader. A non-nil fetcher takes precedence over the platform-native source; production desktop callers pass nil so the registry / plist stays authoritative.

func (*Loader) Load added in v0.79.0

func (l *Loader) Load() *Policy

Load reads the platform-native MDM configuration and returns a Policy. Returns an empty (but non-nil) Policy when no source is present, the source is empty, or the platform is unsupported.

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: [...]"

type Policy

type Policy struct {
	// contains filtered or unexported fields
}

Policy holds MDM-managed settings read from the platform source. A nil or empty Policy means no enforcement is active.

func NewPolicy

func NewPolicy(values map[string]any) *Policy

NewPolicy constructs a Policy from a key→value map. Pass nil or an empty map to construct an empty (no-enforcement) Policy. The returned *Policy is always non-nil.

func (*Policy) GetBool

func (p *Policy) GetBool(key string) (bool, bool)

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, yes/no, on/off), case-insensitively and trimmed of surrounding whitespace.

func (*Policy) GetInt

func (p *Policy) GetInt(key string) (int64, bool)

GetInt returns the managed value for key as int64, and whether the key was set. Accepts native int / int64 (as produced by the Windows registry loader for REG_DWORD/REG_QWORD) and numeric strings (decimal).

func (*Policy) GetString

func (p *Policy) GetString(key string) (string, bool)

GetString returns the managed value for key coerced to string, and whether the key was set. A non-string value returns ("", false).

func (*Policy) GetStringSlice

func (p *Policy) GetStringSlice(key string) ([]string, bool)

GetStringSlice returns the managed value for key as []string, and whether the key was set. Accepts []string, []any (of strings), and a single string (treated as a one-element list).

func (*Policy) HasKey

func (p *Policy) HasKey(key string) bool

HasKey reports whether the given key is MDM-managed.

func (*Policy) IsEmpty

func (p *Policy) IsEmpty() bool

IsEmpty reports whether the Policy has no managed keys.

func (*Policy) ManagedKeys

func (p *Policy) ManagedKeys() []string

ManagedKeys returns the sorted list of managed key names. Returns an empty slice (not nil) on an empty Policy.

type PolicyFetcher added in v0.79.0

type PolicyFetcher interface {
	Fetch() map[string]any
}

PolicyFetcher supplies the managed configuration to a Loader. Mobile platforms (Android / iOS) implement it to push the OS-managed values into the Go runtime. On every platform a non-nil fetcher takes precedence over the native source, which is the test seam for the registry / plist loaders; a nil fetcher leaves the native source in charge, or disables MDM enforcement where there is none.

type Restrictions added in v0.79.0

type Restrictions struct {
	MDM      Fields   `json:"mdm"`
	Features Features `json:"features"`
}

Restrictions is the UI-facing enforcement snapshot; the JSON shape is shared by the desktop frontend and the mobile bridges.

func BuildRestrictions added in v0.79.0

func BuildRestrictions(policy *Policy) Restrictions

BuildRestrictions derives the UI enforcement snapshot from the active policy.

func (Restrictions) JSON added in v0.79.0

func (r Restrictions) JSON() (string, error)

JSON renders the snapshot in the shared UI JSON shape.

type Ticker

type Ticker struct {
	// contains filtered or unexported fields
}

Ticker periodically re-reads the OS-native MDM policy via the injected Loader and invokes the onChange callback (supplied to Run) whenever the observed Policy diverges from the last observation (added / removed / changed keys). Launch with Run from a goroutine; cancel the supplied context to stop.

func NewTicker

func NewTicker(reloadInterval time.Duration, loader *Loader) *Ticker

NewTicker constructs a Ticker that will re-read the OS-native policy every reloadInterval once Run is called. The Loader is injected so the ticker doesn't depend on any package-level state — production passes the daemon-owned Loader, tests pass a fake Loader (built with a fake PolicyFetcher).

The initial snapshot is populated by calling loader.Load() at construction time so the first tick only fires onChange when the policy actually changed since boot — without this baseline the first tick would report every currently-managed key as "added" and trigger a spurious engine restart.

func (*Ticker) Run

func (t *Ticker) Run(ctx context.Context, onChange func(prev, curr *Policy) error)

Run blocks until ctx is cancelled, polling the OS-native policy store at the configured cadence and emitting log lines + onChange callback on every observed diff. onChange must be non-nil.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL