mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-02 04:51:29 +02:00
[management,client] Gate remote jobs behind an admin opt-in with MDM support (#7153)
This introduces a disabled-by-default allow-remote-jobs setting that controls whether the management server may run jobs (such as debug bundles) on a peer. The flag propagates end to end: through client configuration, the daemon SetConfig and Login requests, authentication, and system info, up to management, where it is stored on the peer and exposed on the peers API as remote_jobs_allowed. The client refuses any management-requested job unless the peer has opted in. Because enabling remote jobs crosses the user-to-root boundary, turning it on requires privilege, mirroring the SSH-server gate. Administrators can enforce the setting through MDM policy on both macOS and Windows, and MDM can also override the debug-bundle upload URL. The change ships policy documentation and generated profile templates, and adds configuration, conflict, and enforcement tests covering the opt-in, privilege, and MDM paths.
This commit is contained in:
@@ -85,6 +85,21 @@
|
||||
<false/>
|
||||
-->
|
||||
|
||||
<!-- ===== Remote jobs (debug bundles) =====
|
||||
allowRemoteJobs : opt this device into management-requested
|
||||
remote jobs (e.g. debug bundles). Off by
|
||||
default; enabling is a privileged change.
|
||||
debugBundleUploadURL : override the debug-bundle upload service URL
|
||||
for remote jobs (https URL with a host). Takes
|
||||
precedence over the management-supplied value. -->
|
||||
<!--
|
||||
<key>allowRemoteJobs</key>
|
||||
<true/>
|
||||
|
||||
<key>debugBundleUploadURL</key>
|
||||
<string>https://upload.example.com</string>
|
||||
-->
|
||||
|
||||
<!-- ===== WireGuard UDP port =====
|
||||
Range 1-65535. Omit to keep the daemon default. -->
|
||||
<!--
|
||||
|
||||
@@ -121,6 +121,19 @@
|
||||
<false/>
|
||||
-->
|
||||
|
||||
<!-- ===== Remote jobs (debug bundles) =====
|
||||
allowRemoteJobs : opt into management-requested
|
||||
remote jobs. Off by default.
|
||||
debugBundleUploadURL : override the debug-bundle upload
|
||||
service (https URL with a host);
|
||||
precedence over the management value. -->
|
||||
<!--
|
||||
<key>allowRemoteJobs</key>
|
||||
<true/>
|
||||
<key>debugBundleUploadURL</key>
|
||||
<string>https://upload.example.com</string>
|
||||
-->
|
||||
|
||||
<!-- ===== WireGuard UDP port (int) =====
|
||||
Range 1-65535. Omit to keep the default. -->
|
||||
<!--
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
# 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.
|
||||
# SECURITY: PreSharedKey (and any secret-bearing debugBundleUploadURL) is
|
||||
# redacted in this script's log output, and the installed plist is 0600
|
||||
# root:wheel so its values are not readable by local non-root users.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -56,6 +58,8 @@ NULL='__UNSET__'
|
||||
managementURL='https://api.netbird.io:443'
|
||||
preSharedKey="$NULL" # secret; redacted in log
|
||||
allowServerSSH='true'
|
||||
allowRemoteJobs="$NULL"
|
||||
debugBundleUploadURL="$NULL" # HTTPS URL with a host; overrides management
|
||||
blockInbound="$NULL"
|
||||
disableAutoConnect="$NULL"
|
||||
disableAutostart="$NULL"
|
||||
@@ -107,21 +111,35 @@ end_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 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)` for secret keys — `preSharedKey` and `debugBundleUploadURL`, which can embed credentials or a signed query token).
|
||||
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
|
||||
case "$key" in
|
||||
preSharedKey|debugBundleUploadURL) log_value='********** (secret)' ;;
|
||||
*) ;;
|
||||
esac
|
||||
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.
|
||||
# is_bool returns success if the value is an accepted boolean token.
|
||||
is_bool() {
|
||||
local value="$1"
|
||||
case "$value" in
|
||||
true|True|TRUE|1|yes|false|False|FALSE|0|no) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 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.
|
||||
# It returns success even on invalid input (like emit_int) so a single typo in one
|
||||
# boolean does not abort the whole policy push under `set -euo pipefail`. Callers
|
||||
# that must fail closed on an invalid value (e.g. allowRemoteJobs) validate with
|
||||
# is_bool before calling and substitute a safe default themselves.
|
||||
emit_bool() {
|
||||
local key="$1" value="$2"
|
||||
local xml_bool
|
||||
@@ -145,15 +163,35 @@ emit_int() {
|
||||
log "set $key = $value"
|
||||
}
|
||||
|
||||
# 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 builds the NetBird MDM plist from configured policy variables, validates and installs it to /Library/Managed Preferences/io.netbird.client.plist (root:wheel, 600 — the daemon reads it directly as root, so it need not be world-readable) and optionally triggers the NetBird daemon to reload.
|
||||
main() {
|
||||
log "applying NetBird MDM policy to $PLIST_PATH"
|
||||
# Restrict the temp plist while it is being built: it carries the same
|
||||
# secret-bearing values as the final file, which is installed 0600 below.
|
||||
umask 077
|
||||
/bin/mkdir -p "$PLIST_DIR"
|
||||
start_plist
|
||||
# Force 0600 on the temp file explicitly: start_plist writes it with a
|
||||
# truncating redirect, which keeps an existing file's mode, so a leftover
|
||||
# 0644 tmp from an interrupted run would not be tightened by umask alone.
|
||||
# start_plist only wrote the header so far — the secret-bearing values are
|
||||
# appended after this point.
|
||||
/bin/chmod 600 "$PLIST_PATH.tmp"
|
||||
|
||||
is_set "$managementURL" && emit_string managementURL "$managementURL"
|
||||
is_set "$preSharedKey" && emit_string preSharedKey "$preSharedKey"
|
||||
is_set "$allowServerSSH" && emit_bool allowServerSSH "$allowServerSSH"
|
||||
# Fail closed: an invalid allowRemoteJobs value must not drop the key and
|
||||
# leave a conflicting local opt-in active — enforce the safe default (false).
|
||||
if is_set "$allowRemoteJobs"; then
|
||||
if is_bool "$allowRemoteJobs"; then
|
||||
emit_bool allowRemoteJobs "$allowRemoteJobs"
|
||||
else
|
||||
log "invalid boolean for allowRemoteJobs: $allowRemoteJobs; enforcing safe default (false)"
|
||||
emit_bool allowRemoteJobs false
|
||||
fi
|
||||
fi
|
||||
is_set "$debugBundleUploadURL" && emit_string debugBundleUploadURL "$debugBundleUploadURL"
|
||||
is_set "$blockInbound" && emit_bool blockInbound "$blockInbound"
|
||||
is_set "$disableAutoConnect" && emit_bool disableAutoConnect "$disableAutoConnect"
|
||||
is_set "$disableAutostart" && emit_bool disableAutostart "$disableAutostart"
|
||||
@@ -181,7 +219,12 @@ main() {
|
||||
|
||||
/bin/mv -f "$PLIST_PATH.tmp" "$PLIST_PATH"
|
||||
/usr/sbin/chown root:wheel "$PLIST_PATH"
|
||||
/bin/chmod 644 "$PLIST_PATH"
|
||||
# 0600, not 0644: the daemon's loader (client/mdm/policy_darwin.go) opens the
|
||||
# plist directly as root, so it does not need to be world-readable. Restricting
|
||||
# it keeps secret-bearing values (preSharedKey, a signed debugBundleUploadURL)
|
||||
# from any local non-root user. The loader's only mode check refuses a
|
||||
# world-writable file, which 0600 satisfies.
|
||||
/bin/chmod 600 "$PLIST_PATH"
|
||||
|
||||
log "policy installed; NetBird daemon will pick it up within the next 1-minute reload tick"
|
||||
|
||||
|
||||
Binary file not shown.
@@ -39,6 +39,12 @@
|
||||
<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="AllowRemoteJobs_Name">Allow remote jobs</string>
|
||||
<string id="AllowRemoteJobs_Help">When enabled, this client accepts management-requested remote jobs (e.g. debug bundles). Off by default. Equivalent to --allow-remote-jobs.</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 with a host.</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>
|
||||
|
||||
@@ -79,6 +85,12 @@
|
||||
</textBox>
|
||||
</presentation>
|
||||
|
||||
<presentation id="DebugBundleUploadURL_Pres">
|
||||
<textBox refId="DebugBundleUploadURL_Text">
|
||||
<label>Debug bundle upload URL:</label>
|
||||
</textBox>
|
||||
</presentation>
|
||||
|
||||
<presentation id="PreSharedKey_Pres">
|
||||
<textBox refId="PreSharedKey_Text">
|
||||
<label>Pre-shared key:</label>
|
||||
|
||||
@@ -124,6 +124,31 @@
|
||||
<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="false" />
|
||||
</elements>
|
||||
</policy>
|
||||
|
||||
<policy name="RosenpassEnabled"
|
||||
class="Machine"
|
||||
displayName="$(string.RosenpassEnabled_Name)"
|
||||
|
||||
Reference in New Issue
Block a user