From 44156b62560a9e379c2f5db25c0567b2c9fc6ed7 Mon Sep 17 00:00:00 2001 From: Eduard Gert Date: Tue, 16 Jun 2026 11:33:10 +0200 Subject: [PATCH] mdm test script (can be deleted later) --- client/ui/scripts/mdm-fields.md | 20 +++ client/ui/scripts/mdm-toggle.sh | 253 ++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 client/ui/scripts/mdm-fields.md create mode 100755 client/ui/scripts/mdm-toggle.sh diff --git a/client/ui/scripts/mdm-fields.md b/client/ui/scripts/mdm-fields.md new file mode 100644 index 000000000..e166ba778 --- /dev/null +++ b/client/ui/scripts/mdm-fields.md @@ -0,0 +1,20 @@ +# MDM dev cheatsheet (macOS) + +Edits `/Library/Managed Preferences/io.netbird.client.plist`. Daemon picks up +changes within ≤60s; no restart needed. + +Run the interactive TUI: + +```bash +./mdm-toggle.sh tui +``` + +Keys: number to edit a field, **k** to kick the daemon, **c** to clear the +plist, **l** to view recent MDM log lines, **r** to refresh, **q** to quit. + +## Known keys + +- Strings/ints: `managementURL`, `preSharedKey`, `wireguardPort`, `splitTunnelMode` (`allow`/`disallow`) +- Array: `splitTunnelApps` +- Bools: `rosenpassEnabled`, `rosenpassPermissive`, `disableClientRoutes`, `disableServerRoutes`, `allowServerSSH`, `disableAutoConnect`, `blockInbound`, `disableMetricsCollection` +- Feature gates: `disableAdvancedView`, `disableProfiles`, `disableNetworks`, `disableUpdateSettings` (true = hide; false/unset = allow) diff --git a/client/ui/scripts/mdm-toggle.sh b/client/ui/scripts/mdm-toggle.sh new file mode 100755 index 000000000..114b7b185 --- /dev/null +++ b/client/ui/scripts/mdm-toggle.sh @@ -0,0 +1,253 @@ +#!/usr/bin/env bash +# +# Dev helper for testing NetBird's MDM policy on macOS. +# +# Edits /Library/Managed Preferences/io.netbird.client.plist (the same path the +# OS would populate from a real Configuration Profile). The MDM ticker observes +# the change within ≤60s; the daemon restarts the engine itself in-process. +# +# Usage: +# ./mdm-toggle.sh tui # interactive terminal UI +# ./mdm-toggle.sh show # print current plist +# ./mdm-toggle.sh clear # wipe (delete) the plist +# ./mdm-toggle.sh restart # kick the daemon +# ./mdm-toggle.sh logs [-f] # tail daemon log filtered for MDM lines +# ./mdm-toggle.sh verify # plist + recent MDM log lines +# +# Known keys (see client/mdm/policy.go): +# managementURL preSharedKey wireguardPort rosenpassEnabled rosenpassPermissive +# disableClientRoutes disableServerRoutes allowServerSSH disableAutoConnect +# blockInbound disableMetricsCollection splitTunnelMode splitTunnelApps +# disableProfiles disableNetworks disableUpdateSettings disableAdvancedView + +set -euo pipefail + +PLIST="/Library/Managed Preferences/io.netbird.client.plist" +LAUNCHD_LABEL="system/netbird" +DAEMON_LOG="/var/log/netbird/client.log" +LOG_GREP='MDM|mdm|policy|engine restart|config_changed|managed|RegisterUILog' + +die() { echo "error: $*" >&2; exit 1; } + +require_sudo() { + if [[ $EUID -ne 0 ]]; then + exec sudo --preserve-env=HOME "$0" "$@" + fi +} + +ensure_plist() { + if [[ ! -f "$PLIST" ]]; then + mkdir -p "$(dirname "$PLIST")" + /usr/libexec/PlistBuddy -c "Save" "$PLIST" >/dev/null 2>&1 || \ + /bin/cat >"$PLIST" <<'EOF' + + + +EOF + chown root:wheel "$PLIST" + chmod 0644 "$PLIST" + fi +} + +restart_daemon() { + echo "kicking daemon ($LAUNCHD_LABEL)..." + launchctl kickstart -k "$LAUNCHD_LABEL" +} + +cmd_show() { + if [[ ! -f "$PLIST" ]]; then + echo "(no plist at $PLIST — MDM not active)" + return + fi + echo "# $PLIST" + /usr/libexec/PlistBuddy -c "Print" "$PLIST" +} + +cmd_clear() { + if [[ -f "$PLIST" ]]; then + rm -f "$PLIST" + echo "removed $PLIST" + else + echo "(already absent)" + fi +} + +cmd_logs() { + local follow=0 + [[ "${1:-}" == "-f" || "${1:-}" == "--follow" ]] && follow=1 + [[ -f "$DAEMON_LOG" ]] || die "daemon log not found at $DAEMON_LOG" + if [[ $follow -eq 1 ]]; then + echo "# tailing $DAEMON_LOG (Ctrl-C to stop) — filter: $LOG_GREP" + tail -F "$DAEMON_LOG" | grep -E --line-buffered --color=always "$LOG_GREP" + else + echo "# last MDM-relevant lines from $DAEMON_LOG" + grep -E --color=always "$LOG_GREP" "$DAEMON_LOG" | tail -30 + fi +} + +cmd_verify() { + echo "=== plist ===" + if [[ -f "$PLIST" ]]; then + /usr/libexec/PlistBuddy -c "Print" "$PLIST" + else + echo "(no plist at $PLIST — MDM not active)" + fi + echo + echo "=== daemon log (last 20 MDM-relevant lines) ===" + if [[ -f "$DAEMON_LOG" ]]; then + grep -E --color=always "$LOG_GREP" "$DAEMON_LOG" | tail -20 \ + || echo "(no MDM-related entries yet — wait up to 60s for next ticker fire)" + else + echo "(daemon log not found at $DAEMON_LOG)" + fi +} + +cmd_tui() { + local KEYS=( + managementURL preSharedKey wireguardPort splitTunnelMode splitTunnelApps + rosenpassEnabled rosenpassPermissive disableClientRoutes disableServerRoutes + allowServerSSH disableAutoConnect blockInbound disableMetricsCollection + disableAdvancedView disableProfiles disableNetworks disableUpdateSettings + ) + local TYPES=( + string string integer string array + bool bool bool bool + bool bool bool bool + bool bool bool bool + ) + + # ANSI helpers; bail to plain text when stdout isn't a tty. + local C_RESET="" C_BOLD="" C_DIM="" C_GREEN="" C_RED="" C_YELLOW="" C_CYAN="" + if [[ -t 1 ]]; then + C_RESET=$'\033[0m'; C_BOLD=$'\033[1m'; C_DIM=$'\033[2m' + C_GREEN=$'\033[32m'; C_RED=$'\033[31m'; C_YELLOW=$'\033[33m'; C_CYAN=$'\033[36m' + fi + + read_value() { + local key="$1" + [[ -f "$PLIST" ]] || { echo ""; return; } + /usr/libexec/PlistBuddy -c "Print :$key" "$PLIST" 2>/dev/null || true + } + + render_value() { + local raw="$1" type="$2" + if [[ -z "$raw" ]]; then + printf '%s—%s' "$C_DIM" "$C_RESET" + return + fi + case "$type" in + bool) + if [[ "$raw" == "true" ]]; then + printf '%s✔ true%s' "$C_GREEN" "$C_RESET" + else + printf '%s✘ false%s' "$C_RED" "$C_RESET" + fi ;; + array) + printf '%s[%s]%s' "$C_CYAN" "$(echo "$raw" | tr '\n' ' ' | tr -s ' ')" "$C_RESET" ;; + *) + printf '%s%s%s' "$C_YELLOW" "$raw" "$C_RESET" ;; + esac + } + + render_screen() { + clear + printf '%s════ NetBird MDM Tester ════%s\n' "$C_BOLD" "$C_RESET" + printf '%splist:%s %s %sdaemon log:%s %s\n\n' \ + "$C_DIM" "$C_RESET" "$PLIST" "$C_DIM" "$C_RESET" "$DAEMON_LOG" + local i + for i in "${!KEYS[@]}"; do + local key="${KEYS[$i]}" type="${TYPES[$i]}" raw + raw="$(read_value "$key")" + printf ' %s[%2d]%s %-30s %s%s(%s)%s\n' \ + "$C_BOLD" $((i+1)) "$C_RESET" "$key" \ + "$(render_value "$raw" "$type")" \ + "$C_DIM" "$type" "$C_RESET" + done + printf '\n' + printf ' %s[k]%s kick daemon %s[c]%s clear plist %s[l]%s show recent log %s[r]%s refresh %s[q]%s quit\n' \ + "$C_BOLD" "$C_RESET" "$C_BOLD" "$C_RESET" "$C_BOLD" "$C_RESET" "$C_BOLD" "$C_RESET" "$C_BOLD" "$C_RESET" + printf '\n' + } + + edit_field() { + local idx="$1" + if ! [[ "$idx" =~ ^[0-9]+$ ]] || (( idx < 1 || idx > ${#KEYS[@]} )); then + echo " (out of range)"; sleep 0.6; return + fi + local key="${KEYS[$((idx-1))]}" type="${TYPES[$((idx-1))]}" cur + cur="$(read_value "$key")" + ensure_plist + case "$type" in + bool) + # Cycle: unset → true → false → unset + case "$cur" in + "") /usr/libexec/PlistBuddy -c "Add :$key bool true" "$PLIST" ;; + "true") /usr/libexec/PlistBuddy -c "Delete :$key" "$PLIST" + /usr/libexec/PlistBuddy -c "Add :$key bool false" "$PLIST" ;; + "false") /usr/libexec/PlistBuddy -c "Delete :$key" "$PLIST" ;; + esac ;; + array) + echo " current: $cur" + read -r -p " new array (space-separated, empty to unset): " line + /usr/libexec/PlistBuddy -c "Delete :$key" "$PLIST" 2>/dev/null || true + if [[ -n "$line" ]]; then + /usr/libexec/PlistBuddy -c "Add :$key array" "$PLIST" + local i=0 v + for v in $line; do + /usr/libexec/PlistBuddy -c "Add :$key:$i string $v" "$PLIST" + i=$((i+1)) + done + fi ;; + *) + echo " current: $cur" + read -r -p " new $type (empty to unset): " line + /usr/libexec/PlistBuddy -c "Delete :$key" "$PLIST" 2>/dev/null || true + if [[ -n "$line" ]]; then + /usr/libexec/PlistBuddy -c "Add :$key $type $line" "$PLIST" + fi ;; + esac + } + + show_recent_log() { + clear + printf '%s── recent MDM-related daemon log lines ──%s\n' "$C_BOLD" "$C_RESET" + if [[ -f "$DAEMON_LOG" ]]; then + grep -E --color=always "$LOG_GREP" "$DAEMON_LOG" | tail -25 \ + || echo "(no MDM-related entries yet)" + else + echo "(daemon log not found at $DAEMON_LOG)" + fi + echo + read -r -p "press enter to return " _ + } + + echo "starting MDM tester (Ctrl-C or q to quit)" + while true; do + render_screen + read -r -p "> " choice || break + case "$choice" in + q|quit|exit) break ;; + k|kick) restart_daemon; sleep 0.6 ;; + c|clear) cmd_clear; sleep 0.6 ;; + l|logs) show_recent_log ;; + r|"") : ;; # redraw + *) edit_field "$choice" ;; + esac + done + echo "bye" +} + +sub="${1:-}"; shift || true + +case "$sub" in + show) cmd_show "$@"; exit 0 ;; + logs) require_sudo "$sub" "$@"; cmd_logs "$@"; exit 0 ;; + verify) require_sudo "$sub" "$@"; cmd_verify "$@"; exit 0 ;; + tui) require_sudo "$sub" "$@"; cmd_tui "$@"; exit 0 ;; + restart) require_sudo "$sub" "$@"; restart_daemon; exit 0 ;; + clear) require_sudo "$sub" "$@"; cmd_clear "$@"; exit 0 ;; + ""|-h|--help|help) + sed -n '2,18p' "$0" + ;; + *) die "unknown subcommand: $sub (try --help)" ;; +esac