| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- name: 'Sign Container Image'
- description: >-
- Sign a container image and attest SLSA v1 build provenance using cosign
- keyless (Sigstore) with GitHub Actions OIDC. Callers must check out the
- source tree (e.g. `actions/checkout`) before invoking this action — the
- built commit is resolved with `git rev-parse HEAD` so the attestation
- records the revision that was actually built rather than `github.sha`,
- which on a tag push reflects the tag-pointed commit and may differ from
- the branch tip the release workflow checks out. The calling job must
- have `id-token: write`.
- inputs:
- image:
- description: 'Full image reference (registry/repo:tag) to sign by digest.'
- required: true
- workflow-path:
- description: 'Path to the workflow file (repo-relative) that triggered the build.'
- required: true
- run-started-at:
- description: >-
- ISO-8601 workflow run start time, typically `${{ github.run_started_at }}`
- from the caller. Recorded as `runDetails.metadata.startedOn` in the
- SLSA provenance predicate.
- required: true
- runs:
- using: "composite"
- steps:
- - name: Install cosign
- uses: sigstore/cosign-installer@v3
- - name: Install crane
- uses: imjasonh/setup-crane@v0.5
- - name: Resolve image digest
- id: digest
- shell: bash
- env:
- IMAGE: ${{ inputs.image }}
- run: |
- set -euo pipefail
- DIGEST="$(crane digest "${IMAGE}")"
- REPO="${IMAGE%:*}"
- REF="${REPO}@${DIGEST}"
- echo "Resolved ${IMAGE} -> ${REF}"
- echo "REF=${REF}" >> "$GITHUB_OUTPUT"
- echo "DIGEST=${DIGEST}" >> "$GITHUB_OUTPUT"
- - name: Sign image with cosign (keyless)
- shell: bash
- env:
- REF: ${{ steps.digest.outputs.REF }}
- run: |
- set -euo pipefail
- cosign sign --yes "${REF}"
- - name: Generate SLSA v1 provenance predicate
- shell: bash
- env:
- WORKFLOW_PATH: ${{ inputs.workflow-path }}
- STARTED_ON: ${{ inputs.run-started-at }}
- run: |
- set -euo pipefail
- RESOLVED_GIT_COMMIT="$(git rev-parse HEAD)"
- export RESOLVED_GIT_COMMIT
- python3 - <<'PY' > predicate.json
- import json
- import os
- repo = os.environ["GITHUB_REPOSITORY"]
- commit = os.environ["RESOLVED_GIT_COMMIT"]
- run_id = os.environ["GITHUB_RUN_ID"]
- run_attempt = os.environ["GITHUB_RUN_ATTEMPT"]
- predicate = {
- "buildDefinition": {
- "buildType": "https://github.com/opencost/opencost/build/workflow@v1",
- "externalParameters": {
- "workflow": {
- "ref": os.environ["GITHUB_REF"],
- "repository": f"https://github.com/{repo}",
- "path": os.environ["WORKFLOW_PATH"],
- }
- },
- "internalParameters": {},
- "resolvedDependencies": [
- {
- "uri": f"git+https://github.com/{repo}@{commit}",
- "digest": {"gitCommit": commit},
- }
- ],
- },
- "runDetails": {
- "builder": {
- "id": f"https://github.com/{repo}/actions/runs/{run_id}",
- },
- "metadata": {
- "invocationId": f"https://github.com/{repo}/actions/runs/{run_id}/attempts/{run_attempt}",
- "startedOn": os.environ["STARTED_ON"],
- },
- },
- }
- print(json.dumps(predicate, indent=2))
- PY
- - name: Attest SLSA provenance with cosign
- shell: bash
- env:
- REF: ${{ steps.digest.outputs.REF }}
- run: |
- set -euo pipefail
- cosign attest --yes \
- --predicate predicate.json \
- --type slsaprovenance1 \
- "${REF}"
|