diff --git a/.github/workflows/gateway-msix.yml b/.github/workflows/gateway-msix.yml index 81c0b757..2aff1cb8 100644 --- a/.github/workflows/gateway-msix.yml +++ b/.github/workflows/gateway-msix.yml @@ -84,6 +84,11 @@ jobs: run: > .\scripts\Test-SigningInputs.Tests.ps1 + - name: Test signing workflow configuration + shell: pwsh + run: > + .\scripts\Test-WorkflowSigningConfiguration.ps1 + - name: Test workflow package version shell: pwsh run: > @@ -404,9 +409,9 @@ jobs: - name: Azure login uses: azure/login@v3 with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - name: Sign MSIX packages uses: azure/artifact-signing-action@v2 @@ -416,6 +421,7 @@ jobs: certificate-profile-name: openclaw files-folder: artifacts files-folder-filter: msix + files-folder-recurse: true files-folder-depth: 2 file-digest: SHA256 timestamp-rfc3161: http://timestamp.acs.microsoft.com diff --git a/README.md b/README.md index 064acd24..b874ae82 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,35 @@ Test-signing private keys are generated only on the temporary GitHub runner and are deleted before artifacts are uploaded. No signing secret or private key is stored in the repository. +### Official signing setup + +The `release-signing` GitHub environment must define these environment +variables (they are identifiers, not credentials): + +- `AZURE_CLIENT_ID`: application (client) ID of the dedicated + `openclaw-windows-msix-signing` Entra application; +- `AZURE_TENANT_ID`: Entra tenant ID; +- `AZURE_SUBSCRIPTION_ID`: Azure subscription containing the signing resource. + +Do not create an `AZURE_CLIENT_SECRET`. The `sign-msix` job requests a +short-lived Azure token with GitHub OIDC. The Entra application must have a +federated identity credential with: + +- issuer: `https://token.actions.githubusercontent.com`; +- subject: + `repo:openclaw@252820863/openclaw-windows-packaging@1347889239:environment:release-signing`; +- audience: `api://AzureADTokenExchange`. + +This repository was created after GitHub's immutable OIDC subject rollout, so +the subject includes the organization and repository IDs. The older mutable +`repo:openclaw/openclaw-windows-packaging:...` form will not match its tokens. + +The service principal must have `Artifact Signing Certificate Profile Signer` +on the `openclaw` certificate profile (or a containing scope). The workflow +uses account `openclaw`, certificate profile `openclaw`, and endpoint +`https://eus.codesigning.azure.net/`. The expected public certificate subject +is recorded in `release-policy.json`. + ## Installed data | Data | Default path | diff --git a/scripts/Test-WorkflowSigningConfiguration.ps1 b/scripts/Test-WorkflowSigningConfiguration.ps1 new file mode 100644 index 00000000..b47146f9 --- /dev/null +++ b/scripts/Test-WorkflowSigningConfiguration.ps1 @@ -0,0 +1,37 @@ +[CmdletBinding()] +param() + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$repositoryRoot = Split-Path $PSScriptRoot -Parent +$workflowPath = Join-Path ` + $repositoryRoot ` + '.github\workflows\gateway-msix.yml' +$workflow = Get-Content -LiteralPath $workflowPath -Raw + +$requiredFragments = @( + 'environment: release-signing' + 'id-token: write' + 'uses: azure/login@v3' + 'client-id: ${{ vars.AZURE_CLIENT_ID }}' + 'tenant-id: ${{ vars.AZURE_TENANT_ID }}' + 'subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}' + 'uses: azure/artifact-signing-action@v2' + 'files-folder-recurse: true' + 'endpoint: https://eus.codesigning.azure.net/' + 'signing-account-name: openclaw' + 'certificate-profile-name: openclaw' +) + +foreach ($fragment in $requiredFragments) { + if (-not $workflow.Contains($fragment, [StringComparison]::Ordinal)) { + throw "Signing workflow is missing required configuration: $fragment" + } +} + +if ($workflow.Contains('AZURE_CLIENT_SECRET', [StringComparison]::Ordinal)) { + throw 'Signing workflow must use OIDC, not an Azure client secret.' +} + +Write-Host 'Gateway MSIX signing workflow configuration passed.'