Skip to content

feat(release): sign checksums.txt with cosign keyless signing#903

Draft
mcdgavin wants to merge 1 commit into
livekit:mainfrom
mcdgavin:feat/release-signing
Draft

feat(release): sign checksums.txt with cosign keyless signing#903
mcdgavin wants to merge 1 commit into
livekit:mainfrom
mcdgavin:feat/release-signing

Conversation

@mcdgavin

Copy link
Copy Markdown
Contributor

Follow-up to #880, which verifies archives against checksums.txt — but the checksums file ships alongside the archive in the same release, so it can't detect substituted release assets. This PR signs releases so the checksums are bound to an identity an attacker can't forge.

Approach: keyless (Sigstore) signing of checksums.txt

  • .goreleaser.yaml: a signs section runs cosign sign-blob on checksums.txt during release. Keyless — cosign uses the release workflow's GitHub OIDC identity, so there is no signing key to store or rotate. Each release gains checksums.txt.sig + checksums.txt.pem, and the signature is recorded in the Rekor transparency log. Since every archive is listed in checksums.txt, one signature covers all release artifacts.
  • .github/workflows/release.yaml: adds id-token: write and a sigstore/cosign-installer step (pinned by SHA, matching repo convention).
  • install-cli.sh: opportunistic verification layered on the existing checksum check. When cosign is installed, it verifies the signature with the certificate identity pinned to this repo's release workflow at the exact version tag, aborting on an invalid signature. When cosign is absent, or the release predates signing (no .sig asset), it logs a skip and falls back to checksum-only — nothing breaks for existing releases.

Manual verification for users:

cosign verify-blob \
  --certificate checksums.txt.pem --signature checksums.txt.sig \
  --certificate-identity-regexp '^https://github.com/livekit/livekit-cli/\.github/workflows/release\.yaml@.*$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  checksums.txt

Testing

  • goreleaser check passes on the modified config.
  • The new install-cli.sh block was exercised in isolation across all four paths (no cosign → skip; sig missing → skip; valid sig → continue; invalid sig → abort).
  • Full installer run end-to-end in an Ubuntu container against the real v2.17.0 release, both with and without cosign installed.
  • Not testable from a fork: the signing step itself needs a real tag push with GitHub OIDC in this repo. A maintainer could confirm with a pre-release tag (e.g. v0.0.0-signing-test) before the next real release.

Possible follow-ups (out of scope here)

  • cosign-sign the Docker images in the docker job (same keyless flow on the pushed digest).
  • GitHub artifact attestations (actions/attest-build-provenance) as a cheap complement — users could verify with gh attestation verify.

Sign releases via Sigstore keyless signing: goreleaser invokes cosign
sign-blob on checksums.txt using the release workflow's GitHub OIDC
identity, so there is no signing key to store or rotate. Each release
gains checksums.txt.sig and checksums.txt.pem, and the signature is
recorded in the Rekor transparency log.

install-cli.sh verifies the signature opportunistically: when cosign is
installed it pins the certificate identity to this repo's release
workflow at the exact version tag, aborting on an invalid signature.
Releases that predate signing (no .sig/.pem assets) and systems without
cosign fall back to the existing checksum-only verification.
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