From 2d675720b78db266019082cd5975909041aaab9b Mon Sep 17 00:00:00 2001 From: cproudlock Date: Mon, 27 Jul 2026 14:17:51 -0400 Subject: [PATCH] geenforce client: make ApiToken optional for IP-allowlisted kiosks Get-ShopdbConfig required both BaseUrl AND ApiToken, so a token-less kiosk (authorized by the server's IP allowlist) got a null config and never ran. Now BaseUrl alone is a valid config; X-API-Key is sent only when a token is present (New-ShopdbAuthHeaders), so token-authorized sites are unchanged and vaulted-network sites need no per-PC token. --- .../geenforce/client/Invoke-ShopdbEnforce.ps1 | 2 +- .../geenforce/client/ShopdbEnforceClient.psm1 | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/plugins/geenforce/client/Invoke-ShopdbEnforce.ps1 b/plugins/geenforce/client/Invoke-ShopdbEnforce.ps1 index 66da901..53f2d05 100644 --- a/plugins/geenforce/client/Invoke-ShopdbEnforce.ps1 +++ b/plugins/geenforce/client/Invoke-ShopdbEnforce.ps1 @@ -85,7 +85,7 @@ try { $config = Get-ShopdbConfig -BaseUrl $BaseUrl -ApiToken $ApiToken if (-not $config) { - Write-Log 'No shopdb BaseUrl/ApiToken configured yet - retry next cycle.' 'WARN' + Write-Log 'No shopdb BaseUrl configured yet - retry next cycle.' 'WARN' exit 0 } diff --git a/plugins/geenforce/client/ShopdbEnforceClient.psm1 b/plugins/geenforce/client/ShopdbEnforceClient.psm1 index b5df9b9..a9f8fae 100644 --- a/plugins/geenforce/client/ShopdbEnforceClient.psm1 +++ b/plugins/geenforce/client/ShopdbEnforceClient.psm1 @@ -66,10 +66,22 @@ function Get-ShopdbConfig { if (-not $BaseUrl -and $props.BaseUrl) { $BaseUrl = $props.BaseUrl } if (-not $ApiToken -and $props.ApiToken) { $ApiToken = $props.ApiToken } } - if (-not $BaseUrl -or -not $ApiToken) { return $null } + # ApiToken is OPTIONAL: on a vaulted network the server may authorize by + # source-IP allowlist, so a BaseUrl alone is a valid config. When a token + # is present it is still sent (and honored) for token-authorized sites. + if (-not $BaseUrl) { return $null } return @{ BaseUrl = $BaseUrl.TrimEnd('/'); ApiToken = $ApiToken } } + +function New-ShopdbAuthHeaders { + # Only send X-API-Key when a token is configured; a token-less client + # relies on the server's IP allowlist. + param($Config) + if ($Config.ApiToken) { return @{ 'X-API-Key' = $Config.ApiToken } } + return @{} +} + function Sync-ShopdbManifest { <# Fetch the current published manifest for a scope into a local cache, using @@ -87,7 +99,7 @@ function Sync-ShopdbManifest { if (-not (Test-Path $CacheDir)) { New-Item -ItemType Directory -Path $CacheDir -Force | Out-Null } $manifestPath = Join-Path $CacheDir "$Scope.json" $etagPath = Join-Path $CacheDir "$Scope.etag" - $headers = @{ 'X-API-Key' = $Config.ApiToken } + $headers = New-ShopdbAuthHeaders $Config if (Test-Path $etagPath) { $headers['If-None-Match'] = (Get-Content -LiteralPath $etagPath -Raw).Trim() } $uri = "$($Config.BaseUrl)/api/geenforce/manifest?pctype=$([uri]::EscapeDataString($Scope))" @@ -170,7 +182,7 @@ function Send-ShopdbReport { try { $body = ($Report | ConvertTo-Json -Depth 6) Invoke-RestMethod -Uri "$($Config.BaseUrl)/api/geenforce/report" ` - -Method Post -Headers @{ 'X-API-Key' = $Config.ApiToken } ` + -Method Post -Headers (New-ShopdbAuthHeaders $Config) ` -ContentType 'application/json' -Body $body -TimeoutSec 30 -ErrorAction Stop | Out-Null return $true } catch { @@ -250,7 +262,7 @@ function Get-ShopdbPayload { $uri = "$($Config.BaseUrl)/api/geenforce/payload/$sha" $tmp = "$dest.tmp" try { - Invoke-WebRequest -Uri $uri -Headers @{ 'X-API-Key' = $Config.ApiToken } ` + Invoke-WebRequest -Uri $uri -Headers (New-ShopdbAuthHeaders $Config) ` -UseBasicParsing -TimeoutSec 120 -OutFile $tmp -ErrorAction Stop } catch { if (Test-Path $tmp) { Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue }