From a9540af31af8b973bd9f18b911a03f5db8b79f12 Mon Sep 17 00:00:00 2001 From: Richard Boisvert Date: Fri, 26 Jun 2026 14:28:20 -0400 Subject: [PATCH] [DEVOPS] ci(release): add test trusted signing --- .github/workflows/release.yml | 95 +++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d87d153..e3e8fba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -373,6 +373,9 @@ jobs: - preflight - build environment: ${{ needs.preflight.outputs.publish_env }} + permissions: + id-token: write + contents: read steps: - name: Download unsigned Windows x64 binary @@ -409,24 +412,45 @@ jobs: id: signing_mode shell: pwsh env: + PUBLISH_ENV: ${{ needs.preflight.outputs.publish_env }} CODE_SIGNING_KEYVAULT_URL: ${{ secrets.CODE_SIGNING_KEYVAULT_URL }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} CODE_SIGNING_CLIENT_ID: ${{ secrets.CODE_SIGNING_CLIENT_ID }} CODE_SIGNING_CLIENT_SECRET: ${{ secrets.CODE_SIGNING_CLIENT_SECRET }} CODE_SIGNING_CERTIFICATE_NAME: ${{ secrets.CODE_SIGNING_CERTIFICATE_NAME }} CODE_SIGNING_TIMESTAMP_SERVER: ${{ vars.CODE_SIGNING_TIMESTAMP_SERVER }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + TRUSTED_SIGNING_ENDPOINT: ${{ secrets.TRUSTED_SIGNING_ENDPOINT }} + TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.TRUSTED_SIGNING_ACCOUNT_NAME }} + TRUSTED_SIGNING_PROFILE_NAME: ${{ secrets.TRUSTED_SIGNING_PROFILE_NAME }} run: | $dryRun = [System.Boolean]::Parse('${{ needs.preflight.outputs.dry_run }}') $signDryRun = [System.Boolean]::Parse('${{ github.event.inputs.sign_dry_run }}') - $requiredValues = @( - $env:CODE_SIGNING_KEYVAULT_URL, - $env:AZURE_TENANT_ID, - $env:CODE_SIGNING_CLIENT_ID, - $env:CODE_SIGNING_CLIENT_SECRET, - $env:CODE_SIGNING_CERTIFICATE_NAME, - $env:CODE_SIGNING_TIMESTAMP_SERVER - ) + # publish-test signs with the Azure Trusted Signing (Artifact Signing) test + # profile over OIDC; publish-prod keeps the production EV Azure Key Vault path. + if ($env:PUBLISH_ENV -eq 'publish-test') { + $backend = 'trusted-signing' + $requiredValues = @( + $env:AZURE_CLIENT_ID, + $env:AZURE_TENANT_ID, + $env:AZURE_SUBSCRIPTION_ID, + $env:TRUSTED_SIGNING_ENDPOINT, + $env:TRUSTED_SIGNING_ACCOUNT_NAME, + $env:TRUSTED_SIGNING_PROFILE_NAME + ) + } else { + $backend = 'key-vault' + $requiredValues = @( + $env:CODE_SIGNING_KEYVAULT_URL, + $env:AZURE_TENANT_ID, + $env:CODE_SIGNING_CLIENT_ID, + $env:CODE_SIGNING_CLIENT_SECRET, + $env:CODE_SIGNING_CERTIFICATE_NAME, + $env:CODE_SIGNING_TIMESTAMP_SERVER + ) + } $hasSigningSecrets = $true foreach ($value in $requiredValues) { @@ -439,13 +463,15 @@ jobs: $shouldSign = $hasSigningSecrets -and ((-not $dryRun) -or $signDryRun) if (-not $dryRun -and -not $hasSigningSecrets) { - throw 'Code signing secrets are required for non-dry-run release jobs.' + throw "Code signing secrets are required for non-dry-run release jobs ($backend backend)." } "dry_run=$($dryRun.ToString().ToLowerInvariant())" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append "should_sign=$($shouldSign.ToString().ToLowerInvariant())" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append + "backend=$backend" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append Write-Host "Windows signing dry-run mode: $dryRun" + Write-Host "Windows signing backend: $backend" Write-Host "Windows binaries will be signed with Linux psign-tool: $shouldSign" - name: Add Linux psign-tool to PATH @@ -474,8 +500,8 @@ jobs: $toolDir | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append & $sourceToolPath --version - - name: Code sign Windows executables - if: steps.signing_mode.outputs.should_sign == 'true' + - name: Code sign Windows executables (Azure Key Vault) + if: steps.signing_mode.outputs.should_sign == 'true' && steps.signing_mode.outputs.backend == 'key-vault' shell: pwsh env: CODE_SIGNING_KEYVAULT_URL: ${{ secrets.CODE_SIGNING_KEYVAULT_URL }} @@ -549,6 +575,53 @@ jobs: throw "psign-core signing failed for win-arm64 with exit code $LASTEXITCODE" } + - name: Azure login for Trusted Signing + if: steps.signing_mode.outputs.should_sign == 'true' && steps.signing_mode.outputs.backend == 'trusted-signing' + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Code sign Windows executables (Trusted Signing) + if: steps.signing_mode.outputs.should_sign == 'true' && steps.signing_mode.outputs.backend == 'trusted-signing' + shell: pwsh + env: + TRUSTED_SIGNING_ENDPOINT: ${{ secrets.TRUSTED_SIGNING_ENDPOINT }} + TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.TRUSTED_SIGNING_ACCOUNT_NAME }} + TRUSTED_SIGNING_PROFILE_NAME: ${{ secrets.TRUSTED_SIGNING_PROFILE_NAME }} + run: | + $accessToken = az account get-access-token ` + --scope https://codesigning.azure.net/.default ` + --query accessToken ` + --output tsv + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($accessToken)) { + throw 'Failed to acquire a Trusted Signing data-plane access token.' + } + + $targets = @( + 'work/win-x64/psign-tool.exe', + 'work/win-arm64/psign-tool.exe', + 'work/psign-core-win-x64/psign-core.dll', + 'work/psign-core-win-arm64/psign-core.dll' + ) + + foreach ($target in $targets) { + psign-tool --mode portable --verbose sign ` + --artifact-signing-endpoint "$env:TRUSTED_SIGNING_ENDPOINT" ` + --artifact-signing-account-name "$env:TRUSTED_SIGNING_ACCOUNT_NAME" ` + --artifact-signing-profile-name "$env:TRUSTED_SIGNING_PROFILE_NAME" ` + --artifact-signing-access-token "$accessToken" ` + --timestamp-url http://timestamp.acs.microsoft.com/ ` + --timestamp-digest sha256 ` + --digest sha256 ` + "$target" + + if ($LASTEXITCODE -ne 0) { + throw "psign-tool Trusted Signing failed for $target with exit code $LASTEXITCODE" + } + } + - name: Package Windows artifacts shell: pwsh run: |