From 0a49714d05e70123f28adeb5a08c37d591fa41bb Mon Sep 17 00:00:00 2001 From: cproudlock Date: Tue, 15 Sep 2026 15:52:54 -0400 Subject: [PATCH] Keep every server pin in the upgrade script, and stop promising enrollment to an enrolled PC Found in the Windows VM check. - The agent upgrade script wrapped Get-ShopDbServerPins in @(), which turned several registry pins into one joined string. During a certificate rotation the upgrade download would have refused the rotated server. It also merged appsettings pins with the registry; it now follows the agent's rule: when the ServerPins value exists, even empty, the registry owns the list. The shared pin block in the upgrade script and both reporters gains Test-ShopDbServerPinsValue. - A key-less package installed over an enrolled PC printed both "existing enrolled credential found; kept as is" and "This PC will request enrollment". The enrollment notices now print only when no credential exists. --- .../computers/client/Report-AssetToShopDB.ps1 | 20 ++++++++++++ plugins/endpoints/api/siteagent.py | 10 ++++-- .../api/templates/upgrade-shopdbagent.ps1 | 31 +++++++++++++++++-- .../client/Report-PrintersToShopDB.ps1 | 20 ++++++++++++ 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/plugins/computers/client/Report-AssetToShopDB.ps1 b/plugins/computers/client/Report-AssetToShopDB.ps1 index aa0c86d8..389e0f3a 100644 --- a/plugins/computers/client/Report-AssetToShopDB.ps1 +++ b/plugins/computers/client/Report-AssetToShopDB.ps1 @@ -205,6 +205,26 @@ function Get-ShopDbServerPins { return ,$pins } +function Test-ShopDbServerPinsValue { + # True when ServerPins exists in either registry view, even empty. The + # registry then owns the pin list and appsettings pins are ignored, the + # same rule the agent follows. + foreach ($view in @([Microsoft.Win32.RegistryView]::Registry64, [Microsoft.Win32.RegistryView]::Registry32)) { + try { + $base = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $view) + try { + $key = $base.OpenSubKey('SOFTWARE\GE\ShopDB') + if ($key) { + try { + if ($key.GetValueNames() -contains 'ServerPins') { return $true } + } finally { $key.Dispose() } + } + } finally { $base.Dispose() } + } catch {} + } + return $false +} + function Enable-ShopDbServerPins([string[]]$Pins) { # True when pinning is active. False means pins are set but could not be # enforced; the caller must not send anything then. diff --git a/plugins/endpoints/api/siteagent.py b/plugins/endpoints/api/siteagent.py index 6e1d1185..a498f9eb 100644 --- a/plugins/endpoints/api/siteagent.py +++ b/plugins/endpoints/api/siteagent.py @@ -331,11 +331,15 @@ def _install_script(baseurl, collectorkey, cadenceseconds, certfilename=None, keyparam = '' keycheck = '' keywrite = ( - 'Write-Host " no shared key in this package; the agent will request enrollment." -ForegroundColor Yellow\n') + 'if (-not (Test-Path (Join-Path $env:ProgramData "ShopDB\\EndpointAgent\\credential\\credential.bin"))) {\n' + ' Write-Host " no shared key in this package; the agent will request enrollment." -ForegroundColor Yellow\n' + '}\n') sharedkeymode = 'omitted' enrollnotice = ( - 'Write-Host " This PC will request enrollment. Approve it in ' - 'Settings > Endpoints > Enrollment." -ForegroundColor Yellow\n') + 'if (-not (Test-Path (Join-Path $env:ProgramData "ShopDB\\EndpointAgent\\credential\\credential.bin"))) {\n' + ' Write-Host " This PC will request enrollment. Approve it in ' + 'Settings > Endpoints > Enrollment." -ForegroundColor Yellow\n' + '}\n') return """# install.ps1 # ShopDB endpoint agent installer. Run from an ELEVATED (admin) PowerShell on # the endpoint. Provisions the registry credentials, copies the agent to diff --git a/plugins/endpoints/api/templates/upgrade-shopdbagent.ps1 b/plugins/endpoints/api/templates/upgrade-shopdbagent.ps1 index b7ca75fc..767f435b 100644 --- a/plugins/endpoints/api/templates/upgrade-shopdbagent.ps1 +++ b/plugins/endpoints/api/templates/upgrade-shopdbagent.ps1 @@ -198,6 +198,26 @@ function Get-ShopDbServerPins { return ,$pins } +function Test-ShopDbServerPinsValue { + # True when ServerPins exists in either registry view, even empty. The + # registry then owns the pin list and appsettings pins are ignored, the + # same rule the agent follows. + foreach ($view in @([Microsoft.Win32.RegistryView]::Registry64, [Microsoft.Win32.RegistryView]::Registry32)) { + try { + $base = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $view) + try { + $key = $base.OpenSubKey('SOFTWARE\GE\ShopDB') + if ($key) { + try { + if ($key.GetValueNames() -contains 'ServerPins') { return $true } + } finally { $key.Dispose() } + } + } finally { $base.Dispose() } + } catch {} + } + return $false +} + function Enable-ShopDbServerPins([string[]]$Pins) { # True when pinning is active. False means pins are set but could not be # enforced; the caller must not send anything then. @@ -410,9 +430,14 @@ try { # protocols the server will refuse. [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - $serverPins = @(Get-ShopDbServerPins) - foreach ($pin in $config.ServerPins) { - if ($serverPins -notcontains $pin) { $serverPins += $pin } + # Get-ShopDbServerPins already returns an array; wrapping it in @() made + # several pins one joined string. Registry wins when the value exists, + # even empty; appsettings pins apply only when it is absent. + $serverPins = Get-ShopDbServerPins + if (-not (Test-ShopDbServerPinsValue)) { + foreach ($pin in $config.ServerPins) { + if ($serverPins -notcontains $pin) { $serverPins += $pin } + } } if ($serverPins.Count -gt 0) { if (-not (Enable-ShopDbServerPins $serverPins)) { diff --git a/plugins/printers/client/Report-PrintersToShopDB.ps1 b/plugins/printers/client/Report-PrintersToShopDB.ps1 index 5b83f2b9..b4ab24dc 100644 --- a/plugins/printers/client/Report-PrintersToShopDB.ps1 +++ b/plugins/printers/client/Report-PrintersToShopDB.ps1 @@ -213,6 +213,26 @@ function Get-ShopDbServerPins { return ,$pins } +function Test-ShopDbServerPinsValue { + # True when ServerPins exists in either registry view, even empty. The + # registry then owns the pin list and appsettings pins are ignored, the + # same rule the agent follows. + foreach ($view in @([Microsoft.Win32.RegistryView]::Registry64, [Microsoft.Win32.RegistryView]::Registry32)) { + try { + $base = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $view) + try { + $key = $base.OpenSubKey('SOFTWARE\GE\ShopDB') + if ($key) { + try { + if ($key.GetValueNames() -contains 'ServerPins') { return $true } + } finally { $key.Dispose() } + } + } finally { $base.Dispose() } + } catch {} + } + return $false +} + function Enable-ShopDbServerPins([string[]]$Pins) { # True when pinning is active. False means pins are set but could not be # enforced; the caller must not send anything then.