-
Notifications
You must be signed in to change notification settings - Fork 8
Harden Windows signing verification in CI #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| $ErrorActionPreference = "Stop" | ||
| Set-StrictMode -Version Latest | ||
|
|
||
| $artifacts = @(Get-ChildItem -Path "dist\*.exe" -File -ErrorAction SilentlyContinue) | ||
| if ($artifacts.Count -eq 0) { | ||
| Write-Host "[!] No Windows .exe artifacts found in dist." | ||
| exit 1 | ||
| } | ||
|
|
||
| if ([string]::IsNullOrWhiteSpace($env:SIGNTOOL_PATH)) { | ||
| Write-Host "[!] SIGNTOOL_PATH is not set." | ||
| exit 1 | ||
| } | ||
|
|
||
| if ([string]::IsNullOrWhiteSpace($env:WINDOWS_EXPECTED_SIGNER_SUBJECT)) { | ||
| Write-Host "[!] WINDOWS_EXPECTED_SIGNER_SUBJECT must be set to verify the artifact signer identity." | ||
| exit 1 | ||
| } | ||
|
|
||
| foreach ($artifact in $artifacts) { | ||
| Write-Host "Verifying Authenticode signature for $($artifact.FullName)" | ||
| & $env:SIGNTOOL_PATH verify /pa /v $artifact.FullName | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at this, I wonder if we should consider |
||
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | ||
|
|
||
| $signature = Get-AuthenticodeSignature -FilePath $artifact.FullName | ||
| if ($signature.Status -ne "Valid" -or $null -eq $signature.SignerCertificate) { | ||
| Write-Host "[!] $($artifact.Name) does not have a valid Authenticode signature." | ||
| Write-Host "Status: $($signature.Status)" | ||
| exit 1 | ||
| } | ||
|
|
||
| $actualSubject = $signature.SignerCertificate.Subject | ||
| if ($actualSubject.IndexOf($env:WINDOWS_EXPECTED_SIGNER_SUBJECT.Trim(), [System.StringComparison]::OrdinalIgnoreCase) -lt 0) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm finding this check a bit loose ( |
||
| Write-Host "[!] $($artifact.Name) was not signed by the expected subject." | ||
| Write-Host "Expected subject to contain: $($env:WINDOWS_EXPECTED_SIGNER_SUBJECT.Trim())" | ||
| Write-Host "Actual signer subject: $actualSubject" | ||
| exit 1 | ||
| } | ||
|
|
||
| if (-not [string]::IsNullOrWhiteSpace($env:WINDOWS_EXPECTED_SIGNER_ISSUER)) { | ||
| $actualIssuer = $signature.SignerCertificate.Issuer | ||
| if ($actualIssuer.IndexOf($env:WINDOWS_EXPECTED_SIGNER_ISSUER.Trim(), [System.StringComparison]::OrdinalIgnoreCase) -lt 0) { | ||
| Write-Host "[!] $($artifact.Name) was not signed by the expected issuer." | ||
| Write-Host "Expected issuer to contain: $($env:WINDOWS_EXPECTED_SIGNER_ISSUER.Trim())" | ||
| Write-Host "Actual signer issuer: $actualIssuer" | ||
| exit 1 | ||
| } | ||
| } | ||
|
|
||
| Write-Host "$($artifact.Name) signer identity verified." | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,28 +9,53 @@ steps: | |
| agents: { queue: windows } | ||
| plugins: [$CI_TOOLKIT, $NVM_PLUGIN] | ||
| command: | | ||
| $ErrorActionPreference = "Stop" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest to move this whole command to an external script to avoid unexpected issues with the Buildkite variable interpolation (https://buildkite.com/docs/pipelines/configure/environment-variables#runtime-variable-interpolation). It also makes the code easier to read. For an example of what might go wrong when using scripts directly in the |
||
|
|
||
| function Invoke-NativeCommand { | ||
| param ( | ||
| [Parameter(Mandatory = $true)] | ||
| [string]$FilePath, | ||
| [Parameter(ValueFromRemainingArguments = $true)] | ||
| [string[]]$ArgumentList | ||
| ) | ||
|
|
||
| & $FilePath @ArgumentList | ||
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | ||
| } | ||
|
|
||
| echo "~~~ Setup code signing" | ||
| .buildkite/commands/setup_windows_code_signing.ps1 | ||
|
|
||
| echo "~~~ Install dependencies" | ||
| bash .buildkite/commands/install_node_dependencies.sh | ||
| Invoke-NativeCommand bash .buildkite/commands/install_node_dependencies.sh | ||
|
|
||
| echo "~~~ Build renderer" | ||
| npm run build:once | ||
| Invoke-NativeCommand npm run build:once | ||
|
|
||
| echo "~~~ Build Windows artifact" | ||
| npm run dist:win | ||
| Invoke-NativeCommand npm run dist:win | ||
|
|
||
| echo "~~~ Verify Azure Trusted Signing signature" | ||
| Get-ChildItem dist\*.exe | ForEach-Object { | ||
| & $env:SIGNTOOL_PATH verify /pa /v $_.FullName | ||
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | ||
| } | ||
| .buildkite/commands/verify_windows_signature.ps1 | ||
| artifact_paths: | ||
| - dist\*.exe | ||
| notify: | ||
| - github_commit_status: { context: "Windows Build" } | ||
|
|
||
| ######################################################## | ||
| # Tests | ||
| ######################################################## | ||
| - label: ":node: Tests" | ||
| plugins: [$CI_TOOLKIT, $NVM_PLUGIN] | ||
| command: | | ||
| echo "~~~ Install dependencies" | ||
| .buildkite/commands/install_node_dependencies.sh | ||
|
|
||
| echo "~~~ Run tests" | ||
| npm test | ||
| notify: | ||
| - github_commit_status: { context: "Tests" } | ||
|
|
||
| ######################################################## | ||
| # macOS build | ||
| ######################################################## | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we run
setup_azure_trusted_signing.ps1froma8c-ci-toolkit-buildkite-plugin, which I think already executessigntooland loads the DLib? Then later in this script, we test the tool integrity after that.I'd say installation, verification and execution need to be separate phases, so perhaps this should be in
a8c-ci-toolkit, immediately after download and before the smoke test.