docs: document the MDM keys added since the page was written, and iOS

The reference table listed 18 of the 23 keys the client recognises, and
claimed a count of 16. Six keys were missing from every downloadable
template as well, including lazyConnection, which the page already
documented:

- lazyConnection, disableAdvancedView, enableLocalMetrics,
  localMetricsAddress, allowRemoteJobs and debugBundleUploadURL are now
  in the reference table and in netbird.admx/.adml, io.netbird.client.plist,
  netbird-macos.mobileconfig, netbird-macos.sh and netbird-policy.reg.
- Notes call out the two pairs that read as related but are not:
  disableMetricsCollection (anonymous telemetry) versus
  enableLocalMetrics (the client's own Prometheus endpoint), and
  disableAdvancedView being UI-only, so unlike its neighbours it never
  causes a request to be rejected.

iOS and tvOS now have a section of their own. The channel is Apple's
Managed App Configuration against bundle id io.netbird.app
(io.netbird.app.tv on tvOS), with a new netbird-ios-appconfig.plist
template and per-provider instructions. The section states what the
policy reaches today: the app locks its interface and refuses
configuration changes, while the tunnel engine runs in a Network
Extension that does not receive the app configuration, so keys the
engine applies to the connection are not enforced there yet.

Two template defects found on the way:

- netbird-macos.mobileconfig was not well-formed XML. Its header
  comment contained a double hyphen, which XML forbids inside a
  comment, so plutil -lint and any strict parser rejected the file an
  admin downloaded.
- netbird-policy.reg had one LF-terminated line among CRLF ones;
  normalised, with the UTF-16LE encoding reg import requires preserved.

Verified with npm run build and npm run lint:mdx, plus XML well-formedness
on all five XML templates, bash -n on the shell template, and a
cross-reference check that every string and presentation the ADMX
references is defined in the ADML.
This commit is contained in:
riccardom
2026-09-17 09:17:25 +02:00
parent fed7b1e25b
commit 182b1b9e87
8 changed files with 447 additions and 17 deletions
@@ -85,6 +85,15 @@
<false/>
-->
<!-- ===== Lazy connections =====
true forces lazy connections on, false forces them off, and an
absent key defers to the Management setting. NB_LAZY_CONN
takes precedence when both are configured. -->
<!--
<key>lazyConnection</key>
<true/>
-->
<!-- ===== WireGuard UDP port =====
Range 1-65535. Omit to keep the daemon default. -->
<!--
@@ -92,6 +101,35 @@
<integer>51820</integer>
-->
<!-- ===== Remote jobs =====
allowRemoteJobs : allow management-requested remote jobs
(e.g. debug bundles) on this peer. Off by
default. Present at any value locks the
client toggle, so false pins it off.
debugBundleUploadURL: override the upload service used for
debug bundles produced by remote jobs,
taking precedence over the value
requested by Management. Must be an
https URL with a host. -->
<!--
<key>allowRemoteJobs</key>
<false/>
<key>debugBundleUploadURL</key>
<string>https://uploads.example.com</string>
-->
<!-- ===== Local Prometheus metrics endpoint =====
Unrelated to disableMetricsCollection, which governs anonymous
usage telemetry. -->
<!--
<key>enableLocalMetrics</key>
<true/>
<key>localMetricsAddress</key>
<string>127.0.0.1:9191</string>
-->
<!-- ===== UI / lockdown kill switches =====
disableUpdateSettings : block every config change from UI and CLI
on this device (Settings view stays
@@ -99,6 +137,9 @@
disableProfiles : hide the profile menu, reject profile CRUD.
disableNetworks : hide the Networks / Exit Node menus,
reject the related RPCs.
disableAdvancedView : hide the advanced section of the UI.
UI-only: nothing is rejected on its
account.
disableMetricsCollection: opt out of anonymous usage telemetry. -->
<!--
<key>disableUpdateSettings</key>
@@ -110,6 +151,9 @@
<key>disableNetworks</key>
<true/>
<key>disableAdvancedView</key>
<true/>
<key>disableMetricsCollection</key>
<false/>
-->
@@ -0,0 +1,110 @@
<?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 Managed App Configuration (iOS / iPadOS / tvOS).
Bundle identifier of the managed app:
io.netbird.app (iOS, iPadOS)
io.netbird.app.tv (tvOS)
Delivery: your MDM sends this dictionary alongside the managed app
(Intune "App configuration policies" → Enter XML data; Jamf Pro
"Mobile Device Apps → App Configuration"; the equivalent App
Configuration field on Kandji, Mosyle, Workspace ONE, JumpCloud).
iOS hands it to the app, which reads it on every policy load.
The app must be installed BY the MDM. An app the user installed
from the App Store themselves cannot receive a configuration, and
NetBird then runs with no policy at all.
Editing this file:
- Delete or comment out every key you do NOT want to enforce. An
absent key means "no enforcement" for that field.
- Some MDM consoles want only the inner <dict>; paste from
<dict> to </dict> and drop the plist envelope in that case.
- Validate locally with:
plutil -lint netbird-ios-appconfig.plist
Scope on iOS: these keys lock and reshape the app's interface, and
the app refuses configuration changes accordingly. Keys the tunnel
engine applies to the connection itself are not yet enforced on
iOS, because the engine runs in a Network Extension that does not
receive the app configuration. See the iOS section of
docs/src/pages/client/mdm-integration.mdx.
Keys with no iOS surface (allowServerSSH, disableServerRoutes,
disableAutostart, disableMetricsCollection, enableLocalMetrics,
localMetricsAddress, debugBundleUploadURL, splitTunnelMode,
splitTunnelApps) are parsed and ignored here, so one payload can be
shared with desktop devices.
blockInbound, wireguardPort and lazyConnection are left out of this
template on purpose: they are applied by the tunnel engine, which
on iOS does not receive this configuration yet. They do no harm in
a shared payload, they simply have no effect on an iOS device.
-->
<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
client treats as a deliberate empty-PSK enforcement.
-->
<!--
<key>preSharedKey</key>
<string>REPLACE_ME</string>
-->
<!-- ===== Interface lockdown =====
disableUpdateSettings : read-only mode — nothing is hidden,
every configuration control is locked.
disableProfiles : hide the Profiles section (no create,
switch or remove).
disableNetworks : remove the Resources tab.
disableAdvancedView : hide Advanced (on tvOS, the whole
section). -->
<!--
<key>disableUpdateSettings</key>
<true/>
<key>disableProfiles</key>
<true/>
<key>disableNetworks</key>
<true/>
<key>disableAdvancedView</key>
<true/>
-->
<!-- ===== Connection controls =====
Each of these locks its own control in the app. -->
<!--
<key>disableAutoConnect</key>
<false/>
<key>disableClientRoutes</key>
<false/>
<key>rosenpassEnabled</key>
<true/>
<key>rosenpassPermissive</key>
<false/>
-->
<!-- ===== Remote jobs =====
Present at any value locks the client toggle, so pushing
false pins remote jobs off. -->
<!--
<key>allowRemoteJobs</key>
<false/>
-->
</dict>
</plist>
@@ -16,7 +16,8 @@
(confirm against the signed pkg before fleet roll-out)
Distribution:
- sign with `productsign --sign "Developer ID Installer: ..." ...`
- sign with `productsign`, passing your "Developer ID Installer"
certificate to its sign flag,
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`.
@@ -121,6 +122,15 @@
<false/>
-->
<!-- ===== Lazy connections (bool) =====
true forces them on, false forces them off, an
absent key defers to the Management setting.
NB_LAZY_CONN wins over both. -->
<!--
<key>lazyConnection</key>
<true/>
-->
<!-- ===== WireGuard UDP port (int) =====
Range 1-65535. Omit to keep the default. -->
<!--
@@ -128,6 +138,29 @@
<integer>51820</integer>
-->
<!-- ===== Remote jobs =====
allowRemoteJobs is off by default; present at any
value it locks the client toggle, so false pins
remote jobs off. debugBundleUploadURL overrides the
upload service for bundles produced by remote jobs
and must be an https URL with a host. -->
<!--
<key>allowRemoteJobs</key>
<false/>
<key>debugBundleUploadURL</key>
<string>https://uploads.example.com</string>
-->
<!-- ===== Local Prometheus metrics endpoint =====
Unrelated to disableMetricsCollection below, which
governs anonymous usage telemetry. -->
<!--
<key>enableLocalMetrics</key>
<true/>
<key>localMetricsAddress</key>
<string>127.0.0.1:9191</string>
-->
<!-- ===== Split tunnel (Android-only at the daemon level)
Pushed harmlessly on macOS for fleets with mixed
desktop+mobile devices; the macOS daemon ignores it. -->
@@ -138,7 +171,9 @@
<string>com.acme.app1,com.acme.app2</string>
-->
<!-- ===== UI / kill switches (bool) ===== -->
<!-- ===== UI / kill switches (bool) =====
disableAdvancedView is UI-only: it hides the
advanced section and rejects nothing. -->
<!--
<key>disableUpdateSettings</key>
<true/>
@@ -146,6 +181,8 @@
<true/>
<key>disableNetworks</key>
<true/>
<key>disableAdvancedView</key>
<true/>
<key>disableMetricsCollection</key>
<false/>
-->
+12
View File
@@ -65,9 +65,15 @@ disableMetricsCollection="$NULL"
disableUpdateSettings="$NULL"
disableProfiles="$NULL"
disableNetworks="$NULL"
disableAdvancedView="$NULL" # UI-only: hides the advanced section, rejects nothing
rosenpassEnabled="$NULL"
rosenpassPermissive="$NULL"
lazyConnection="$NULL" # "true"/"false"; absent defers to the Management setting
wireguardPort='51820'
allowRemoteJobs="$NULL" # present at any value locks the client toggle
debugBundleUploadURL="$NULL" # https URL with a host
enableLocalMetrics="$NULL" # local Prometheus endpoint; not the usage telemetry switch
localMetricsAddress="$NULL" # default 127.0.0.1:9191
splitTunnelMode="$NULL" # "allow" or "disallow", Android-only at the daemon level
splitTunnelApps="$NULL" # comma-separated app IDs, Android-only
##############################################################################
@@ -175,9 +181,15 @@ main() {
is_set "$disableUpdateSettings" && emit_bool disableUpdateSettings "$disableUpdateSettings"
is_set "$disableProfiles" && emit_bool disableProfiles "$disableProfiles"
is_set "$disableNetworks" && emit_bool disableNetworks "$disableNetworks"
is_set "$disableAdvancedView" && emit_bool disableAdvancedView "$disableAdvancedView"
is_set "$rosenpassEnabled" && emit_bool rosenpassEnabled "$rosenpassEnabled"
is_set "$rosenpassPermissive" && emit_bool rosenpassPermissive "$rosenpassPermissive"
is_set "$lazyConnection" && emit_bool lazyConnection "$lazyConnection"
is_set "$wireguardPort" && emit_int wireguardPort "$wireguardPort"
is_set "$allowRemoteJobs" && emit_bool allowRemoteJobs "$allowRemoteJobs"
is_set "$debugBundleUploadURL" && emit_string debugBundleUploadURL "$debugBundleUploadURL"
is_set "$enableLocalMetrics" && emit_bool enableLocalMetrics "$enableLocalMetrics"
is_set "$localMetricsAddress" && emit_string localMetricsAddress "$localMetricsAddress"
is_set "$splitTunnelMode" && emit_split_tunnel_mode "$splitTunnelMode"
is_set "$splitTunnelApps" && emit_string splitTunnelApps "$splitTunnelApps"
Binary file not shown.
+31
View File
@@ -66,6 +66,24 @@
<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>
<string id="DisableAdvancedView_Name">Disable advanced view</string>
<string id="DisableAdvancedView_Help">When enabled, the client hides the advanced section of its interface. This policy only changes what the interface offers: it is not part of the daemon configuration and never causes a configuration change to be rejected. Setting it to Disabled explicitly re-enables the section.</string>
<string id="LazyConnection_Name">Lazy connections</string>
<string id="LazyConnection_Help">Local override for lazy connections. Enabled forces lazy connections on, Disabled forces them off, and leaving the policy Not Configured defers to the Management setting. The NB_LAZY_CONN environment variable takes precedence over this policy.</string>
<string id="AllowRemoteJobs_Name">Allow remote jobs</string>
<string id="AllowRemoteJobs_Help">Allows management-requested remote jobs, such as debug bundle requests, to run on this peer. Off by default; equivalent to --allow-remote-jobs. Configuring this policy at either value locks the corresponding client toggle, so Disabled pins remote jobs off.</string>
<string id="DebugBundleUploadURL_Name">Debug bundle upload URL</string>
<string id="DebugBundleUploadURL_Help">Overrides the upload service used for debug bundles produced by remote jobs, taking precedence over the value requested by Management. Must be an https URL including a host.</string>
<string id="EnableLocalMetrics_Name">Enable local metrics endpoint</string>
<string id="EnableLocalMetrics_Help">Exposes the client's local Prometheus /metrics endpoint. Unrelated to "Disable metrics collection", which governs anonymous usage telemetry.</string>
<string id="LocalMetricsAddress_Name">Local metrics listen address</string>
<string id="LocalMetricsAddress_Help">Listen address of the local Prometheus /metrics endpoint. Defaults to 127.0.0.1:9191 when the endpoint is enabled and this policy is Not Configured.</string>
</stringTable>
<presentationTable>
@@ -93,6 +111,19 @@
</textBox>
</presentation>
<presentation id="DebugBundleUploadURL_Pres">
<textBox refId="DebugBundleUploadURL_Text">
<label>Debug bundle upload URL:</label>
</textBox>
</presentation>
<presentation id="LocalMetricsAddress_Pres">
<textBox refId="LocalMetricsAddress_Text">
<label>Listen address:</label>
<defaultValue>127.0.0.1:9191</defaultValue>
</textBox>
</presentation>
</presentationTable>
</resources>
</policyDefinitionResources>
+74
View File
@@ -231,5 +231,79 @@
<disabledValue><decimal value="0" /></disabledValue>
</policy>
<policy name="DisableAdvancedView"
class="Machine"
displayName="$(string.DisableAdvancedView_Name)"
explainText="$(string.DisableAdvancedView_Help)"
key="Software\Policies\NetBird"
valueName="DisableAdvancedView">
<parentCategory ref="NetBird" />
<supportedOn ref="SUPPORTED_NetBird_All" />
<enabledValue><decimal value="1" /></enabledValue>
<disabledValue><decimal value="0" /></disabledValue>
</policy>
<policy name="LazyConnection"
class="Machine"
displayName="$(string.LazyConnection_Name)"
explainText="$(string.LazyConnection_Help)"
key="Software\Policies\NetBird"
valueName="LazyConnection">
<parentCategory ref="NetBird" />
<supportedOn ref="SUPPORTED_NetBird_All" />
<enabledValue><decimal value="1" /></enabledValue>
<disabledValue><decimal value="0" /></disabledValue>
</policy>
<policy name="AllowRemoteJobs"
class="Machine"
displayName="$(string.AllowRemoteJobs_Name)"
explainText="$(string.AllowRemoteJobs_Help)"
key="Software\Policies\NetBird"
valueName="AllowRemoteJobs">
<parentCategory ref="NetBird" />
<supportedOn ref="SUPPORTED_NetBird_All" />
<enabledValue><decimal value="1" /></enabledValue>
<disabledValue><decimal value="0" /></disabledValue>
</policy>
<policy name="DebugBundleUploadURL"
class="Machine"
displayName="$(string.DebugBundleUploadURL_Name)"
explainText="$(string.DebugBundleUploadURL_Help)"
key="Software\Policies\NetBird"
presentation="$(presentation.DebugBundleUploadURL_Pres)">
<parentCategory ref="NetBird" />
<supportedOn ref="SUPPORTED_NetBird_All" />
<elements>
<text id="DebugBundleUploadURL_Text" valueName="DebugBundleUploadURL" required="true" />
</elements>
</policy>
<policy name="EnableLocalMetrics"
class="Machine"
displayName="$(string.EnableLocalMetrics_Name)"
explainText="$(string.EnableLocalMetrics_Help)"
key="Software\Policies\NetBird"
valueName="EnableLocalMetrics">
<parentCategory ref="NetBird" />
<supportedOn ref="SUPPORTED_NetBird_All" />
<enabledValue><decimal value="1" /></enabledValue>
<disabledValue><decimal value="0" /></disabledValue>
</policy>
<policy name="LocalMetricsAddress"
class="Machine"
displayName="$(string.LocalMetricsAddress_Name)"
explainText="$(string.LocalMetricsAddress_Help)"
key="Software\Policies\NetBird"
presentation="$(presentation.LocalMetricsAddress_Pres)">
<parentCategory ref="NetBird" />
<supportedOn ref="SUPPORTED_NetBird_All" />
<elements>
<text id="LocalMetricsAddress_Text" valueName="LocalMetricsAddress" required="true" />
</elements>
</policy>
</policies>
</policyDefinitions>