mirror of
https://github.com/netbirdio/docs.git
synced 2026-10-17 02:59:04 +02:00
docs: iOS enforces every key; document when a change reaches the tunnel
The iOS section was written while the policy stopped at the app: the network extension that runs the engine has its own preferences domain, and Apple's app-configuration channel does not deliver there. The app now mirrors the configuration into the shared App Group and tells the extension when it changes, so the keys the engine applies to the connection are enforced too. What replaces the old warning is a timing caveat, which is what an admin actually needs in order to plan a rollout. The hand-off happens only while the app is running: in the foreground a change reaches the tunnel within about 30 seconds or immediately on activation, but iOS suspends a backgrounded app, so a policy pushed — or withdrawn — in that window applies the next time the app is opened. A tunnel brought up by VPN On Demand or the widget therefore runs on the last policy the app saw. tvOS keeps the old behaviour and now says so: the hand-off needs a shared app group, which does not work between the tvOS app and its extension, so only the interface is enforced there. Also corrected: blockInbound, wireguardPort and lazyConnection are no longer listed as having no effect but as having no control to lock, and debugBundleUploadURL moves out of the ignored list, since the engine applies it when a remote job produces a bundle. The app-config template carries the same three keys and the same timing note.
This commit is contained in:
@@ -25,23 +25,26 @@
|
||||
- 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.
|
||||
When a change takes effect: iOS hands this configuration to the
|
||||
NetBird app, and the app passes it to the network extension that
|
||||
runs the tunnel whenever the app is running. With the app in the
|
||||
foreground a change reaches the tunnel within about 30 seconds;
|
||||
while the app is closed or backgrounded iOS suspends it, so a
|
||||
policy pushed (or withdrawn) in that window applies the next time
|
||||
the app is opened. A tunnel brought up by VPN On Demand or the
|
||||
widget runs on the last policy the app saw.
|
||||
|
||||
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.
|
||||
tvOS: only the interface is enforced. The hand-off needs a shared
|
||||
app group, which does not work between the tvOS app and its
|
||||
extension, so the tvOS engine runs without the policy.
|
||||
|
||||
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.
|
||||
Keys that do not apply to the platform are parsed and ignored here,
|
||||
so one payload can be shared with desktop devices: allowServerSSH,
|
||||
disableServerRoutes, disableAutostart, disableMetricsCollection,
|
||||
enableLocalMetrics, localMetricsAddress, splitTunnelMode,
|
||||
splitTunnelApps.
|
||||
|
||||
See the iOS section of docs/src/pages/client/mdm-integration.mdx.
|
||||
-->
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
@@ -99,11 +102,29 @@
|
||||
-->
|
||||
|
||||
<!-- ===== Remote jobs =====
|
||||
Present at any value locks the client toggle, so pushing
|
||||
false pins remote jobs off. -->
|
||||
allowRemoteJobs present at any value locks the client toggle,
|
||||
so pushing false pins remote jobs off. debugBundleUploadURL
|
||||
overrides where a bundle produced by a remote job is sent. -->
|
||||
<!--
|
||||
<key>allowRemoteJobs</key>
|
||||
<false/>
|
||||
|
||||
<key>debugBundleUploadURL</key>
|
||||
<string>https://uploads.example.com</string>
|
||||
-->
|
||||
|
||||
<!-- ===== Applied by the tunnel engine =====
|
||||
No control to lock in the app - the engine applies these to
|
||||
the connection itself. -->
|
||||
<!--
|
||||
<key>blockInbound</key>
|
||||
<false/>
|
||||
|
||||
<key>wireguardPort</key>
|
||||
<integer>51820</integer>
|
||||
|
||||
<key>lazyConnection</key>
|
||||
<true/>
|
||||
-->
|
||||
|
||||
</dict>
|
||||
|
||||
@@ -401,20 +401,45 @@ enforce. Where to put it, by provider:
|
||||
Booleans may be written as `<true/>`/`<false/>` or as `<integer>1</integer>`/`<integer>0</integer>`;
|
||||
both are accepted. Integers and strings behave as on desktop.
|
||||
|
||||
### What each key does on iOS
|
||||
### When a policy takes effect
|
||||
|
||||
Every key is enforced on iOS: the ones that reshape the interface, and
|
||||
the ones the engine applies to the connection itself. Getting there
|
||||
takes one extra step that is worth understanding, because it decides
|
||||
*when* a change lands.
|
||||
|
||||
iOS delivers the configuration to the NetBird app. The tunnel, though,
|
||||
runs in a Network Extension — a separate process the system starts when
|
||||
the tunnel comes up and stops when it goes down, and which Apple's
|
||||
app-configuration channel does not deliver to. The app therefore passes
|
||||
the configuration on to the extension, and does so whenever it is
|
||||
running: on activation, when a settings screen appears, and on a timer
|
||||
roughly every 30 seconds. When the policy has changed, the engine
|
||||
restarts on its own and the user is told their IT policy was applied.
|
||||
|
||||
<Note>
|
||||
**A policy lands while the app is open.** With NetBird in the
|
||||
foreground, a change pushed from your MDM reaches the tunnel within
|
||||
about 30 seconds, or immediately when the user switches back to the
|
||||
app. Nothing needs to be restarted by hand.
|
||||
|
||||
**While the app is closed or backgrounded, iOS suspends it**, and a
|
||||
policy pushed in that window — or withdrawn in it — reaches the tunnel
|
||||
only the next time the app is opened. This matters most with VPN On
|
||||
Demand or the widget, where the tunnel can come up without the app ever
|
||||
appearing: it then runs on the last policy the app saw. Plan rollouts
|
||||
accordingly, and treat "applied" as "applied once the user next opens
|
||||
the app".
|
||||
</Note>
|
||||
|
||||
<Warning>
|
||||
The policy reaches the NetBird **app**, not the tunnel engine. On iOS
|
||||
the engine runs in a Network Extension, a separate process with its own
|
||||
preferences domain, which Apple's app-configuration channel does not
|
||||
deliver to. So the keys below lock and reshape the interface, and the
|
||||
app refuses configuration changes accordingly — but keys whose effect is
|
||||
applied by the engine on the connection itself (`managementURL`,
|
||||
`preSharedKey`, `rosenpassEnabled`, `rosenpassPermissive`,
|
||||
`blockInbound`, `wireguardPort`, `lazyConnection`) are **not yet
|
||||
enforced on the tunnel**. Treat iOS policy as interface lockdown today,
|
||||
not as connection compliance. Mirroring the dictionary into the shared
|
||||
app group so the extension can read it is tracked work.
|
||||
**On tvOS only the interface is enforced.** The hand-off described above
|
||||
relies on a shared app group, which does not work between the tvOS app
|
||||
and its extension, so the tvOS engine runs without the policy. Keys that
|
||||
lock or hide controls behave as described; the ones the engine applies
|
||||
to the connection — `managementURL`, `preSharedKey`, `rosenpassEnabled`,
|
||||
`rosenpassPermissive`, `blockInbound`, `wireguardPort`, `lazyConnection`
|
||||
— do not reach the tunnel there yet.
|
||||
</Warning>
|
||||
|
||||
| Key | Effect in the iOS / tvOS app |
|
||||
@@ -430,11 +455,19 @@ app group so the extension can read it is tracked work.
|
||||
| `disableUpdateSettings` | Read-only mode: nothing is hidden, every configuration control is locked |
|
||||
| `allowRemoteJobs` | Locks the remote debug bundle toggle |
|
||||
|
||||
The remaining keys have no iOS surface and are parsed and ignored:
|
||||
`allowServerSSH`, `disableServerRoutes`, `disableAutostart`,
|
||||
`disableMetricsCollection`, `enableLocalMetrics`, `localMetricsAddress`,
|
||||
`debugBundleUploadURL`, `splitTunnelMode`, `splitTunnelApps`. They are
|
||||
safe to leave in a payload shared with desktop devices.
|
||||
The table above is what the user sees. A second group of keys has no
|
||||
control to lock, because the engine applies them straight to the
|
||||
connection: `blockInbound`, `wireguardPort`, `lazyConnection`, and
|
||||
`debugBundleUploadURL` when a remote job produces a bundle. They are
|
||||
enforced just the same — there is simply nothing on screen to mark.
|
||||
|
||||
The rest do not apply to the platform and are parsed and ignored:
|
||||
`allowServerSSH` (no SSH server on iOS), `disableServerRoutes` (an iOS
|
||||
peer does not route for others), `disableAutostart` (desktop GUI only),
|
||||
`disableMetricsCollection`, `enableLocalMetrics` and
|
||||
`localMetricsAddress` (the local endpoint is not reachable on iOS), and
|
||||
`splitTunnelMode` / `splitTunnelApps` (Android). They are safe to leave
|
||||
in a payload shared with desktop devices.
|
||||
|
||||
### Verifying on a device
|
||||
|
||||
@@ -446,6 +479,13 @@ app-configuration payload is assigned to the same device group, and that
|
||||
the bundle id matches (`io.netbird.app`, or `io.netbird.app.tv` on
|
||||
tvOS).
|
||||
|
||||
That covers delivery to the app. To confirm a change reached the
|
||||
*tunnel*, change a policy while the tunnel is connected and bring
|
||||
NetBird to the foreground: the engine restarts and the device shows
|
||||
"NetBird configuration was updated by your IT policy". That notice comes
|
||||
from the extension, so seeing it means the engine applied the new
|
||||
policy.
|
||||
|
||||
## Verifying enforcement
|
||||
|
||||
On Windows and macOS, the cleanest verification is the daemon's own
|
||||
@@ -509,10 +549,12 @@ The device is not MDM-enrolled. macOS protects
|
||||
controls the directory. Enroll the device with a real MDM provider for
|
||||
persistent rollouts.
|
||||
|
||||
**On iOS, the interface is locked but the connection ignores the policy.**
|
||||
Expected today: the tunnel engine runs in a Network Extension that does
|
||||
not receive the app configuration. See the warning under
|
||||
[iOS](#what-each-key-does-on-i-os).
|
||||
**On iOS, a policy change has not reached the tunnel.**
|
||||
The app passes the configuration to the engine only while it is running.
|
||||
Open NetBird and leave it in the foreground for half a minute; the
|
||||
engine restarts and reports that the policy was applied. On tvOS the
|
||||
engine does not receive the policy at all — see [When a policy takes
|
||||
effect](#when-a-policy-takes-effect).
|
||||
|
||||
**My MDM provider is not in the list above.**
|
||||
Any MDM that can push a Configuration Profile on macOS, write a
|
||||
|
||||
Reference in New Issue
Block a user