mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-16 11:39:57 +00:00
197 lines
7.4 KiB
PowerShell
197 lines
7.4 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Interactive tester for NetBird's MDM policy on Windows.
|
|
|
|
.DESCRIPTION
|
|
Edits HKLM\Software\Policies\NetBird (the same registry key the OS would
|
|
populate from Group Policy / Intune ADMX ingestion / a Registry CSP profile,
|
|
see client/mdm/policy_windows.go). The MDM ticker re-reads the key within
|
|
<=60s; the daemon restarts the engine itself in-process - no service restart
|
|
needed for a change to take effect (the [k] kick only forces an immediate
|
|
re-read or recovers a wedged daemon).
|
|
|
|
Value-type mapping (mirrors readRegistryValue):
|
|
string -> REG_SZ
|
|
integer -> REG_DWORD
|
|
bool -> REG_DWORD (1=true, 0=false) GetBool treats !=0 as true
|
|
array -> REG_MULTI_SZ
|
|
|
|
Requires elevation (writing HKLM). Re-launches itself elevated if needed.
|
|
|
|
Keys: number to edit a field, [k] kick the daemon, [c] clear the whole
|
|
policy key, [r] refresh, [q] quit.
|
|
#>
|
|
[CmdletBinding()]
|
|
param()
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$PolicyPath = 'HKLM:\Software\Policies\NetBird'
|
|
|
|
# Known keys and their registry types. Order drives the TUI numbering.
|
|
$Fields = @(
|
|
@{ Key = 'managementURL'; Type = 'string' }
|
|
@{ Key = 'preSharedKey'; Type = 'string' }
|
|
@{ Key = 'wireguardPort'; Type = 'integer' }
|
|
@{ Key = 'splitTunnelMode'; Type = 'string' } # allow / disallow
|
|
@{ Key = 'splitTunnelApps'; Type = 'array' }
|
|
@{ Key = 'rosenpassEnabled'; Type = 'bool' }
|
|
@{ Key = 'rosenpassPermissive'; Type = 'bool' }
|
|
@{ Key = 'disableClientRoutes'; Type = 'bool' }
|
|
@{ Key = 'disableServerRoutes'; Type = 'bool' }
|
|
@{ Key = 'allowServerSSH'; Type = 'bool' }
|
|
@{ Key = 'disableAutoConnect'; Type = 'bool' }
|
|
@{ Key = 'blockInbound'; Type = 'bool' }
|
|
@{ Key = 'disableMetricsCollection'; Type = 'bool' }
|
|
@{ Key = 'disableAdvancedView'; Type = 'bool' }
|
|
@{ Key = 'disableProfiles'; Type = 'bool' }
|
|
@{ Key = 'disableNetworks'; Type = 'bool' }
|
|
@{ Key = 'disableUpdateSettings'; Type = 'bool' }
|
|
)
|
|
|
|
# ---- elevation --------------------------------------------------------------
|
|
|
|
function Test-Admin {
|
|
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
(New-Object Security.Principal.WindowsPrincipal $id).IsInRole(
|
|
[Security.Principal.WindowsBuiltinRole]::Administrator)
|
|
}
|
|
|
|
if (-not (Test-Admin)) {
|
|
Write-Host 'Elevation required for HKLM writes - relaunching as admin...' -ForegroundColor Yellow
|
|
Start-Process -FilePath 'powershell.exe' -Verb RunAs -ArgumentList @(
|
|
'-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', "`"$PSCommandPath`"")
|
|
exit 0
|
|
}
|
|
|
|
# ---- ANSI helpers (VT; conhost on Win10+ and Windows Terminal support it) ----
|
|
|
|
$IsTty = -not [Console]::IsOutputRedirected
|
|
$e = [char]27
|
|
function C($code) { if ($IsTty) { "${e}[${code}m" } else { '' } }
|
|
$Reset = C '0'; $Bold = C '1'; $Dim = C '2'
|
|
$Green = C '32'; $Red = C '31'; $Yellow = C '33'; $Cyan = C '36'
|
|
|
|
# ---- service ----------------------------------------------------------------
|
|
|
|
function Restart-Daemon {
|
|
$svc = Get-Service -Name 'Netbird', 'netbird' -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
if (-not $svc) { Write-Host 'Netbird service not found.' -ForegroundColor Red; return }
|
|
Write-Host "restarting service ($($svc.Name))..."
|
|
Restart-Service -Name $svc.Name -Force
|
|
}
|
|
|
|
# ---- registry read/write ----------------------------------------------------
|
|
|
|
function Ensure-PolicyKey {
|
|
if (-not (Test-Path $PolicyPath)) { New-Item -Path $PolicyPath -Force | Out-Null }
|
|
}
|
|
|
|
function Read-Value {
|
|
param([string]$Key)
|
|
if (-not (Test-Path $PolicyPath)) { return $null }
|
|
$item = Get-ItemProperty -Path $PolicyPath -Name $Key -ErrorAction SilentlyContinue
|
|
if ($null -eq $item) { return $null }
|
|
return $item.$Key
|
|
}
|
|
|
|
function Set-Value {
|
|
param([string]$Key, [string]$Type, $Value)
|
|
Ensure-PolicyKey
|
|
switch ($Type) {
|
|
'array' { Set-ItemProperty -Path $PolicyPath -Name $Key -Value ([string[]]$Value) -Type MultiString }
|
|
'integer' { Set-ItemProperty -Path $PolicyPath -Name $Key -Value ([int]$Value) -Type DWord }
|
|
'bool' { Set-ItemProperty -Path $PolicyPath -Name $Key -Value ([int]$Value) -Type DWord }
|
|
default { Set-ItemProperty -Path $PolicyPath -Name $Key -Value ([string]$Value) -Type String }
|
|
}
|
|
}
|
|
|
|
function Clear-Value {
|
|
param([string]$Key)
|
|
if (Test-Path $PolicyPath) {
|
|
Remove-ItemProperty -Path $PolicyPath -Name $Key -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
function Clear-PolicyKey {
|
|
if (Test-Path $PolicyPath) {
|
|
Remove-Item -Path $PolicyPath -Recurse -Force
|
|
Write-Host "removed $PolicyPath"
|
|
} else {
|
|
Write-Host '(already absent)'
|
|
}
|
|
}
|
|
|
|
# ---- TUI --------------------------------------------------------------------
|
|
|
|
function Render-Value {
|
|
param($Raw, [string]$Type)
|
|
if ($null -eq $Raw -or ($Raw -is [string] -and $Raw -eq '')) {
|
|
return "$Dim-$Reset"
|
|
}
|
|
switch ($Type) {
|
|
'bool' {
|
|
if ([int]$Raw -ne 0) { "${Green}[+] true$Reset" } else { "${Red}[-] false$Reset" }
|
|
}
|
|
'array' { "${Cyan}[$($Raw -join ' ')]$Reset" }
|
|
default { "$Yellow$Raw$Reset" }
|
|
}
|
|
}
|
|
|
|
function Render-Screen {
|
|
Clear-Host
|
|
Write-Host "$Bold==== NetBird MDM Tester (Windows) ====$Reset"
|
|
Write-Host "${Dim}key:$Reset $PolicyPath`n"
|
|
for ($i = 0; $i -lt $Fields.Count; $i++) {
|
|
$f = $Fields[$i]
|
|
$raw = Read-Value $f.Key
|
|
$num = ('[{0,2}]' -f ($i + 1))
|
|
$val = Render-Value $raw $f.Type
|
|
Write-Host (" $Bold$num$Reset {0,-30} {1} $Dim({2})$Reset" -f $f.Key, $val, $f.Type)
|
|
}
|
|
Write-Host ''
|
|
Write-Host " ${Bold}[k]$Reset kick daemon ${Bold}[c]$Reset clear key ${Bold}[r]$Reset refresh ${Bold}[q]$Reset quit"
|
|
Write-Host ''
|
|
}
|
|
|
|
function Edit-Field {
|
|
param([int]$Index)
|
|
if ($Index -lt 1 -or $Index -gt $Fields.Count) { Write-Host ' (out of range)'; Start-Sleep -Milliseconds 600; return }
|
|
$f = $Fields[$Index - 1]
|
|
$cur = Read-Value $f.Key
|
|
switch ($f.Type) {
|
|
'bool' {
|
|
# Cycle: unset -> true -> false -> unset
|
|
if ($null -eq $cur) { Set-Value $f.Key 'bool' 1 }
|
|
elseif ([int]$cur -ne 0) { Set-Value $f.Key 'bool' 0 }
|
|
else { Clear-Value $f.Key }
|
|
}
|
|
'array' {
|
|
Write-Host " current: $($cur -join ' ')"
|
|
$line = Read-Host ' new array (space-separated, empty to unset)'
|
|
if ([string]::IsNullOrWhiteSpace($line)) { Clear-Value $f.Key }
|
|
else { Set-Value $f.Key 'array' ($line -split '\s+' | Where-Object { $_ }) }
|
|
}
|
|
default {
|
|
Write-Host " current: $cur"
|
|
$line = Read-Host " new $($f.Type) (empty to unset)"
|
|
if ([string]::IsNullOrWhiteSpace($line)) { Clear-Value $f.Key }
|
|
else { Set-Value $f.Key $f.Type $line }
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-Host 'starting MDM tester (q to quit)'
|
|
while ($true) {
|
|
Render-Screen
|
|
$choice = Read-Host '>'
|
|
switch -Regex ($choice) {
|
|
'^(q|quit|exit)$' { Write-Host 'bye'; return }
|
|
'^(k|kick)$' { Restart-Daemon; Start-Sleep -Milliseconds 600 }
|
|
'^(c|clear)$' { Clear-PolicyKey; Start-Sleep -Milliseconds 600 }
|
|
'^(r|)$' { } # redraw
|
|
'^\d+$' { Edit-Field ([int]$choice) }
|
|
default { Write-Host ' (unknown - number to edit, k/c/r/q)'; Start-Sleep -Milliseconds 600 }
|
|
}
|
|
}
|