Files
PacketStation/scripts/Export-PescoCaBundle.ps1
spencermandClaude Fable 5.1 94008ceed0
ci / frontend (push) Successful in 35s
ci / server (push) Successful in 18m3s
ci / image (push) Successful in 13s
ci / e2e (push) Successful in 1m44s
scripts: Export-PescoCaBundle.ps1 exports the PESCO CA chain from Windows and copies it to docker-1
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-17 12:04:10 -06:00

71 lines
2.9 KiB
PowerShell

<#
.SYNOPSIS
Exports the PESCO internal CA certificates from this PC's Windows certificate stores
to a PEM bundle and copies it to docker-1 for the Packet Station stacks.
.DESCRIPTION
Run on a domain-joined PC. It reads the LocalMachine and CurrentUser Root and CA stores,
keeps every unexpired certificate whose subject matches -SubjectMatch (default PESCO),
writes them as one PEM file with a comment line per certificate, and copies the file to
the remote path with scp. The file holds public certificates only; nothing secret.
The stacks point LDAP_CA_BUNDLE and SMTP_CA_BUNDLE at this file. MFILES_CA_BUNDLE stays
unset because M-Files uses a public certificate.
.EXAMPLE
.\scripts\Export-PescoCaBundle.ps1
Writes the bundle and copies it to [email protected]:~/packetstation/test/ca/pesco-ca.pem
.EXAMPLE
.\scripts\Export-PescoCaBundle.ps1 -Remote '[email protected]:/etc/packetstation/ca/pesco-ca.pem'
.EXAMPLE
.\scripts\Export-PescoCaBundle.ps1 -NoCopy -OutFile C:\temp\pesco-ca.pem
#>
[CmdletBinding()]
param(
# Regex matched against each certificate subject.
[string]$SubjectMatch = 'PESCO',
# Local PEM file to write.
[string]$OutFile = (Join-Path $env:TEMP 'pesco-ca.pem'),
# scp destination. Needs an SSH key that works without a prompt.
[string]$Remote = '[email protected]:~/packetstation/test/ca/pesco-ca.pem',
# Write the file only; do not copy it.
[switch]$NoCopy
)
$ErrorActionPreference = 'Stop'
$now = Get-Date
$stores = 'Cert:\LocalMachine\Root', 'Cert:\LocalMachine\CA', 'Cert:\CurrentUser\Root', 'Cert:\CurrentUser\CA'
$certs = Get-ChildItem $stores |
Where-Object { $_.Subject -match $SubjectMatch -and $_.NotAfter -gt $now } |
Sort-Object Thumbprint -Unique
if (-not $certs) {
throw "No unexpired certificate with subject matching '$SubjectMatch' in the Root or CA stores."
}
$sb = New-Object System.Text.StringBuilder
foreach ($c in $certs) {
$sig = $c.SignatureAlgorithm.FriendlyName
[void]$sb.AppendLine("# $($c.Subject) | issued by $($c.Issuer) | expires $($c.NotAfter.ToString('yyyy-MM-dd')) | $sig")
[void]$sb.AppendLine('-----BEGIN CERTIFICATE-----')
[void]$sb.AppendLine([Convert]::ToBase64String($c.RawData, 'InsertLineBreaks'))
[void]$sb.AppendLine('-----END CERTIFICATE-----')
Write-Host (" {0} expires {1} {2}" -f $c.Subject, $c.NotAfter.ToString('yyyy-MM-dd'), $sig)
if ($sig -match 'sha1') {
Write-Warning "$($c.Subject) is SHA-1 signed. OpenSSL 3 refuses SHA-1 chains at its default security level; see docs/DEPLOY.md."
}
}
# LF line endings so the file is clean on Linux.
[IO.File]::WriteAllText($OutFile, $sb.ToString().Replace("`r`n", "`n"))
Write-Host "Wrote $OutFile ($($certs.Count) certificates)"
if ($NoCopy) { return }
& scp -o BatchMode=yes $OutFile $Remote
if ($LASTEXITCODE -ne 0) { throw "scp to $Remote failed (exit $LASTEXITCODE)" }
Write-Host "Copied to $Remote"