Skip to content

Check weekly that release runs, files, Rekor entries and images match - #159

Merged
Gorka Gonzalo (d0ubl3g) merged 2 commits into
devfrom
ci/release-integrity
Oct 12, 2026
Merged

Gorka Gonzalo (d0ubl3g) merged 2 commits into
devfrom
ci/release-integrity

Conversation

@d0ubl3g

Copy link
Copy Markdown
Contributor

Refs #152, which closes when this reaches main. It implements the criteria as changed on 2026-10-12. Monitoring the log for every entry made with the identity is #158.

What it checks

.github/scripts/release-integrity.py uses only the Python standard library and reads only public data and the run history. It checks, in both directions:

  • Runs ↔ releases.
    • Every tag run of release.yml, and every run of container-image.yml, must be for a published release at its tag's commit. Manual release.yml runs are dry runs and are skipped.
    • Every published release must have a successful release.yml run at that commit, and from 0.4.0 a successful image run.
  • Files. Every asset must match its SHA256SUMS line. Besides the listed files, only bundles and provenance may be attached.
  • Rekor, by hash. Every file with a .sigstore.json bundle is looked up in Rekor by its SHA-256, and the Fulcio certificate is parsed with a small DER reader.
    • At least one entry must name release.yml@refs/tags/<tag>, the GitHub Actions issuer, the tag's commit (OID …1.3) and a successful run of that tag (OID …1.21).
    • An entry with this repository's identity that no published release made is a mismatch.
    • A file that two releases ship unchanged, such as install.sh in 0.3.0 and 0.3.1, is signed by both, and that is accepted.
  • Images. Every version tag of the image must be a published release, and every release from 0.4.0 must have one. cosign verify runs on its digest against container-image.yml@refs/tags/<tag> with trigger release.

Anything that cannot be read fails, never passes. ACCEPTED_RUNS holds known exceptions with reasons, and is empty.

The workflow

.github/workflows/release-integrity.yml runs weekly, on demand, and on pull requests that change it.

  • Permissions: contents: read, actions: read and issues: write. Issues are not touched on a pull request.
  • cosign: v3.1.3 through the pinned cosign-installer.
  • Steps: it runs the unit tests, then the check. A failure opens or comments on an issue labelled release-integrity, and a pass closes it. It also reports a gap since the previous run, as repo-freshness does.

Docs

Checks run

  • release-integrity-test.py: 12 tests pass. The certificate fixture is the public Rekor entry of 0.4.1's SHA256SUMS.
  • The live check against this repository passes:
    • 7 published releases, 16 release.yml runs and 2 image runs;
    • every signed file of every release, 0.1.0 to 0.4.1, is tied to its tag, commit and run in Rekor;
    • the 0.4.0 and 0.4.1 images verify;
    • result: "Everything matches."
  • It takes about 5 minutes, mostly the Rekor lookups.
  • actionlint 1.7.12 and zizmor 1.30.1 (pedantic) report no findings.

A new weekly workflow, release-integrity.yml, compares the runs of
release.yml and container-image.yml with the published releases in both
directions, checks every release file against SHA256SUMS, looks up every
signed file in Rekor by its SHA-256 and checks that its certificate names
release.yml at the release's tag, commit and run, and verifies each
container image signature with cosign. A mismatch, or anything it cannot
read, fails the run and opens or updates an issue labelled
release-integrity; a pass closes it.

docs/release-signing.md describes the check, what it cannot see and what
to do when it alerts, and puts the Sigstore and SLSA item back in the
list of who may change the signing identities. CONTRIBUTING and the risk
assessment list the check.

Refs #152

Signed-off-by: d0ubl3g <48954024+d0ubl3g@users.noreply.github.com>
Rekor accepts any certificate in a hashedrekord entry, so an entry with a
certificate that claims the release identity could make a file look
signed, or raise a false mismatch. The integrity check now counts an
entry only if Rekor's signed entry timestamp verifies with Rekor's key
and its certificate chains to Sigstore's certificate authority at the
time it was logged, both from the trust root that cosign initialize
fetches through TUF. Tests use a public fixture of that trust root and
of the v0.4.1 entry.

Refs #152

Signed-off-by: d0ubl3g <48954024+d0ubl3g@users.noreply.github.com>
@d0ubl3g
Gorka Gonzalo (d0ubl3g) merged commit 066fc66 into dev Oct 12, 2026
22 checks passed
@d0ubl3g
Gorka Gonzalo (d0ubl3g) deleted the ci/release-integrity branch October 12, 2026 17:26
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