Skip to content

feat(release): sign binaries (SLSA L3) and container images (cosign) — OpenSSF Phase 3#531

Merged
christiangda merged 1 commit into
mainfrom
feat/openssf-signed-releases
May 23, 2026
Merged

feat(release): sign binaries (SLSA L3) and container images (cosign) — OpenSSF Phase 3#531
christiangda merged 1 commit into
mainfrom
feat/openssf-signed-releases

Conversation

@christiangda
Copy link
Copy Markdown
Contributor

Summary

Phase 3 of the OpenSSF Scorecard hardening effort. Closes the Signed-Releases check (0/10 → 10/10) and lays groundwork toward CII-Best-Practices Silver/Gold (which both require cryptographic release signing).

Scorecard check Before Expected after
Signed-Releases 0 / 10 10 / 10 (starts counting from the first release cut after merge)

Note

The check looks at past releases, so the score will reflect 10/10 only after the next tagged release inherits these workflows. The plumbing itself is complete.

What changed

1. SLSA Level 3 provenance for binary release zips

  • New build job output hashes = base64(sha256sum *.zip).
  • New provenance job in .github/workflows/release.yml calls the official slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 with upload-assets: true.
  • It binds every release zip's sha256 to a single Sigstore-signed in-toto attestation (multiple.intoto.jsonl) and uploads it to the GitHub release alongside the zips.
  • Verifiable by anyone with slsa-verifier.

Important

The SLSA generator workflow is intentionally pinned by tag, not SHA. The SLSA verifier validates the workflow ref against its own allow-list of signed releases; SHA-pinning would break verification. This is the only documented exception to the project's SHA-pinning rule. A comment in release.yml and a note in docs/Whats-New.md flag this for future readers.

2. Cosign keyless signing for container images

  • container-image.yml now installs sigstore/cosign-installer@v3.9.2 (SHA-pinned).
  • After podman manifest push, resolves the multi-arch manifest tag to its content digest via podman manifest inspect | jq -r '.digest' and signs by digest with cosign sign --recursive (atomic vs. racy when signing by tag).
  • --recursive covers the manifest plus each per-platform image under it.
  • Both the tagged image (vX.Y.Z) and latest are signed.
  • Reuses the existing GHCR token via cosign login — no new secret required.

3. Docs

  • SECURITY.md — added a Verifying release artifacts section with copy-pasteable slsa-verifier verify-artifact and cosign verify commands. Also fixed a stale codeql-analysis.yml badge reference (actual workflow is codeql.yml).
  • docs/Whats-New.md — Phase 3 entry under Unreleased, including the SLSA tag-pinning rationale.

Verification (manual, post-merge)

The actual signing/provenance generation only runs on tagged release pushes. Plan:

  1. Merge this PR.
  2. Cut a pre-release tag (e.g. v0.44.2-rc1) to exercise the path end-to-end.
  3. Confirm the GitHub release contains:
    • All idpscim-*.zip and .sha256 files (existing behavior)
    • multiple.intoto.jsonl (new — SLSA provenance)
  4. Run the verification commands from SECURITY.md; expect `PASSED: SLSA verification passed` and a cosign verify with a valid Fulcio cert.
  5. Pull the new container image and run cosign verify ghcr.io/slashdevops/idp-scim-sync:v0.44.2-rc1 with the OIDC issuer matcher.
  6. Re-run scorecard --repo=... locally and confirm Signed-Releases moves toward 10/10 (will fully tick over once a non-RC release is cut).

Test plan (CI)

Follow-ups

  • PR-4: fuzz target + CI job
  • PR-5: packaging detector (goreleaser)
  • Admin: branch protection on main, CII Silver application (this PR unlocks the signing prerequisite)

🤖 Generated with Claude Code

…s with cosign

Closes the OpenSSF Scorecard Signed-Releases check (0/10 -> 10/10) and
boosts CII-Best-Practices toward Silver/Gold criteria for cryptographic
release signing.

Two complementary supply-chain primitives, both Sigstore-backed:

1. SLSA Level 3 provenance for binary release zips
-----------------------------------------------------
* New build-job output `hashes` = base64(sha256sum *.zip).
* New `provenance` job in release.yml calls
  slsa-framework/slsa-github-generator/.github/workflows/
  generator_generic_slsa3.yml@v2.1.0 with upload-assets: true.
* It binds every release zip's sha256 to a single Sigstore-signed
  in-toto attestation (multiple.intoto.jsonl) and uploads it to the
  GitHub release alongside the zips.
* SLSA generator is intentionally pinned by tag (not SHA): the SLSA
  verifier validates the workflow ref against its own allow-list of
  signed releases; SHA-pinning would break verification. Noted this
  exception in a comment and in Whats-New.md.

2. Cosign keyless signing for container images
-----------------------------------------------------
* container-image.yml now installs sigstore/cosign-installer@v3.9.2
  (SHA-pinned) and signs the published multi-arch manifest after
  podman manifest push.
* Signs by digest (resolved via `podman manifest inspect | jq -r
  .digest`), not by tag -- atomic vs racy on subsequent pushes.
* `cosign sign --recursive` covers the manifest plus each per-platform
  image under it. Both the tagged image and `latest` are signed.
* Reuses the existing GHCR token via `cosign login` to avoid asking
  for a new secret.

Docs
-----------------------------------------------------
* SECURITY.md gains a "Verifying release artifacts" section with
  copy-pasteable slsa-verifier and cosign-verify commands.
* Fixed the stale codeql-analysis.yml badge reference in SECURITY.md
  (the workflow is codeql.yml).

Will be smoke-tested on the next real release tag. The Scorecard check
itself looks at past releases, so it will start counting from the first
release cut after this lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@christiangda christiangda self-assigned this May 23, 2026
@christiangda christiangda added this pull request to the merge queue May 23, 2026
Merged via the queue into main with commit 849f8d1 May 23, 2026
5 checks passed
@christiangda christiangda deleted the feat/openssf-signed-releases branch May 23, 2026 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant