Exchange cloud identity for registry access
Publishing an image requires registry authorization. A long-lived service-account key in GitHub would let anyone who stole the key impersonate that account until it was revoked. This baseline instead exchanges a GitHub-issued identity token through GCP Workload Identity Federation, then impersonates a service account for short-lived access.
- Cloud path: GitHub token exchanges through federation for short-lived GCP service-account access.
- The CI cloud account publishes/reads registry content under IAM.
- Signing path: the workflow obtains keyless identity for the canonical sign-attest.yml at refs/heads/main.
- Fulcio certificate and Rekor evidence support Cosign verification against subject and issuer.
Solid arrows indicate the stated handoff, not a claim of independent trust. Optional relationships are described in the text equivalent. Historical components are labeled in the caption.
The actual exchange
Section titled “The actual exchange”The reusable jobs request id-token: write. The local GCP authentication action calls google-github-actions/auth with the configured federation provider and service-account email, then configures Docker for the GAR host. These inputs come from repository variables through the trusted caller. The action does not create a JSON service-account key.
The federation definition maps GitHub claims into attributes, accepts issuer https://token.actions.githubusercontent.com, and uses the condition assertion.repository == '${var.github_repository}'. A service-account IAM binding authorizes the matching repository principal set to impersonate supply-chain-ci.
That service account receives roles/artifactregistry.writer on the application repository and the separate Cosign metadata repository. Scope is narrower than project-wide writer access. It still carries publishing authority in each authenticated build/sign/verify job that uses the same account.
A critical boundary belongs to workflow code
Section titled “A critical boundary belongs to workflow code”The provider maps assertion.ref and workflow-related attributes but does not compare them in its acceptance condition. The impersonation binding also uses the repository attribute, not the ref. Consequently this final Terraform source does not independently enforce main-only cloud impersonation. The release caller’s job conditions enforce the trusted-main path in the inspected workflow.
This distinction changes the threat analysis. Short credential lifetime reduces the harm of a copied credential, but a compromised repository or changed workflow with token permission can attack the trust contract itself. Do not equate “OIDC-only” with “only one main workflow can ever impersonate this account.” Historical remote federation settings would require separate inspection to establish more restrictive enforcement than the checked-in source.
Cloud account and signer are different identities
Section titled “Cloud account and signer are different identities”The GCP service account can write to GAR. It is not the keyless certificate subject accepted by Kyverno. Cosign signing uses GitHub OIDC for a Sigstore identity whose subject names sign-attest.yml@refs/heads/main. Both exchanges begin with GitHub identity, but one grants cloud API access and the other supports a verifiable artifact statement.
Kyverno takes a third path: its Kubernetes ServiceAccount is bound through GKE Workload Identity to kyverno-verifier, with reader access to both repositories. It reads the image and its signature/attestation metadata. That reader relationship does not make Kyverno an image publisher or trusted signer.
Limits and operational consequences
Section titled “Limits and operational consequences”The verification job’s commands only read and verify, yet the job authenticates using the CI writer account. Its effective cloud role is therefore broader than its command purpose. Least privilege must be assessed from IAM grants as well as shell commands and workflow names.
In an owned reproduction, align the provider, repository/ref conditions, service-account roles, and job permissions deliberately; wiring explains the dependencies. Do not copy historical service-account identifiers into a different project or assume a fork inherits federation. Next, inspect which workflow identity signs the digest.