Keep every server pin in the upgrade script, and stop promising enrollment to an enrolled PC
Some checks failed
CI / backend (push) Has been cancelled
CI / naming (push) Has been cancelled
CI / gates (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / lean-build (push) Has been cancelled

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.
This commit is contained in:
cproudlock
2026-09-15 15:52:54 -04:00
parent f4a03387b2
commit 0a49714d05
4 changed files with 75 additions and 6 deletions

View File

@@ -205,6 +205,26 @@ function Get-ShopDbServerPins {
return ,$pins 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) { function Enable-ShopDbServerPins([string[]]$Pins) {
# True when pinning is active. False means pins are set but could not be # True when pinning is active. False means pins are set but could not be
# enforced; the caller must not send anything then. # enforced; the caller must not send anything then.

View File

@@ -331,11 +331,15 @@ def _install_script(baseurl, collectorkey, cadenceseconds, certfilename=None,
keyparam = '' keyparam = ''
keycheck = '' keycheck = ''
keywrite = ( 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' sharedkeymode = 'omitted'
enrollnotice = ( enrollnotice = (
'if (-not (Test-Path (Join-Path $env:ProgramData "ShopDB\\EndpointAgent\\credential\\credential.bin"))) {\n'
' Write-Host " This PC will request enrollment. Approve it in ' ' Write-Host " This PC will request enrollment. Approve it in '
'Settings > Endpoints > Enrollment." -ForegroundColor Yellow\n') 'Settings > Endpoints > Enrollment." -ForegroundColor Yellow\n'
'}\n')
return """# install.ps1 return """# install.ps1
# ShopDB endpoint agent installer. Run from an ELEVATED (admin) PowerShell on # ShopDB endpoint agent installer. Run from an ELEVATED (admin) PowerShell on
# the endpoint. Provisions the registry credentials, copies the agent to # the endpoint. Provisions the registry credentials, copies the agent to

View File

@@ -198,6 +198,26 @@ function Get-ShopDbServerPins {
return ,$pins 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) { function Enable-ShopDbServerPins([string[]]$Pins) {
# True when pinning is active. False means pins are set but could not be # True when pinning is active. False means pins are set but could not be
# enforced; the caller must not send anything then. # enforced; the caller must not send anything then.
@@ -410,10 +430,15 @@ try {
# protocols the server will refuse. # protocols the server will refuse.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$serverPins = @(Get-ShopDbServerPins) # 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) { foreach ($pin in $config.ServerPins) {
if ($serverPins -notcontains $pin) { $serverPins += $pin } if ($serverPins -notcontains $pin) { $serverPins += $pin }
} }
}
if ($serverPins.Count -gt 0) { if ($serverPins.Count -gt 0) {
if (-not (Enable-ShopDbServerPins $serverPins)) { if (-not (Enable-ShopDbServerPins $serverPins)) {
throw 'ServerPins is set but the pin check could not be enabled; not downloading.' throw 'ServerPins is set but the pin check could not be enabled; not downloading.'

View File

@@ -213,6 +213,26 @@ function Get-ShopDbServerPins {
return ,$pins 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) { function Enable-ShopDbServerPins([string[]]$Pins) {
# True when pinning is active. False means pins are set but could not be # True when pinning is active. False means pins are set but could not be
# enforced; the caller must not send anything then. # enforced; the caller must not send anything then.