Merge branch 'main' into embedded-vnc

This commit is contained in:
Viktor Liu
2026-09-02 19:10:03 +02:00
153 changed files with 9089 additions and 1934 deletions

View File

@@ -91,6 +91,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. -->
<!--

View File

@@ -125,6 +125,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. -->
<!--

View File

@@ -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
allowServerVNC="$NULL"
disableVNCApproval="$NULL"
blockInbound="$NULL"
@@ -109,21 +113,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/&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/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
@@ -147,15 +165,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 "$allowServerVNC" && emit_bool allowServerVNC "$allowServerVNC"
is_set "$disableVNCApproval" && emit_bool disableVNCApproval "$disableVNCApproval"
is_set "$blockInbound" && emit_bool blockInbound "$blockInbound"
@@ -185,7 +223,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.

View File

@@ -43,6 +43,12 @@
<string id="DisableVNCApproval_Name">Disable VNC connection approval</string>
<string id="DisableVNCApproval_Help">When enabled, incoming VNC sessions are accepted without prompting the local user for approval.</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>
@@ -83,6 +89,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>

View File

@@ -124,6 +124,18 @@
<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="AllowServerVNC"
class="Machine"
displayName="$(string.AllowServerVNC_Name)"
@@ -148,6 +160,19 @@
<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)"

View File

@@ -32,7 +32,7 @@ list; both are optional and default to the full privileged suite.
1. Skips immediately when it detects it is already inside the container
(`DOCKER_CI=true`), so the privileged tests run in place instead of recursing.
2. Otherwise spins up a `golang:1.25-alpine` container (matching CI),
2. Otherwise spins up a `golang:1.26.7-alpine` container (matching CI),
bind-mounts the repo and the host Go build/module caches, installs the
required packages, and runs `go test -tags 'devcert privileged'` over the
client packages.