Skip to content

Sign release assets and attach an SBOM to each release #4759

Open

Description

Problem / motivation

No release carries a signature, provenance or SBOM. OpenSSF Scorecard's Signed-Releases check scores 0 ("0 out of 5 artifacts are signed or have provenance" for v1.7.0-alpha.3, v1.6.4, v1.6.4-rc.1, v1.6.3 and v1.6.3-rc.2). CLOMonitor's signed_releases and sbom checks fail with it and hold the security section down. Together with the missing Security Insights manifest (#4757), that section scores 72.73.

Users who install from the release page download a Talos ISO, raw disk images, a kernel and initramfs, operator manifests, CRDs and cozypkg binaries. They can verify none of it. The only checksum file is cozypkg-checksums.txt, which covers cozypkg alone and is not signed.

This gap is already acknowledged in several places:

Proposed solution

Both release paths end in hack/upload-assets.sh, with every asset in _out/assets. The rc path is the prepare-release job in tags.yaml. The stable path is promote-rc.yaml, after it re-stamps the version-stamped assets. Before the uploads, the script would:

  1. Generate cozystack-sbom.spdx.json with syft, over the source tree and the cozypkg binaries. The file name matters: CLOMonitor's sbom check matches (?i)sbom in the asset names of the latest release.
  2. Write cozystack-checksums.txt with the SHA-256 of every asset, the SBOM included.
  3. Sign it keylessly with the workflow's GitHub OIDC identity: cosign sign-blob --yes --bundle cozystack-checksums.txt.sigstore.json cozystack-checksums.txt. One signature then covers every file through the checksums.
  4. Upload all three files with the rest.

Workflow changes: the two jobs that call make upload_assets need id-token: write, and need sigstore/cosign-installer and anchore/sbom-action/download-syft pinned by SHA for the zizmor gate. Signing inside the script also covers promotion. promote-rc.yaml downloads the rc's assets and rebuilds some of them, so the stable release has to get freshly computed checksums and a fresh signature, not the rc's copies.

Users would verify with:

cosign verify-blob cozystack-checksums.txt \
  --bundle cozystack-checksums.txt.sigstore.json \
  --certificate-identity-regexp '^https://github.com/cozystack/cozystack/\.github/workflows/(tags|promote-rc)\.yaml@' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
sha256sum --check --ignore-missing cozystack-checksums.txt

That procedure would go into the release docs and into project.documentation.signature-verification in security-insights.yml.

Scorecard gives 8 when every recent release carries a signature. CLOMonitor passes once that score reaches 1, so the first release that carries the bundle flips both CLOMonitor checks. A later step to Scorecard's 10 would be SLSA provenance (*.intoto.jsonl) over the same checksums file, via the slsa-github-generator generic workflow.

Scope check

  • This affects a single component / package (if it spans many, consider a design proposal)
  • I can describe the expected behavior concretely (if not, consider a Discussion first)

Alternatives considered

  • GitHub artifact attestations (actions/attest-build-provenance). Users can verify them with gh attestation verify, but they live outside the release assets, so Scorecard and CLOMonitor do not see them unless the bundle is also attached. They can be added alongside the bundle.
  • PGP-signed checksums. This needs a long-lived key, its custody and rotation. Keyless signing ties the signature to the workflow identity instead.
  • Image SBOM attestations (Finish the CI image-build refactor: SBOM emission and pr-* tag retention #2937, item A2 in [Tracking] Remaining CI / build / release / E2E automation work #3278). They are complementary: they cover the container images, not the release downloads, and they do not satisfy CLOMonitor's sbom check, which reads only release assets and the README.

Additional context

Activity

  1. added
    kind/featureCategorizes issue or PR as related to a new feature
    triage/needs-triageIndicates an issue needs triage by a maintainer
    area/ciIssues or PRs related to CI workflows, GitHub Actions, automation
    area/releaseIssues or PRs related to release tooling (changelog, backport, release pipeline)
    on Oct 5, 2026
  2. themoriarti commented on Oct 21, 2026

    @themoriarti
    CollaboratorAuthor

    Status on 2026-10-21, with the pull requests that implement this issue and what is still open after them.

    Landed

    • feat(release): sign the release assets and attach a source SBOM #4861 (merged 2026-10-20): the release-asset half as proposed above. hack/upload-assets.sh hands its asset list to hack/sign-release-assets.sh, which writes cozystack-source-sbom.spdx.json (syft over the committed tree and the cozypkg binaries), cozystack-checksums.txt (SHA-256 of every asset and the SBOM) and cozystack-checksums.txt.sigstore.json (keyless cosign, identity pinned to tags.yaml@refs/tags/v* and promote-rc.yaml@refs/heads/main), and verifies the bundle before the first upload. Both upload jobs got id-token: write, syft 1.54.1 from its release tarball checked against a committed SHA-256, and cosign v3.1.3; docs/release.md carries the verify recipe. The source SBOM is named source on purpose, so cozystack-sbom stays free for a document of the whole release.

    Open, in review

    Still to do

    1. The first pre-release cut after these merge is the end-to-end test of all four: feat(release): sign the release assets and attach a source SBOM #4861's bundle, feat(release): attach an SBOM to every release image #4997's attestations in GHCR, feat(release): sign the release images and attach their SBOMs #5042's signatures and archive. Until a release carries the files nothing moves: the latest release, v1.6.5 of 2026-10-16, predates feat(release): sign the release assets and attach a source SBOM #4861, Scorecard still reports Signed-Releases 0 (scan of 2026-10-19), and CLOMonitor reads the latest release. The release-1.6 line stays unsigned until hack/sign-release-assets.sh and, later, the two scripts of feat(release): sign the release images and attach their SBOMs #5042 are backported to it, since promotion runs the rc tree's scripts.
    2. project.documentation.signature-verification in security-insights.yml → docs/release.md#how-to-verify-a-release, once the first signed release exists (the docs(security): add an OpenSSF Security Insights manifest #4757 follow-up).
    3. Scorecard's 10 for Signed-Releases needs a release-level SLSA provenance over cozystack-checksums.txt (*.intoto.jsonl, the slsa-github-generator generic workflow, as the issue says). feat(build): attach a provenance attestation beside the SBOM #5043's provenance is per image, in the registry; Scorecard does not read it.
    4. The packages OCI artifact (cozystack-packages) and the cozy-installer chart are not signed by feat(release): sign the release images and attach their SBOMs #5042: promotion republishes both under a new digest, so they need a signing step in promote-rc.yaml and in finalize, with its own verify.
    5. cozystack-sbom.spdx.json as one SPDX document of the whole release, the source SBOM and the image SBOMs tied by relationships, under the name this issue reserved for it. Cheap once feat(release): sign the release images and attach their SBOMs #5042's archive exists; worth doing only if someone wants one file rather than the archive.
    6. The v* tag rulesets that restrict tag creation to the CI app, and a parse-time guard refusing a promote-rc.yaml dispatch from a branch other than main: until then a hand-pushed v* tag whose tree carries an edited tags.yaml still signs, which docs/release.md says. Both are repository settings and workflow guards, not part of the PRs above.
    7. The Renovate manager for the scanner pin, from Aleksei Sviridkin (@lexfrei)'s review of feat(release): attach an SBOM to every release image #4997, so the syft inside the scanner does not age silently.

    I suggest keeping this issue open as the tracker until the first signed release is out and the CLOMonitor and Scorecard checks have moved, then closing it with that release linked and filing items 3 to 6 separately if they are still wanted. It still carries triage/needs-triage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/ciIssues or PRs related to CI workflows, GitHub Actions, automationarea/releaseIssues or PRs related to release tooling (changelog, backport, release pipeline)kind/featureCategorizes issue or PR as related to a new featuretriage/needs-triageIndicates an issue needs triage by a maintainer

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions