diff --git a/.github/scripts/Get-Endpoints.ps1 b/.github/scripts/Get-Endpoints.ps1 index 2a453457..5f693449 100644 --- a/.github/scripts/Get-Endpoints.ps1 +++ b/.github/scripts/Get-Endpoints.ps1 @@ -4,15 +4,16 @@ .DESCRIPTION CI's member of the URL registry (Shared Constants.cs, web lib/config.ts, MCP src/config.ts): - every own host the workflows, the .github/scripts helpers and the local release build talk to, - plus the Authenticode timestamp server, is defined here once. Assert-HostsFromRegistry.ps1 - (Workflow gates) fails on any of these hosts spelled out elsewhere under .github/. + every own host the workflows and the .github/scripts helpers talk to, plus the Authenticode + timestamp server, is defined here once. Assert-HostsFromRegistry.ps1 (Workflow gates) fails + on any of these hosts spelled out elsewhere under .github/. The storage URLs derive from the account name, so blob and table can never point at two different accounts. - A file in the repository rather than a GitHub variable: the local release build reads it too, - and bearer tokens are sent to these URLs -- where a token goes stays a reviewed line in git. + A file in the repository rather than a GitHub variable: the helpers read it themselves, so + they behave the same in every job and in a local run, and bearer tokens are sent to these + URLs -- where a token goes stays a reviewed line in git. .PARAMETER ExportToGitHubEnv Write every entry to $GITHUB_ENV instead of returning it, so the following steps of the job diff --git a/.github/scripts/Get-StorageAuthHeaders.ps1 b/.github/scripts/Get-StorageAuthHeaders.ps1 index 44100324..31b5a207 100644 --- a/.github/scripts/Get-StorageAuthHeaders.ps1 +++ b/.github/scripts/Get-StorageAuthHeaders.ps1 @@ -9,8 +9,8 @@ live here so they cannot drift between call sites. Identity comes from whatever `az login` established: the federated GitHub OIDC principal - in CI (azure/login, subject repo:okieselbach/AutopilotMonitor:ref:refs/heads/main), the - operator's own account for the local build. No secret is read, stored or passed around. + in CI (azure/login in a job of the `production` or `release` environment), the operator's + own account when a helper runs locally. No secret is read, stored or passed around. Under GitHub Actions the token is masked in the run log before it is returned. x-ms-version is MANDATORY here, not decoration: Entra-authorized requests are rejected diff --git a/.github/workflows/build-agent.yml b/.github/workflows/build-agent.yml index 5d0b961f..fc5444ab 100644 --- a/.github/workflows/build-agent.yml +++ b/.github/workflows/build-agent.yml @@ -258,12 +258,14 @@ jobs: # run here -- never a restore or build. The build output arrives as data. The runner # image's Windows SDK signtool is used read-only, to verify third-party signatures. # - # No `environment:` yet: the code-signing credential is still bound to the ref, and a job - # carries exactly one OIDC subject, so an environment here would break the signing login. + # Runs in the `release` environment: both identities trust its subject, and it admits main + # only. No federated credential trusts the ref, so a job without an environment can neither + # sign nor write storage, whatever its permissions say. release: name: Sign and publish needs: [reserve, build] runs-on: windows-latest + environment: release permissions: contents: write # tag + GitHub Release id-token: write # code-signing and storage logins, keyless Sigstore signing diff --git a/.github/workflows/build-msi.yml b/.github/workflows/build-msi.yml index 59453a19..c6cc571e 100644 --- a/.github/workflows/build-msi.yml +++ b/.github/workflows/build-msi.yml @@ -39,11 +39,11 @@ jobs: # need the embedded loader to carry a signature of its own. # # Trust zone: only this repository's scripts, pinned actions and the pinned signing tool run - # here. No `environment:` yet: the code-signing credential is still bound to the ref, and a - # job carries exactly one OIDC subject. + # here. The `release` environment is the only subject the code-signing identity trusts. sign-loader: name: Sign bootstrap loader runs-on: windows-latest + environment: release permissions: contents: read id-token: write # code-signing login @@ -198,12 +198,13 @@ jobs: if-no-files-found: error retention-days: 1 - # Trust zone: signs, verifies and publishes the MSI as data; never builds. No - # `environment:` yet, for the same reason as sign-loader. + # Trust zone: signs, verifies and publishes the MSI as data; never builds. Environment + # `release`, like sign-loader. release: name: Sign and publish MSI needs: build runs-on: windows-latest + environment: release permissions: contents: read id-token: write # code-signing and storage logins, keyless Sigstore signing diff --git a/.github/workflows/publish-scripts.yml b/.github/workflows/publish-scripts.yml index 13591dce..e99861fb 100644 --- a/.github/workflows/publish-scripts.yml +++ b/.github/workflows/publish-scripts.yml @@ -54,6 +54,8 @@ jobs: name: Publish to the download alias needs: gates runs-on: windows-latest + # Both identities trust only the `release` environment, which admits main only. + environment: release # Job-scoped: only the publish job federates to Azure, the gates job never holds an # OIDC token it does not need. permissions: @@ -74,9 +76,7 @@ jobs: } # First, and only to fetch the Key Vault token: azure/login sets the default az - # account and the storage login below has to be the one that stays. See D-223 -- - # both federated credentials are bound to the ref, so this job must never carry a - # GitHub `environment:`. + # account and the storage login below has to be the one that stays. - name: Login to Azure (code-signing identity) uses: azure/login@a641126d1b8aa4d1fa005f4f92df94a3a4c4c906 # v3.1.0 with: