Org-wide trusted CI workflows that run in an isolated trust domain. App teams have no write access to this repository — that unwritability is the security property: a calling build cannot alter or influence these workflows, so artifacts they sign carry an identity the caller cannot forge.
A reusable workflow that attaches a SLSA v0.2 build-provenance attestation to an already-built container image, signed keyless (Sigstore Fulcio/Rekor) with this workflow's own GitHub OIDC identity.
Why (SLSA Build L3 — platform #131, ADR-042): if an app's own build job both writes and
signs its provenance, a compromised build step could forge it. Moving the signing here — where
the app team can't edit the workflow — makes the provenance non-forgeable: the Fulcio
certificate's signer identity (SAN) is …/trusted-ci/.github/workflows/slsa-provenance.yml@…,
not the caller. The platform's Kyverno policy admits only provenance signed by this identity
(and, per team, only when the certificate's GitHub-workflow-repository extension is that team's
own app-<team> repo).
It mints its own ECR token (assume-role → ecr-login), so no registry credential crosses a
job boundary — which is exactly why the off-the-shelf slsa-github-generator couldn't be used
with ECR (see #131 P0).
provenance:
needs: [build] # a job that outputs the image (no tag) and digest
permissions:
id-token: write
contents: read
uses: asanexample/trusted-ci/.github/workflows/slsa-provenance.yml@<commit-sha>
with:
image: ${{ needs.build.outputs.image }} # <acct>.dkr.ecr.<region>.amazonaws.com/team-<team>/<app>
digest: ${{ needs.build.outputs.digest }} # sha256:<hex>Pin uses: to a commit SHA (supply-chain hygiene).
cosign verify-attestation --type slsaprovenance \
--certificate-identity-regexp 'https://github.com/asanexample/trusted-ci/.github/workflows/slsa-provenance.yml@.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-github-workflow-repository asanexample/app-<team> \
<image>@<digest>- Private repo; reusable-workflow call access is restricted to the app repos. A reusable workflow's signing identity is reusable by anyone who can call it, so this is the first containment line.
team/imageguard: the workflow refuses to attest an image outside the callingapp-<team>repo'steam-<team>/*namespace.- AWS role: the signer assumes the caller team's existing
github-actions-ecr-push-<team>role (derived from the caller repo), whose ECR scope is already limited toteam-<team>/*. We do not use a sharedjob_workflow_ref-scoped role: AWS only honors the OIDCsub/audclaims in IAM trust conditions, notjob_workflow_ref. The AWS role is only ECR-push transport — the non-forgeability guarantee is the cosign certificate identity, not the role. - Recommended follow-up: SHA-pin the third-party actions below (Dependabot keeps them
current); branch protection + this
CODEOWNERSkeep changes reviewed by the platform team.