mirror of
https://github.com/netbirdio/docs.git
synced 2026-08-24 16:51:26 +02:00
Adds doc for Windows/macOS MDM integration (#783)
* Adds doc for Windows/macOS MDM integration * Fixes coderabbit suggestions * Update public/docs-static/files/netbird-policy.reg.ps1 Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Check port and allow/disallow in admitted values for macos script --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
126
public/docs-static/files/io.netbird.client.plist
Normal file
126
public/docs-static/files/io.netbird.client.plist
Normal file
@@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<!--
|
||||
NetBird MDM preferences (macOS) — bare plist for MDM platforms that
|
||||
accept a managed-preferences plist tied to a bundle identifier
|
||||
(e.g. JumpCloud "Mac Application Custom Settings", Mosyle "Custom
|
||||
Settings", Jamf "Application & Custom Settings" → External
|
||||
Application).
|
||||
|
||||
Bundle identifier (preference domain): io.netbird.client
|
||||
|
||||
The MDM provider will wrap this plist into a Configuration Profile
|
||||
payload of type com.apple.ManagedClient.preferences and push it to
|
||||
target devices via the Apple MDM protocol. The OS materializes the
|
||||
final file at:
|
||||
/Library/Managed Preferences/io.netbird.client.plist
|
||||
which is what the NetBird daemon's client/mdm/policy_darwin.go
|
||||
loader reads on every 1-minute MDM reload tick.
|
||||
|
||||
For MDM platforms that expect a full Configuration Profile instead
|
||||
of a bare plist (Custom Configuration Profile / .mobileconfig upload),
|
||||
use docs/netbird-macos.mobileconfig — same keys, additional Payload*
|
||||
envelope.
|
||||
|
||||
Editing this file:
|
||||
- Remove or comment out any key you do NOT want to enforce. The
|
||||
daemon treats an absent key as "no enforcement" for that field.
|
||||
- Keep the document well-formed XML. Validate locally with:
|
||||
plutil -lint docs/io.netbird.client.plist
|
||||
- Keys are camelCase; values are typed (<string>, <true/>, <false/>,
|
||||
<integer>). See docs/src/pages/client/mdm-integration.mdx (the
|
||||
public docs page) for the full reference.
|
||||
|
||||
Persistence caveat:
|
||||
macOS wipes /Library/Managed Preferences/ at every boot on
|
||||
devices that are NOT MDM-enrolled. This plist only sticks across
|
||||
reboots when delivered through a real MDM channel. For local
|
||||
testing on an un-enrolled host, write the file manually as root
|
||||
and accept it will not survive the next boot.
|
||||
-->
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
|
||||
<!-- ===== Identity / auth ===== -->
|
||||
<key>managementURL</key>
|
||||
<string>https://api.netbird.io:443</string>
|
||||
|
||||
<!--
|
||||
Pre-shared key: secret. Remove the entry entirely when not used;
|
||||
do NOT leave an empty <string></string>, which the daemon would
|
||||
otherwise treat as a deliberate empty-PSK enforcement.
|
||||
-->
|
||||
<!--
|
||||
<key>preSharedKey</key>
|
||||
<string>REPLACE_ME</string>
|
||||
-->
|
||||
|
||||
<!-- ===== Engine / runtime behavior =====
|
||||
Each key is optional. Remove or comment out to leave the
|
||||
field unmanaged on the client. -->
|
||||
|
||||
<key>allowServerSSH</key>
|
||||
<true/>
|
||||
|
||||
<!--
|
||||
<key>disableAutoConnect</key>
|
||||
<false/>
|
||||
|
||||
<key>disableClientRoutes</key>
|
||||
<false/>
|
||||
|
||||
<key>disableServerRoutes</key>
|
||||
<false/>
|
||||
|
||||
<key>blockInbound</key>
|
||||
<false/>
|
||||
|
||||
<key>rosenpassEnabled</key>
|
||||
<true/>
|
||||
|
||||
<key>rosenpassPermissive</key>
|
||||
<false/>
|
||||
-->
|
||||
|
||||
<!-- ===== WireGuard UDP port =====
|
||||
Range 1-65535. Omit to keep the daemon default. -->
|
||||
<!--
|
||||
<key>wireguardPort</key>
|
||||
<integer>51820</integer>
|
||||
-->
|
||||
|
||||
<!-- ===== UI / lockdown kill switches =====
|
||||
disableUpdateSettings : block every config change from UI and CLI
|
||||
on this device (Settings view stays
|
||||
readable but read-only).
|
||||
disableProfiles : hide the profile menu, reject profile CRUD.
|
||||
disableNetworks : hide the Networks / Exit Node menus,
|
||||
reject the related RPCs.
|
||||
disableMetricsCollection: opt out of anonymous usage telemetry. -->
|
||||
<!--
|
||||
<key>disableUpdateSettings</key>
|
||||
<true/>
|
||||
|
||||
<key>disableProfiles</key>
|
||||
<true/>
|
||||
|
||||
<key>disableNetworks</key>
|
||||
<true/>
|
||||
|
||||
<key>disableMetricsCollection</key>
|
||||
<false/>
|
||||
-->
|
||||
|
||||
<!-- ===== Split tunnel =====
|
||||
Android-only at the client level. Safe to ship on macOS for
|
||||
mixed-platform fleets; the macOS daemon parses and ignores. -->
|
||||
<!--
|
||||
<key>splitTunnelMode</key>
|
||||
<string>allow</string>
|
||||
|
||||
<key>splitTunnelApps</key>
|
||||
<string>com.acme.app1,com.acme.app2</string>
|
||||
-->
|
||||
|
||||
</dict>
|
||||
</plist>
|
||||
159
public/docs-static/files/netbird-macos.mobileconfig
Normal file
159
public/docs-static/files/netbird-macos.mobileconfig
Normal file
@@ -0,0 +1,159 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<!--
|
||||
NetBird MDM configuration profile (macOS).
|
||||
|
||||
Wraps a `com.apple.ManagedClient.preferences` payload that pushes the
|
||||
NetBird MDM policy into:
|
||||
/Library/Managed Preferences/io.netbird.client.plist
|
||||
|
||||
Read at runtime by the netbird daemon's macOS loader
|
||||
(client/mdm/policy_darwin.go — Phase 2). Key names match the canonical
|
||||
lowerCamelCase form used in docs/netbird.admx and the mdm.Key*
|
||||
constants in client/mdm/policy.go.
|
||||
|
||||
Bundle identifier: io.netbird.client
|
||||
(confirm against the signed pkg before fleet roll-out)
|
||||
|
||||
Distribution:
|
||||
- sign with `productsign --sign "Developer ID Installer: ..." ...`
|
||||
before fleet roll-out (Apple-Configurator-2 won't install an
|
||||
unsigned profile on Sonoma+ without user override).
|
||||
- For local dev install: `sudo profiles install -path netbird-macos.mobileconfig`.
|
||||
- For MDM (Jamf/Kandji/Mosyle/Intune): upload as a Custom Profile.
|
||||
|
||||
Editing:
|
||||
- Replace UUID placeholders below with fresh UUIDs (`uuidgen` on
|
||||
macOS) when forking this template for a real fleet — each
|
||||
deployment should have unique UUIDs so the OS treats it as a
|
||||
distinct profile.
|
||||
- Tune the PayloadContent values to the policy you want to enforce.
|
||||
- Remove any key you do NOT want to enforce (the daemon treats an
|
||||
absent key as "no enforcement" for that field).
|
||||
|
||||
iOS note:
|
||||
This file is macOS-specific. iOS uses managed app config via
|
||||
UserDefaults[com.apple.configuration.managed] under a different
|
||||
payload type (com.apple.app.configuration.managed); the wrapper
|
||||
structure is the same but the inner payload dictionary differs.
|
||||
See docs/netbird-ios.mobileconfig (Phase 5) when shipped.
|
||||
-->
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- Outer profile envelope -->
|
||||
<key>PayloadType</key>
|
||||
<string>Configuration</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>io.netbird.client.mdm</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>11111111-1111-1111-1111-111111111111</string>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>NetBird MDM Policy</string>
|
||||
<key>PayloadDescription</key>
|
||||
<string>Enforces NetBird client configuration. Values written here override any local user / CLI / on-disk setting and are re-applied at every daemon boot and on every 1-minute MDM reload tick.</string>
|
||||
<key>PayloadOrganization</key>
|
||||
<string>NetBird</string>
|
||||
<key>PayloadScope</key>
|
||||
<string>System</string>
|
||||
<key>PayloadRemovalDisallowed</key>
|
||||
<false/>
|
||||
|
||||
<key>PayloadContent</key>
|
||||
<array>
|
||||
<dict>
|
||||
<!-- Managed preferences payload: writes /Library/Managed Preferences/io.netbird.client.plist -->
|
||||
<key>PayloadType</key>
|
||||
<string>com.apple.ManagedClient.preferences</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>io.netbird.client.mdm.preferences</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>22222222-2222-2222-2222-222222222222</string>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>NetBird Managed Preferences</string>
|
||||
<key>PayloadEnabled</key>
|
||||
<true/>
|
||||
|
||||
<key>PayloadContent</key>
|
||||
<dict>
|
||||
<key>io.netbird.client</key>
|
||||
<dict>
|
||||
<key>Forced</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>mcx_preference_settings</key>
|
||||
<dict>
|
||||
|
||||
<!-- ===== Identity / auth (strings) ===== -->
|
||||
<key>managementURL</key>
|
||||
<string>https://api.netbird.io:443</string>
|
||||
|
||||
<!-- Pre-shared key: secret. Remove the entry entirely
|
||||
when not used; do NOT leave an empty string. -->
|
||||
<!--
|
||||
<key>preSharedKey</key>
|
||||
<string>REPLACE_ME</string>
|
||||
-->
|
||||
|
||||
<!-- ===== Engine / runtime behavior (bool) =====
|
||||
Remove any key to leave the field unmanaged. -->
|
||||
<!--
|
||||
<key>disableAutoConnect</key>
|
||||
<false/>
|
||||
<key>disableClientRoutes</key>
|
||||
<false/>
|
||||
<key>disableServerRoutes</key>
|
||||
<false/>
|
||||
<key>blockInbound</key>
|
||||
<false/>
|
||||
-->
|
||||
<key>allowServerSSH</key>
|
||||
<true/>
|
||||
<!--
|
||||
<key>rosenpassEnabled</key>
|
||||
<true/>
|
||||
<key>rosenpassPermissive</key>
|
||||
<false/>
|
||||
-->
|
||||
|
||||
<!-- ===== WireGuard UDP port (int) =====
|
||||
Range 1-65535. Omit to keep the default. -->
|
||||
<!--
|
||||
<key>wireguardPort</key>
|
||||
<integer>51820</integer>
|
||||
-->
|
||||
|
||||
<!-- ===== Split tunnel (Android-only at the daemon level)
|
||||
Pushed harmlessly on macOS for fleets with mixed
|
||||
desktop+mobile devices; the macOS daemon ignores it. -->
|
||||
<!--
|
||||
<key>splitTunnelMode</key>
|
||||
<string>allow</string>
|
||||
<key>splitTunnelApps</key>
|
||||
<string>com.acme.app1,com.acme.app2</string>
|
||||
-->
|
||||
|
||||
<!-- ===== UI / kill switches (bool) ===== -->
|
||||
<!--
|
||||
<key>disableUpdateSettings</key>
|
||||
<true/>
|
||||
<key>disableProfiles</key>
|
||||
<true/>
|
||||
<key>disableNetworks</key>
|
||||
<true/>
|
||||
<key>disableMetricsCollection</key>
|
||||
<false/>
|
||||
-->
|
||||
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
202
public/docs-static/files/netbird-macos.sh
Normal file
202
public/docs-static/files/netbird-macos.sh
Normal file
@@ -0,0 +1,202 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# SYNOPSIS
|
||||
# Push the NetBird MDM policy to a macOS device via JumpCloud Commands.
|
||||
#
|
||||
# DESCRIPTION
|
||||
# This is the macOS counterpart of docs/netbird-policy.reg.ps1.
|
||||
# It writes the values declared in the "POLICY VALUES" block below to
|
||||
# the managed-preferences plist that the NetBird daemon's
|
||||
# client/mdm/policy_darwin.go loader reads on every 1-minute MDM
|
||||
# reload tick:
|
||||
#
|
||||
# /Library/Managed Preferences/io.netbird.client.plist
|
||||
#
|
||||
# Once the plist lands, the daemon picks up the new values without
|
||||
# restart (the ticker calls Config.apply() → applyMDMPolicy() and
|
||||
# restarts the engine on diff).
|
||||
#
|
||||
# DEPLOYMENT (JumpCloud)
|
||||
# 1. Admin Console -> Device Management -> Commands -> +.
|
||||
# 2. Type: Mac, Shell, Run as: root.
|
||||
# 3. Paste this file verbatim into the command body.
|
||||
# 4. Bind to the target system group, save, run.
|
||||
#
|
||||
# IMPORTANT: PERSISTENCE
|
||||
# macOS wipes /Library/Managed Preferences/ at every boot on devices
|
||||
# that are NOT MDM-enrolled. For a persistent fleet rollout, push the
|
||||
# companion docs/netbird-macos.mobileconfig as a Custom Configuration
|
||||
# Profile (Admin Console -> MDM -> Mac Custom Configuration Profiles)
|
||||
# instead of this script. Use this script when:
|
||||
# - the device is MDM-enrolled (file survives reboots), or
|
||||
# - you need a one-shot test push before reboot, or
|
||||
# - you orchestrate via JumpCloud Commands and want the same
|
||||
# variable-driven workflow as the Windows .ps1 sibling.
|
||||
#
|
||||
# IDEMPOTENCY: re-running with the same values is a no-op from the
|
||||
# daemon's point of view (the 1-minute reload ticker diff returns empty).
|
||||
#
|
||||
# SECURITY: PreSharedKey is redacted in this script's log output.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
### POLICY VALUES — EDIT THIS BLOCK ###########################################
|
||||
#
|
||||
# Set each variable below to the desired value. Set to empty string ""
|
||||
# or to NULL to omit a key entirely (the daemon treats an absent key
|
||||
# as "no enforcement" for that field). Booleans use "true"/"false"
|
||||
# (lowercase). Integers as decimal.
|
||||
#
|
||||
# Reference for key names + accepted values:
|
||||
# client/mdm/policy.go (Key* constants)
|
||||
# docs/netbird-macos.mobileconfig (sample profile)
|
||||
# docs/netbird.admx + .adml (Windows ADMX schema)
|
||||
#
|
||||
NULL='__UNSET__'
|
||||
managementURL='https://api.netbird.io:443'
|
||||
preSharedKey="$NULL" # secret; redacted in log
|
||||
allowServerSSH='true'
|
||||
blockInbound="$NULL"
|
||||
disableAutoConnect="$NULL"
|
||||
disableClientRoutes="$NULL"
|
||||
disableServerRoutes="$NULL"
|
||||
disableMetricsCollection="$NULL"
|
||||
disableUpdateSettings="$NULL"
|
||||
disableProfiles="$NULL"
|
||||
disableNetworks="$NULL"
|
||||
rosenpassEnabled="$NULL"
|
||||
rosenpassPermissive="$NULL"
|
||||
wireguardPort='51820'
|
||||
splitTunnelMode="$NULL" # "allow" or "disallow", Android-only at the daemon level
|
||||
splitTunnelApps="$NULL" # comma-separated app IDs, Android-only
|
||||
##############################################################################
|
||||
|
||||
readonly PLIST_DIR='/Library/Managed Preferences'
|
||||
readonly PLIST_PATH="$PLIST_DIR/io.netbird.client.plist"
|
||||
readonly LOG_TAG='netbird-mdm'
|
||||
|
||||
# log sends a message to the system logger using the configured tag and echoes the message to stdout prefixed by an ISO 8601 UTC timestamp and the tag.
|
||||
log() {
|
||||
/usr/bin/logger -t "$LOG_TAG" "$*"
|
||||
printf '%s [%s] %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$LOG_TAG" "$*"
|
||||
}
|
||||
|
||||
# is_set returns success if the provided value is non-empty and is not equal to the special NULL marker.
|
||||
is_set() {
|
||||
local value="$1"
|
||||
[[ -n "$value" && "$value" != "$NULL" ]]
|
||||
}
|
||||
|
||||
# start_plist creates the temporary plist file at "$PLIST_PATH.tmp" containing the XML plist header and opening `<dict>` for the policy plist.
|
||||
start_plist() {
|
||||
cat > "$PLIST_PATH.tmp" <<'EOF'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
EOF
|
||||
}
|
||||
|
||||
# end_plist appends the closing `</dict>` and `</plist>` tags to the temporary plist file.
|
||||
end_plist() {
|
||||
cat >> "$PLIST_PATH.tmp" <<'EOF'
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
}
|
||||
|
||||
# emit_string appends a plist `<key>`/`<string>` entry for the given key and value to "$PLIST_PATH.tmp", XML-escaping `&`, `<`, and `>`, and logs the assignment (masking the logged value as `********** (secret)` when the key is `preSharedKey`).
|
||||
emit_string() {
|
||||
local key="$1" value="$2" log_value="$2"
|
||||
# Escape XML entities in the value
|
||||
local escaped
|
||||
escaped="$(printf '%s' "$value" | sed -e 's/&/\&/g' -e 's/</\</g' -e 's/>/\>/g')"
|
||||
printf ' <key>%s</key>\n <string>%s</string>\n' "$key" "$escaped" >> "$PLIST_PATH.tmp"
|
||||
if [[ "$key" == "preSharedKey" ]]; then
|
||||
log_value='********** (secret)'
|
||||
fi
|
||||
log "set $key = $log_value"
|
||||
}
|
||||
|
||||
# emit_bool writes a boolean plist entry for a given key into the temporary plist file.
|
||||
# emit_bool writes a boolean plist entry for a key when the provided value matches an accepted boolean token; logs an error and skips the key on invalid input.
|
||||
emit_bool() {
|
||||
local key="$1" value="$2"
|
||||
local xml_bool
|
||||
case "$value" in
|
||||
true|True|TRUE|1|yes) xml_bool='<true/>' ; value='true' ;;
|
||||
false|False|FALSE|0|no) xml_bool='<false/>' ; value='false' ;;
|
||||
*) log "invalid boolean for $key: $value (must be true/false); skipping"; return ;;
|
||||
esac
|
||||
printf ' <key>%s</key>\n %s\n' "$key" "$xml_bool" >> "$PLIST_PATH.tmp"
|
||||
log "set $key = $value"
|
||||
}
|
||||
|
||||
# emit_int validates that VALUE contains only decimal digits and, if valid, appends an `<integer>` plist entry for KEY to the temporary plist (`$PLIST_PATH.tmp`) and logs the assignment; on invalid input it logs a skip and does not emit the key.
|
||||
emit_int() {
|
||||
local key="$1" value="$2"
|
||||
if ! [[ "$value" =~ ^[0-9]+$ ]]; then
|
||||
log "invalid integer for $key: $value (must be decimal); skipping"
|
||||
return
|
||||
fi
|
||||
if [[ "$key" == "wireguardPort" ]] && (( value < 1 || value > 65535 )); then
|
||||
log "invalid integer for $key: $value (must be 1-65535); skipping"
|
||||
return
|
||||
fi
|
||||
printf ' <key>%s</key>\n <integer>%s</integer>\n' "$key" "$value" >> "$PLIST_PATH.tmp"
|
||||
log "set $key = $value"
|
||||
}
|
||||
|
||||
# emit_split_tunnel_mode validates that VALUE is "allow" or "disallow" and, if valid, appends a plist string entry for splitTunnelMode; on invalid input it logs a skip and does not emit the key.
|
||||
emit_split_tunnel_mode() {
|
||||
local value="$1"
|
||||
case "$value" in
|
||||
allow|disallow) emit_string splitTunnelMode "$value" ;;
|
||||
*) log "invalid splitTunnelMode: $value (must be allow/disallow); skipping" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# main builds the NetBird MDM plist from configured policy variables, validates and installs it to /Library/Managed Preferences/io.netbird.client.plist (root:wheel, 644) and optionally triggers the NetBird daemon to reload.
|
||||
main() {
|
||||
log "applying NetBird MDM policy to $PLIST_PATH"
|
||||
/bin/mkdir -p "$PLIST_DIR"
|
||||
start_plist
|
||||
|
||||
is_set "$managementURL" && emit_string managementURL "$managementURL"
|
||||
is_set "$preSharedKey" && emit_string preSharedKey "$preSharedKey"
|
||||
is_set "$allowServerSSH" && emit_bool allowServerSSH "$allowServerSSH"
|
||||
is_set "$blockInbound" && emit_bool blockInbound "$blockInbound"
|
||||
is_set "$disableAutoConnect" && emit_bool disableAutoConnect "$disableAutoConnect"
|
||||
is_set "$disableClientRoutes" && emit_bool disableClientRoutes "$disableClientRoutes"
|
||||
is_set "$disableServerRoutes" && emit_bool disableServerRoutes "$disableServerRoutes"
|
||||
is_set "$disableMetricsCollection" && emit_bool disableMetricsCollection "$disableMetricsCollection"
|
||||
is_set "$disableUpdateSettings" && emit_bool disableUpdateSettings "$disableUpdateSettings"
|
||||
is_set "$disableProfiles" && emit_bool disableProfiles "$disableProfiles"
|
||||
is_set "$disableNetworks" && emit_bool disableNetworks "$disableNetworks"
|
||||
is_set "$rosenpassEnabled" && emit_bool rosenpassEnabled "$rosenpassEnabled"
|
||||
is_set "$rosenpassPermissive" && emit_bool rosenpassPermissive "$rosenpassPermissive"
|
||||
is_set "$wireguardPort" && emit_int wireguardPort "$wireguardPort"
|
||||
is_set "$splitTunnelMode" && emit_split_tunnel_mode "$splitTunnelMode"
|
||||
is_set "$splitTunnelApps" && emit_string splitTunnelApps "$splitTunnelApps"
|
||||
|
||||
end_plist
|
||||
|
||||
if ! /usr/bin/plutil -lint "$PLIST_PATH.tmp" >/dev/null 2>&1; then
|
||||
log "ERROR: generated plist failed plutil lint; not installing"
|
||||
/usr/bin/plutil -lint "$PLIST_PATH.tmp" >&2 || true
|
||||
/bin/rm -f "$PLIST_PATH.tmp"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
/bin/mv -f "$PLIST_PATH.tmp" "$PLIST_PATH"
|
||||
/usr/sbin/chown root:wheel "$PLIST_PATH"
|
||||
/bin/chmod 644 "$PLIST_PATH"
|
||||
|
||||
log "policy installed; NetBird daemon will pick it up within the next 1-minute reload tick"
|
||||
|
||||
# Optional: kick the daemon for an immediate apply. Safe — does
|
||||
# nothing on a host where NetBird is not yet installed.
|
||||
/bin/launchctl kickstart -k system/io.netbird.client 2>/dev/null || true
|
||||
}
|
||||
|
||||
main "$@"
|
||||
BIN
public/docs-static/files/netbird-policy.reg
Normal file
BIN
public/docs-static/files/netbird-policy.reg
Normal file
Binary file not shown.
98
public/docs-static/files/netbird-policy.reg.ps1
Normal file
98
public/docs-static/files/netbird-policy.reg.ps1
Normal file
@@ -0,0 +1,98 @@
|
||||
#requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Push the NetBird MDM policy to a Windows device via JumpCloud Commands
|
||||
by importing a sidecar netbird-policy.reg file.
|
||||
|
||||
.DESCRIPTION
|
||||
Windows counterpart of docs/netbird-macos.sh. Outcome:
|
||||
HKLM\Software\Policies\NetBird populated from the attached
|
||||
netbird-policy.reg file, daemon picks up the change via the
|
||||
1-minute MDM reload ticker.
|
||||
|
||||
Deployment:
|
||||
1. Admin Console -> Device Management -> Commands -> +.
|
||||
2. Type: Windows PowerShell. Run as: SYSTEM.
|
||||
3. Paste this file verbatim into the command body.
|
||||
4. In the same command, attach `netbird-policy.reg` as a file.
|
||||
JumpCloud copies attached files into the command's working
|
||||
directory before invoking the script, so `$PSScriptRoot` or
|
||||
Get-Location resolves to where the .reg lives.
|
||||
5. Bind to the target system group, save, run.
|
||||
|
||||
Producing the .reg file:
|
||||
On a reference machine, after configuring the policy values either
|
||||
via gpedit (GPO) or manual `reg add`, export with:
|
||||
|
||||
reg export "HKLM\Software\Policies\NetBird" netbird-policy.reg /y
|
||||
|
||||
Then attach the resulting file to the JumpCloud command.
|
||||
|
||||
Semantics:
|
||||
- The script nukes the existing HKLM\Software\Policies\NetBird key
|
||||
before importing the .reg, so the .reg is the SINGLE SOURCE OF
|
||||
TRUTH. Any value present in the registry but absent from the .reg
|
||||
is removed. This is what an MDM admin almost always wants.
|
||||
- Setting the .reg to an empty (header-only) file effectively unsets
|
||||
the policy.
|
||||
|
||||
Idempotency: re-running the script with the same .reg is a no-op from
|
||||
the daemon's perspective (values identical → 1-min ticker sees no
|
||||
diff → engine not restarted).
|
||||
|
||||
Exit codes: 0 = success; 1 = .reg missing or reg.exe error.
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$RegFileName = "netbird-policy.reg"
|
||||
$RegKey = "HKLM\Software\Policies\NetBird"
|
||||
|
||||
# Resolve the attached .reg file: JumpCloud copies command attachments
|
||||
# into C:\Windows\Temp\ before invoking the script. Cwd / $PSScriptRoot
|
||||
# fallbacks cover the local-dev case where you might dot-source this
|
||||
# from elsewhere.
|
||||
$candidates = @(
|
||||
(Join-Path "$env:WINDIR\Temp" $RegFileName)
|
||||
(Join-Path (Get-Location) $RegFileName)
|
||||
(Join-Path $PSScriptRoot $RegFileName)
|
||||
) | Where-Object { Test-Path $_ }
|
||||
|
||||
if ($candidates.Count -eq 0) {
|
||||
Write-Error "[netbird-mdm] $RegFileName not found in working directory or `$PSScriptRoot. Attach the file to the JumpCloud command."
|
||||
exit 1
|
||||
}
|
||||
$regFile = $candidates[0]
|
||||
Write-Host "[netbird-mdm] using $regFile"
|
||||
|
||||
# Wipe the existing policy key so the .reg is authoritative.
|
||||
$existed = Test-Path "Registry::HKEY_LOCAL_MACHINE\Software\Policies\NetBird"
|
||||
if ($existed) {
|
||||
& reg.exe delete $RegKey /f | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "[netbird-mdm] failed to clear $RegKey before import (exit $LASTEXITCODE)"
|
||||
exit 1
|
||||
}
|
||||
Write-Host "[netbird-mdm] cleared previous values under $RegKey"
|
||||
}
|
||||
|
||||
# Import. reg.exe writes both data and (re-)creates the key if needed.
|
||||
& reg.exe import $regFile
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "[netbird-mdm] reg import failed (exit $LASTEXITCODE)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Audit dump so the JumpCloud per-execution log captures the applied state.
|
||||
Write-Host "[netbird-mdm] final policy state under $RegKey :"
|
||||
if (Test-Path "Registry::$RegKey") {
|
||||
& reg.exe query $RegKey /s
|
||||
} else {
|
||||
Write-Host "[netbird-mdm] no policy values present under $RegKey"
|
||||
}
|
||||
|
||||
# Daemon's 1-min reload ticker picks up the change automatically.
|
||||
# Uncomment to force immediate convergence (skips the ticker wait):
|
||||
# Restart-Service netbird -Force -ErrorAction SilentlyContinue
|
||||
|
||||
exit 0
|
||||
95
public/docs-static/files/netbird.adml
Normal file
95
public/docs-static/files/netbird.adml
Normal file
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
revision="1.0"
|
||||
schemaVersion="1.0"
|
||||
xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
|
||||
<displayName>NetBird Client Policies</displayName>
|
||||
<description>Group Policy template for NetBird client MDM-managed settings. Values are written under HKLM\Software\Policies\NetBird and consumed by the netbird daemon at startup and every 1-minute reload tick.</description>
|
||||
<resources>
|
||||
<stringTable>
|
||||
|
||||
<!-- Categories -->
|
||||
<string id="NetBird_Category">NetBird</string>
|
||||
<string id="SUPPORTED_NetBird_All">NetBird Client 0.40+</string>
|
||||
|
||||
<!-- Identity / auth -->
|
||||
<string id="ManagementURL_Name">Management URL</string>
|
||||
<string id="ManagementURL_Help">URL of the NetBird management server. Format: https://host[:port]. When set, users cannot override this value via UI or CLI.</string>
|
||||
|
||||
<string id="PreSharedKey_Name">Pre-shared key</string>
|
||||
<string id="PreSharedKey_Help">WireGuard pre-shared key used as an additional symmetric secret on every peer-to-peer tunnel. Secret value.</string>
|
||||
|
||||
<!-- Settings: engine / runtime behavior -->
|
||||
<string id="DisableAutoConnect_Name">Disable auto-connect</string>
|
||||
<string id="DisableAutoConnect_Help">When enabled, the NetBird tunnel does not auto-connect at daemon startup. Equivalent to --disable-auto-connect.</string>
|
||||
|
||||
<string id="DisableClientRoutes_Name">Disable client routes</string>
|
||||
<string id="DisableClientRoutes_Help">When enabled, this client will not consume routes advertised by routing peers. Equivalent to --disable-client-routes.</string>
|
||||
|
||||
<string id="DisableServerRoutes_Name">Disable server routes</string>
|
||||
<string id="DisableServerRoutes_Help">When enabled, this client will not act as a routing peer for other clients. Equivalent to --disable-server-routes.</string>
|
||||
|
||||
<string id="BlockInbound_Name">Block inbound</string>
|
||||
<string id="BlockInbound_Help">When enabled, the client firewall blocks all inbound peer traffic on the WireGuard interface. Equivalent to --block-inbound.</string>
|
||||
|
||||
<string id="AllowServerSSH_Name">Allow server SSH</string>
|
||||
<string id="AllowServerSSH_Help">When enabled, this client accepts incoming SSH sessions via NetBird SSH. Equivalent to --allow-server-ssh.</string>
|
||||
|
||||
<string id="RosenpassEnabled_Name">Enable Rosenpass</string>
|
||||
<string id="RosenpassEnabled_Help">Enables Rosenpass post-quantum key exchange on WireGuard tunnels. Both peers must support it.</string>
|
||||
|
||||
<string id="RosenpassPermissive_Name">Rosenpass permissive</string>
|
||||
<string id="RosenpassPermissive_Help">When enabled, the client falls back to plain WireGuard if a peer does not support Rosenpass; otherwise it refuses the connection.</string>
|
||||
|
||||
<string id="WireguardPort_Name">WireGuard port</string>
|
||||
<string id="WireguardPort_Help">UDP port used by the local WireGuard interface. Allowed range: 1-65535.</string>
|
||||
|
||||
<string id="SplitTunnel_Name">Split tunnel</string>
|
||||
<string id="SplitTunnel_Help">Restrict the NetBird tunnel to or from a chosen list of application package names. Choose either the allow mode (only the listed apps route through NetBird) or the disallow mode (the listed apps bypass NetBird; everything else routes through). The mode is mutually exclusive — only one can be active at a time. Android-only at the daemon level; Windows/macOS/iOS clients ignore this policy.</string>
|
||||
<string id="SplitTunnel_Allow">Allow only listed apps (everything else bypasses)</string>
|
||||
<string id="SplitTunnel_Disallow">Disallow listed apps (everything else routes)</string>
|
||||
|
||||
<!-- UI -->
|
||||
<string id="DisableUpdateSettings_Name">Disable update settings</string>
|
||||
<string id="DisableUpdateSettings_Help">When enabled, blocks every configuration change from the client UI and from the CLI (netbird up / login / setconfig). The Settings view stays viewable but read-only. Equivalent to --disable-update-settings.</string>
|
||||
|
||||
<string id="DisableProfiles_Name">Disable profiles</string>
|
||||
<string id="DisableProfiles_Help">When enabled, the client UI/CLI cannot list, create, switch or remove NetBird connection profiles. Equivalent to --disable-profiles.</string>
|
||||
|
||||
<string id="DisableNetworks_Name">Disable networks</string>
|
||||
<string id="DisableNetworks_Help">When enabled, the client UI/CLI cannot list, select or deselect NetBird networks (the corresponding daemon RPCs return Unavailable). Equivalent to --disable-networks.</string>
|
||||
|
||||
<string id="DisableMetricsCollection_Name">Disable metrics collection</string>
|
||||
<string id="DisableMetricsCollection_Help">When enabled, the client does not collect or report local usage metrics.</string>
|
||||
|
||||
</stringTable>
|
||||
<presentationTable>
|
||||
|
||||
<presentation id="ManagementURL_Pres">
|
||||
<textBox refId="ManagementURL_Text">
|
||||
<label>Management URL:</label>
|
||||
<defaultValue>https://api.netbird.io:443</defaultValue>
|
||||
</textBox>
|
||||
</presentation>
|
||||
|
||||
<presentation id="PreSharedKey_Pres">
|
||||
<textBox refId="PreSharedKey_Text">
|
||||
<label>Pre-shared key:</label>
|
||||
</textBox>
|
||||
</presentation>
|
||||
|
||||
<presentation id="WireguardPort_Pres">
|
||||
<decimalTextBox refId="WireguardPort_Decimal" defaultValue="51820">WireGuard UDP port:</decimalTextBox>
|
||||
</presentation>
|
||||
|
||||
<presentation id="SplitTunnel_Pres">
|
||||
<dropdownList refId="SplitTunnel_Mode" defaultItem="0">Mode:</dropdownList>
|
||||
<textBox refId="SplitTunnel_Apps">
|
||||
<label>Package names (comma-separated):</label>
|
||||
</textBox>
|
||||
</presentation>
|
||||
|
||||
</presentationTable>
|
||||
</resources>
|
||||
</policyDefinitionResources>
|
||||
223
public/docs-static/files/netbird.admx
Normal file
223
public/docs-static/files/netbird.admx
Normal file
@@ -0,0 +1,223 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
revision="1.0"
|
||||
schemaVersion="1.0"
|
||||
xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
|
||||
<policyNamespaces>
|
||||
<target prefix="netbird" namespace="NetBird.Policies.Client" />
|
||||
</policyNamespaces>
|
||||
<resources minRequiredRevision="1.0" />
|
||||
<supportedOn>
|
||||
<definitions>
|
||||
<definition name="SUPPORTED_NetBird_All" displayName="$(string.SUPPORTED_NetBird_All)" />
|
||||
</definitions>
|
||||
</supportedOn>
|
||||
<categories>
|
||||
<category name="NetBird" displayName="$(string.NetBird_Category)" />
|
||||
</categories>
|
||||
<policies>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- TOP-LEVEL: foundational identity / authentication -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<policy name="ManagementURL"
|
||||
class="Machine"
|
||||
displayName="$(string.ManagementURL_Name)"
|
||||
explainText="$(string.ManagementURL_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
presentation="$(presentation.ManagementURL_Pres)">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<elements>
|
||||
<text id="ManagementURL_Text" valueName="ManagementURL" required="true" />
|
||||
</elements>
|
||||
</policy>
|
||||
|
||||
<policy name="PreSharedKey"
|
||||
class="Machine"
|
||||
displayName="$(string.PreSharedKey_Name)"
|
||||
explainText="$(string.PreSharedKey_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
presentation="$(presentation.PreSharedKey_Pres)">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<elements>
|
||||
<text id="PreSharedKey_Text" valueName="PreSharedKey" />
|
||||
</elements>
|
||||
</policy>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- SETTINGS: engine / runtime / connection behavior -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<policy name="DisableAutoConnect"
|
||||
class="Machine"
|
||||
displayName="$(string.DisableAutoConnect_Name)"
|
||||
explainText="$(string.DisableAutoConnect_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="DisableAutoConnect">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="DisableClientRoutes"
|
||||
class="Machine"
|
||||
displayName="$(string.DisableClientRoutes_Name)"
|
||||
explainText="$(string.DisableClientRoutes_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="DisableClientRoutes">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="DisableServerRoutes"
|
||||
class="Machine"
|
||||
displayName="$(string.DisableServerRoutes_Name)"
|
||||
explainText="$(string.DisableServerRoutes_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="DisableServerRoutes">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="BlockInbound"
|
||||
class="Machine"
|
||||
displayName="$(string.BlockInbound_Name)"
|
||||
explainText="$(string.BlockInbound_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="BlockInbound">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="AllowServerSSH"
|
||||
class="Machine"
|
||||
displayName="$(string.AllowServerSSH_Name)"
|
||||
explainText="$(string.AllowServerSSH_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="AllowServerSSH">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="RosenpassEnabled"
|
||||
class="Machine"
|
||||
displayName="$(string.RosenpassEnabled_Name)"
|
||||
explainText="$(string.RosenpassEnabled_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="RosenpassEnabled">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="RosenpassPermissive"
|
||||
class="Machine"
|
||||
displayName="$(string.RosenpassPermissive_Name)"
|
||||
explainText="$(string.RosenpassPermissive_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="RosenpassPermissive">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="WireguardPort"
|
||||
class="Machine"
|
||||
displayName="$(string.WireguardPort_Name)"
|
||||
explainText="$(string.WireguardPort_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
presentation="$(presentation.WireguardPort_Pres)">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<elements>
|
||||
<decimal id="WireguardPort_Decimal" valueName="WireguardPort"
|
||||
minValue="1" maxValue="65535" required="true" />
|
||||
</elements>
|
||||
</policy>
|
||||
|
||||
<policy name="SplitTunnel"
|
||||
class="Machine"
|
||||
displayName="$(string.SplitTunnel_Name)"
|
||||
explainText="$(string.SplitTunnel_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
presentation="$(presentation.SplitTunnel_Pres)">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<elements>
|
||||
<enum id="SplitTunnel_Mode" valueName="SplitTunnelMode" required="true">
|
||||
<item displayName="$(string.SplitTunnel_Allow)"><value><string>allow</string></value></item>
|
||||
<item displayName="$(string.SplitTunnel_Disallow)"><value><string>disallow</string></value></item>
|
||||
</enum>
|
||||
<text id="SplitTunnel_Apps" valueName="SplitTunnelApps" required="true" />
|
||||
</elements>
|
||||
</policy>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- UI: visibility / UX kill switches -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<policy name="DisableUpdateSettings"
|
||||
class="Machine"
|
||||
displayName="$(string.DisableUpdateSettings_Name)"
|
||||
explainText="$(string.DisableUpdateSettings_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="DisableUpdateSettings">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="DisableProfiles"
|
||||
class="Machine"
|
||||
displayName="$(string.DisableProfiles_Name)"
|
||||
explainText="$(string.DisableProfiles_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="DisableProfiles">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="DisableNetworks"
|
||||
class="Machine"
|
||||
displayName="$(string.DisableNetworks_Name)"
|
||||
explainText="$(string.DisableNetworks_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="DisableNetworks">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
<policy name="DisableMetricsCollection"
|
||||
class="Machine"
|
||||
displayName="$(string.DisableMetricsCollection_Name)"
|
||||
explainText="$(string.DisableMetricsCollection_Help)"
|
||||
key="Software\Policies\NetBird"
|
||||
valueName="DisableMetricsCollection">
|
||||
<parentCategory ref="NetBird" />
|
||||
<supportedOn ref="SUPPORTED_NetBird_All" />
|
||||
<enabledValue><decimal value="1" /></enabledValue>
|
||||
<disabledValue><decimal value="0" /></disabledValue>
|
||||
</policy>
|
||||
|
||||
</policies>
|
||||
</policyDefinitions>
|
||||
@@ -766,6 +766,7 @@ export const docsNavigation = [
|
||||
links: [
|
||||
{ title: 'Profiles', href: '/client/profiles' },
|
||||
{ title: 'Environment Variables', href: '/client/environment-variables' },
|
||||
{ title: 'MDM Integration', href: '/client/mdm-integration' },
|
||||
{
|
||||
title: 'Settings',
|
||||
isOpen: false,
|
||||
|
||||
387
src/pages/client/mdm-integration.mdx
Normal file
387
src/pages/client/mdm-integration.mdx
Normal file
@@ -0,0 +1,387 @@
|
||||
import { Note, Warning } from "@/components/mdx";
|
||||
|
||||
export const description =
|
||||
"Enforce NetBird client configuration through your MDM channel (Intune, Jamf, Kandji, Mosyle, Workspace ONE, JumpCloud, Group Policy). Push management URL, kill switches, and other settings to a fleet of Windows and macOS devices.";
|
||||
|
||||
# MDM Integration
|
||||
|
||||
NetBird's client honors policies pushed by your Mobile Device Management
|
||||
(MDM) channel, so an administrator can enforce configuration across a
|
||||
fleet of devices instead of touching each machine. On every supported
|
||||
platform the daemon reads from the **OS-native managed-configuration
|
||||
store** that your MDM already writes to. No agent of ours sits between
|
||||
you and the MDM provider; whatever you can push to that store (manually,
|
||||
via Group Policy, via a Configuration Profile, via your MDM console)
|
||||
becomes effective NetBird policy.
|
||||
|
||||
This page covers Windows and macOS. iOS and Android support is on the
|
||||
roadmap.
|
||||
|
||||
## At a glance
|
||||
|
||||
| Platform | Where NetBird reads policy | How an admin writes it |
|
||||
| --- | --- | --- |
|
||||
| Windows | `HKLM\Software\Policies\NetBird` (registry) | Group Policy (ADMX) · Intune ADMX ingestion or OMA-URI · `reg import` · MDM-vendor scripts |
|
||||
| macOS | `/Library/Managed Preferences/io.netbird.client.plist` | Configuration Profile (`.mobileconfig`) pushed by your MDM, targeting bundle id `io.netbird.client` |
|
||||
|
||||
Both backends are the de-facto convention for desktop apps (the same
|
||||
shape Chrome, Edge, Firefox, Zoom, Tailscale, Citrix Workspace, and
|
||||
others use). Any MDM that supports the platform also supports NetBird —
|
||||
there is no NetBird-specific integration to build.
|
||||
|
||||
## How enforcement works
|
||||
|
||||
When NetBird starts, and every minute while it runs, the daemon:
|
||||
|
||||
1. Reads the platform-native managed-configuration store.
|
||||
2. Merges the values **on top of every other configuration layer**
|
||||
(defaults → on-disk profile → environment variables → CLI/UI input →
|
||||
**MDM**). MDM always wins.
|
||||
3. Locks any field that came from the MDM source. Attempts to change
|
||||
that field from the GUI, the CLI (`netbird up --flag=...`, `netbird
|
||||
login --flag=...`) or via direct gRPC are rejected with a clear
|
||||
error listing the locked fields. The client UI greys these fields
|
||||
out and tags them with **(MDM)** so the user knows they cannot
|
||||
change them.
|
||||
4. If the MDM payload changes (admin pushes new values), the change
|
||||
takes effect within ~1 minute on the device — no client restart
|
||||
needed.
|
||||
|
||||
<Note>
|
||||
MDM is **authoritative**. When a key is present in the MDM payload, the
|
||||
MDM value wins regardless of whether it is `true` or `false`. An admin
|
||||
pushing `disableNetworks=false` via MDM re-enables the feature even on a
|
||||
host that was installed with `--disable-networks`. The MDM payload is
|
||||
the source of truth as long as the policy is in place. MDM-supplied
|
||||
values are also never written back to the on-disk profile, so removing
|
||||
the policy at the MDM side takes effect on the next reload with no
|
||||
stale residue on the device.
|
||||
</Note>
|
||||
|
||||
## Policy keys reference
|
||||
|
||||
The same 16 keys apply on every platform. Names are camelCase in the
|
||||
managed-configuration payload; the Windows ADMX template renders the
|
||||
PascalCase variant in the Group Policy Editor — both are recognized.
|
||||
|
||||
| Key | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `managementURL` | string | Override the management server URL (e.g. `https://api.netbird.io:443` or a self-hosted URL). |
|
||||
| `preSharedKey` | string | WireGuard pre-shared key. Treated as secret and redacted in logs. |
|
||||
| `wireguardPort` | integer | UDP port the local WireGuard interface binds to. Range `1–65535`. |
|
||||
| `allowServerSSH` | boolean | Allow the embedded NetBird SSH server on this peer. |
|
||||
| `disableAutoConnect` | boolean | Skip auto-connecting on startup; require an explicit `netbird up`. |
|
||||
| `rosenpassEnabled` | boolean | Turn on the post-quantum Rosenpass key exchange. |
|
||||
| `rosenpassPermissive` | boolean | Permissive mode for Rosenpass (interop with non-Rosenpass peers). |
|
||||
| `blockInbound` | boolean | Drop all inbound traffic except established/related — kill-switch style. |
|
||||
| `disableClientRoutes` | boolean | This peer does not route traffic to other peers. |
|
||||
| `disableServerRoutes` | boolean | This peer is not a router for others. |
|
||||
| `disableMetricsCollection` | boolean | Disable anonymous usage telemetry. |
|
||||
| `disableUpdateSettings` | boolean | Block every configuration change from UI or CLI on this device (read-only mode). |
|
||||
| `disableProfiles` | boolean | Hide the profile menu in the GUI and reject profile CRUD via CLI. |
|
||||
| `disableNetworks` | boolean | Hide the Networks / Exit Node menus in the GUI and reject the related RPCs. |
|
||||
| `splitTunnelMode` | string | `allow` or `disallow` — split-tunnel policy mode (Android only at the client level; harmless on desktop). |
|
||||
| `splitTunnelApps` | string | Comma-separated list of package names that the split-tunnel mode applies to (Android only). |
|
||||
|
||||
### Notes on a few keys
|
||||
|
||||
- `disableUpdateSettings` and `disableProfiles` overlap with the
|
||||
service-install CLI flags `--disable-update-settings` and
|
||||
`--disable-profiles`. Either source can disable the feature; the MDM
|
||||
value wins when present.
|
||||
- `disableUpdateSettings` keeps the Settings view in the GUI visible
|
||||
(so users can inspect current values) but rejects every attempt to
|
||||
save changes. Use it for read-only fleets.
|
||||
- `splitTunnelMode` and `splitTunnelApps` are wired into Android's
|
||||
`VpnService.Builder.addAllowedApplication()` flow; on Windows and
|
||||
macOS the daemon parses the keys but ignores them. They are safe to
|
||||
ship in a cross-platform payload.
|
||||
- The `disableMetricsCollection` key is reserved for an upcoming
|
||||
metrics integration; the client recognizes it today but no metrics
|
||||
pipeline is shipped yet.
|
||||
|
||||
## Windows
|
||||
|
||||
The NetBird daemon reads policies from
|
||||
`HKLM\Software\Policies\NetBird`. Anything that ends up under that
|
||||
registry key — through whichever delivery channel — becomes policy.
|
||||
The shapes are:
|
||||
|
||||
| Value name | Registry type | Example |
|
||||
| --- | --- | --- |
|
||||
| `ManagementURL`, `PreSharedKey`, `SplitTunnelMode`, `SplitTunnelApps` | `REG_SZ` | `"https://api.netbird.io:443"` |
|
||||
| All `Disable*` flags, `AllowServerSSH`, `RosenpassEnabled`, `RosenpassPermissive` | `REG_DWORD` (0 / 1) | `0x00000001` |
|
||||
| `WireguardPort` | `REG_DWORD` | `0x0000ca6c` (51820 decimal) |
|
||||
|
||||
Choose one of the delivery channels below. All four converge on the
|
||||
same registry key.
|
||||
|
||||
### Group Policy (on-prem AD / local gpedit)
|
||||
|
||||
1. Copy the ADMX/ADML files into the system Policy Definitions store:
|
||||
- Place `netbird.admx` in `C:\Windows\PolicyDefinitions\`.
|
||||
- Place `netbird.adml` in `C:\Windows\PolicyDefinitions\en-US\`.
|
||||
2. Open `gpedit.msc` (or the AD Group Policy Management Editor).
|
||||
3. Navigate to **Computer Configuration → Administrative Templates →
|
||||
NetBird**.
|
||||
4. Edit any policy (e.g. **Management URL**), set it to **Enabled**
|
||||
with the desired value, and click **OK**.
|
||||
5. Run `gpupdate /force` on each target device (or wait for the
|
||||
periodic refresh).
|
||||
6. Verify with `reg query HKLM\Software\Policies\NetBird` — the values
|
||||
you set should appear there.
|
||||
|
||||
Download the templates: <a href="/docs-static/files/netbird.admx" download>netbird.admx</a> / <a href="/docs-static/files/netbird.adml" download>netbird.adml</a>.
|
||||
|
||||
### Microsoft Intune (ADMX ingestion)
|
||||
|
||||
Recommended for cloud-managed Windows fleets.
|
||||
|
||||
1. In the Intune admin center, go to **Devices → Configuration → Import
|
||||
ADMX**, upload `netbird.admx` together with `netbird.adml`. Wait for
|
||||
the **Available** status.
|
||||
2. Create a new **Configuration Profile → Templates → Imported
|
||||
Administrative templates → NetBird**.
|
||||
3. Configure the policies you want to enforce.
|
||||
4. Assign the profile to your device group(s) and save.
|
||||
|
||||
Devices pick up the policy on the next Intune sync (typically within
|
||||
8 hours, sooner if you trigger a manual sync from the device). The
|
||||
values end up in `HKLM\Software\Policies\NetBird`.
|
||||
|
||||
### Microsoft Intune (custom OMA-URI)
|
||||
|
||||
If you cannot ingest the ADMX template, you can push individual values
|
||||
via OMA-URI under
|
||||
`./Device/Vendor/MSFT/Policy/ConfigOperations/ADMXInstall/...` or via
|
||||
the Registry CSP at
|
||||
`./Device/Vendor/MSFT/Registry/HKEY_LOCAL_MACHINE/Software/Policies/NetBird/<ValueName>`.
|
||||
ADMX ingestion is simpler and gives admins the same UI as on-prem GPO,
|
||||
so prefer that.
|
||||
|
||||
### `.reg` import (single source of truth)
|
||||
|
||||
For fleets without an MDM, or as a quick-test path, you can carry the
|
||||
whole policy in a single `.reg` file:
|
||||
|
||||
1. Configure the policy values on a reference machine (via `gpedit` or
|
||||
`reg add`).
|
||||
2. Export the key:
|
||||
```
|
||||
reg export "HKLM\Software\Policies\NetBird" netbird-policy.reg /y
|
||||
```
|
||||
3. Distribute the resulting file and apply with:
|
||||
```
|
||||
reg import netbird-policy.reg
|
||||
```
|
||||
|
||||
Download a sample: <a href="/docs-static/files/netbird-policy.reg" download>netbird-policy.reg</a>.
|
||||
|
||||
### JumpCloud
|
||||
|
||||
NetBird ships a JumpCloud companion script: <a href="/docs-static/files/netbird-policy.reg.ps1" download>netbird-policy.reg.ps1</a>. To use it:
|
||||
|
||||
1. In the JumpCloud admin console, go to **Device Management →
|
||||
Commands → +**.
|
||||
2. Type: **Windows PowerShell**. Run as: **SYSTEM**.
|
||||
3. Paste `netbird-policy.reg.ps1` verbatim into the command body.
|
||||
4. In the same command, attach the `netbird-policy.reg` file you
|
||||
produced above. JumpCloud copies attached files into the command's
|
||||
working directory before invoking the script.
|
||||
5. Bind the command to the target system group and run it.
|
||||
|
||||
The script wipes the existing `HKLM\Software\Policies\NetBird` key
|
||||
before importing the `.reg`, so the `.reg` is the **single source of
|
||||
truth** for that device. To unset all policy, attach an empty (header-
|
||||
only) `.reg`; the daemon will pick up the absence on the next reload.
|
||||
|
||||
## macOS
|
||||
|
||||
The NetBird daemon reads policy from
|
||||
`/Library/Managed Preferences/io.netbird.client.plist`. macOS writes
|
||||
that file when an MDM provider pushes a Configuration Profile whose
|
||||
`com.apple.ManagedClient.preferences` payload targets the bundle id
|
||||
`io.netbird.client`.
|
||||
|
||||
<Warning>
|
||||
macOS wipes the contents of `/Library/Managed Preferences/` on every
|
||||
boot if the device is not MDM-enrolled. Manual `defaults write` works
|
||||
for a quick test but does not survive a reboot on an un-enrolled Mac.
|
||||
Use a real MDM channel for production rollouts.
|
||||
</Warning>
|
||||
|
||||
### Custom Configuration Profile (recommended)
|
||||
|
||||
This is the canonical macOS path and works with every MDM
|
||||
(Jamf, Kandji, Mosyle, Microsoft Intune for Mac, Workspace ONE,
|
||||
JumpCloud, Apple Configurator 2, etc.).
|
||||
|
||||
1. Start from the template <a href="/docs-static/files/netbird-macos.mobileconfig" download>netbird-macos.mobileconfig</a>. Open it in your editor (or in
|
||||
[iMazing Profile Editor](https://imazing.com/profile-editor) /
|
||||
[ProfileCreator](https://github.com/ProfileCreator/ProfileCreator)).
|
||||
2. Inside the `mcx_preference_settings` dictionary, set the keys you
|
||||
want to enforce. Keep the bundle id `io.netbird.client` as the
|
||||
preference domain.
|
||||
3. Replace the placeholder `PayloadUUID` values with freshly generated
|
||||
UUIDs (`uuidgen` on macOS) so each deployment has unique ids.
|
||||
4. (Optional, recommended for production) sign the profile with your
|
||||
organization's Developer ID Installer certificate using
|
||||
`productsign` — unsigned profiles on Sonoma/Sequoia/Tahoe require
|
||||
an extra user confirmation on install.
|
||||
5. Upload the resulting `.mobileconfig` to your MDM as a **Custom
|
||||
Configuration Profile** and scope it to the target device group.
|
||||
|
||||
Verify on a target device with:
|
||||
|
||||
```bash
|
||||
sudo defaults read "/Library/Managed Preferences/io.netbird.client"
|
||||
```
|
||||
|
||||
The output should match the keys you set in the profile.
|
||||
|
||||
### MDM-specific notes
|
||||
|
||||
- **Jamf Pro**: upload as **Computers → Configuration Profiles → New →
|
||||
Application & Custom Settings → External Applications → Upload File
|
||||
(Plist file)** for the preference domain `io.netbird.client`.
|
||||
- **Kandji**: use the **Custom Profile** assignment library item.
|
||||
- **Mosyle**: **Profiles → Add new profile → Custom Settings** with
|
||||
domain `io.netbird.client`.
|
||||
- **Microsoft Intune (for Mac)**: **Devices → Configuration → Create
|
||||
profile → macOS → Templates → Custom**, upload the `.mobileconfig`.
|
||||
- **Apple Configurator 2** (no MDM, ideal for testing on a tethered
|
||||
device): drag the `.mobileconfig` onto the device in Configurator and
|
||||
push.
|
||||
|
||||
### JumpCloud
|
||||
|
||||
JumpCloud supports two delivery channels for the NetBird policy on
|
||||
macOS. Pick whichever fits how your fleet is enrolled.
|
||||
|
||||
#### MDM Custom Configuration Profile (recommended for MDM-enrolled fleets)
|
||||
|
||||
If your Macs are MDM-enrolled with JumpCloud, push the policy as a
|
||||
managed-preferences plist:
|
||||
|
||||
1. In the JumpCloud admin console, open **Policy Management →
|
||||
Policies → +** and choose the **Mac** platform.
|
||||
2. Pick the **MDM Custom Configuration Profile** policy template.
|
||||
3. Upload <a href="/docs-static/files/io.netbird.client.plist" download>io.netbird.client.plist</a>
|
||||
as the plist payload. Edit the file before upload to enable just
|
||||
the keys you want to enforce — leave the rest commented out.
|
||||
4. Bind the policy to the target Device Group and save.
|
||||
|
||||
Notes:
|
||||
|
||||
- JumpCloud's **MDM Custom Configuration Profile** accepts a bare
|
||||
managed-preferences plist (the inner Apple managed-prefs dictionary)
|
||||
— **not** a full `.mobileconfig` envelope. Uploading
|
||||
`netbird-macos.mobileconfig` will be rejected. Use the bare
|
||||
`io.netbird.client.plist` for this code path; reserve
|
||||
`netbird-macos.mobileconfig` for other MDMs that expect the full
|
||||
Configuration Profile shape.
|
||||
- Keep the filename as `io.netbird.client.plist`. The Apple
|
||||
convention for managed-preferences plists is
|
||||
`<bundle.id>.plist` (this is how macOS materializes the file at
|
||||
`/Library/Managed Preferences/<bundle.id>.plist`), and JumpCloud's
|
||||
policy form does not currently expose a separate bundle-identifier
|
||||
field — keeping the canonical filename is the safest path. If your
|
||||
JumpCloud console version surfaces a bundle-id / preference-domain
|
||||
field elsewhere in the policy wizard, set it to `io.netbird.client`
|
||||
too.
|
||||
|
||||
JumpCloud wraps the plist into an Apple Configuration Profile and
|
||||
pushes it via the MDM channel. The OS materializes the file at
|
||||
`/Library/Managed Preferences/io.netbird.client.plist`, where the
|
||||
NetBird daemon picks it up within the next 1-minute reload tick.
|
||||
Removing the policy from JumpCloud removes the file on the next sync,
|
||||
which un-locks the corresponding fields on the client.
|
||||
|
||||
#### Shell Command (no MDM enrollment required)
|
||||
|
||||
If your fleet is JumpCloud-managed but not MDM-enrolled, NetBird ships
|
||||
a companion script: <a href="/docs-static/files/netbird-macos.sh" download>netbird-macos.sh</a>. It is the macOS
|
||||
counterpart of the Windows `.reg.ps1` script — same fleet, different
|
||||
backend:
|
||||
|
||||
1. Edit the `### POLICY VALUES ###` block at the top of the script;
|
||||
set the variables for the keys you want to enforce and leave the
|
||||
rest at `$NULL`.
|
||||
2. In the JumpCloud admin console, go to **Device Management →
|
||||
Commands → +**. Type: **Mac, Shell**. Run as: **root**.
|
||||
3. Paste the edited script verbatim into the command body.
|
||||
4. Bind to the target system group and run.
|
||||
|
||||
The script writes
|
||||
`/Library/Managed Preferences/io.netbird.client.plist`, sets ownership
|
||||
to `root:wheel` with mode `644`, and kicks the NetBird daemon so the
|
||||
change applies immediately. On MDM-enrolled devices the file survives
|
||||
reboots; on un-enrolled devices the file is wiped at the next reboot
|
||||
(macOS-imposed). Prefer the Custom Mac Application Settings policy
|
||||
above when the fleet is enrolled.
|
||||
|
||||
## Verifying enforcement
|
||||
|
||||
On any platform, the cleanest verification is the daemon's own debug
|
||||
dump:
|
||||
|
||||
```bash
|
||||
netbird debug config
|
||||
```
|
||||
|
||||
The response includes a `mDMManagedFields` array that lists every key
|
||||
the daemon is currently honoring from the MDM source. If a key you
|
||||
expected to be locked is missing from that array, the MDM payload did
|
||||
not reach the device (or used a value name the daemon does not
|
||||
recognize).
|
||||
|
||||
The client UI mirrors the same state: any submenu item, settings field,
|
||||
or kill switch driven by MDM appears greyed out with a **(MDM)** tag
|
||||
next to its label.
|
||||
|
||||
Daemon logs (`/var/log/netbird/client.log` on Linux/macOS,
|
||||
`%ProgramData%\Netbird\` on Windows) contain a one-line
|
||||
`MDM enrolled with N managed key(s): [...]` entry on every reload, plus
|
||||
one `MDM override <key> = <value>` line per applied key. Secrets are
|
||||
redacted.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**The policy did not apply at all.**
|
||||
Check that the daemon can see the source.
|
||||
|
||||
- Windows: `reg query HKLM\Software\Policies\NetBird` — if empty, the
|
||||
delivery channel did not write the values. Check `gpresult /h` for
|
||||
GPO failures or the Intune sync status in **Settings → Accounts →
|
||||
Access work or school → Info → Sync**.
|
||||
- macOS:
|
||||
`sudo defaults read /Library/Managed\ Preferences/io.netbird.client`
|
||||
— if the file is missing, the MDM payload was not pushed or the
|
||||
bundle id in the profile does not match `io.netbird.client`.
|
||||
|
||||
**The key shows up in the registry / plist but not in `netbird debug
|
||||
config` `mDMManagedFields`.**
|
||||
The value name is misspelled. Names are case-insensitive but must match
|
||||
one of the keys in the reference table above. The daemon log emits an
|
||||
`MDM ignoring unknown <key>` warning when this happens.
|
||||
|
||||
**The user can still change the field from the GUI / CLI.**
|
||||
The change is being rejected by the daemon but the UI may not have
|
||||
caught up yet. The UI refreshes within a couple of seconds after a
|
||||
config change; try closing and reopening the Settings window. If the
|
||||
change actually sticks, double-check that the MDM payload is still
|
||||
present on the device — it may have been removed by another policy.
|
||||
|
||||
**On macOS, the file disappears after a reboot.**
|
||||
The device is not MDM-enrolled. macOS protects
|
||||
`/Library/Managed Preferences/` by wiping it at boot if no MDM
|
||||
controls the directory. Enroll the device with a real MDM provider for
|
||||
persistent rollouts.
|
||||
|
||||
**My MDM provider is not in the list above.**
|
||||
Any MDM that can push a Configuration Profile on macOS or write a
|
||||
registry value on Windows works. The mechanism is OS-native, not
|
||||
NetBird-specific. If you hit a quirk specific to your provider, please
|
||||
open an issue at
|
||||
https://github.com/netbirdio/netbird/issues with the provider name and
|
||||
what you observed.
|
||||
Reference in New Issue
Block a user