Sign release SHA256SUMS with cosign keyless#65
Conversation
SHA256SUMS proves integrity but not authenticity: it lives on the same Release as the binaries, so anyone who can tamper with the release can swap both. Add cosign keyless signing to close that gap. release.yml: - Grant the release job id-token: write (keyless signing via GitHub OIDC; no long-lived key/secret to manage). - Install cosign (sigstore/cosign-installer, SHA-pinned). - Sign SHA256SUMS -> SHA256SUMS.cosign.bundle (self-contained: signature + Fulcio cert + Rekor proof), and verify-blob it in-workflow so a broken signature fails the release. The bundle is uploaded by the existing `release/*` glob. Signing the manifest covers both binaries transitively. README: add an optional "Verifying authenticity (cosign)" step with the verify-blob command (identity = the release workflow, OIDC issuer = GitHub). Validated: actionlint clean, YAML parses. Keyless signing needs the CI OIDC token so it can only be exercised on a real tagged release run — the in-workflow verify-blob is the built-in self-check for that run. Refs smkwlab/.github#69 (E-5 signing follow-up). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JPuooHgsrboSEbfJsNf6iG
| # Fail the release if the bundle we just produced does not verify. | ||
| cosign verify-blob \ | ||
| --bundle SHA256SUMS.cosign.bundle \ | ||
| --certificate-identity-regexp "^https://github.com/${GITHUB_REPOSITORY}/\.github/workflows/release\.yml@refs/tags/" \ |
There was a problem hiding this comment.
ℹ️ [LOW] --certificate-identity-regexp の正規表現で ${GITHUB_REPOSITORY} を直接埋め込んでいますが、リポジトリ名に正規表現のメタ文字(例: . など)が含まれる場合、意図しないマッチになる可能性があります。実際には smkwlab/tdig のように問題ないケースがほとんどですが、末尾に @refs/tags/ の後に anchor($ など)が無いため、tag部分は任意にマッチする点は意図通りか確認しておくとよいでしょう(README側にも同旨の注記あり)。厳密性を高めるなら本ワークフローの検証ではタグを固定する選択肢もあります。
There was a problem hiding this comment.
対応しました。regexp の末尾を @refs/tags/[^/]+$ に anchor し、リリースタグ identity 1件に厳密一致させました(末尾に余分なパスが付く identity を弾く)。README の例も同様に更新。なお ${GITHUB_REPOSITORY} のメタ文字懸念は本リポ名 smkwlab/tdig にメタ文字が無いため実害はありませんが、anchor 追加で過剰マッチの余地も塞いでいます。actionlint は引き続き clean。
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | ||
|
|
||
| - name: Install cosign | ||
| uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3 |
There was a problem hiding this comment.
✨ [POSITIVE] cosign-installer のピン留めが SHA 固定されているのは良い実践です。keyless 署名により長期鍵の管理・ローテーションが不要になり、workflow identity に署名がバインドされる点は運用面・セキュリティ面ともに明確な改善です。
The --certificate-identity-regexp ended at `@refs/tags/` (a prefix match). Anchor the tag portion with `[^/]+$` so it matches exactly one release-tag identity and can't match an identity with extra trailing path segments. Benign for real GitHub OIDC identities, but tighter is correct for a verification regexp. Mirrored in the README example. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JPuooHgsrboSEbfJsNf6iG
| # Fail the release if the bundle we just produced does not verify. | ||
| cosign verify-blob \ | ||
| --bundle SHA256SUMS.cosign.bundle \ | ||
| --certificate-identity-regexp "^https://github.com/${GITHUB_REPOSITORY}/\.github/workflows/release\.yml@refs/tags/[^/]+$" \ |
There was a problem hiding this comment.
ℹ️ [LOW] in-workflowでのverify-blobはビルド時の健全性チェックとしては有効ですが、--certificate-identity-regexpが.github/workflows/release.ymlにハードコードされており、ワークフローのパスやファイル名を変更した際に検証が壊れることに注意が必要です。将来のリネームに備え、コメントで依存関係を明記しておくとメンテナンス性が向上します。
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | ||
|
|
||
| - name: Install cosign | ||
| uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3 |
There was a problem hiding this comment.
ℹ️ [LOW] cosign-installerのバージョンピンがv3(メジャータグ)でコメントされていますが、SHA自体は固定されているため再現性は保たれています。他のアクション(checkout v4.3.1、download-artifact v4)とコメントの粒度を揃えると、レビュー時にどのパッチバージョンにピンされているか把握しやすくなります。
| # Verified OK | ||
| ``` | ||
|
|
||
| Once the manifest is verified, the `sha256sum` check above ties the binaries to |
There was a problem hiding this comment.
✨ [POSITIVE] 検証手順の記載は明快で利用者に優しい内容です。--certificate-identity-regexpのタグ固定の代替案まで示している点は、セキュリティ意識の高い利用者への配慮として優れています。
Release the cosign keyless signing of release binaries (#65). This release also serves as the end-to-end verification of that signing step (the release workflow signs SHA256SUMS and verify-blobs it in-CI). Claude-Session: https://claude.ai/code/session_01JPuooHgsrboSEbfJsNf6iG Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Follow-up to the E-5 checksums (smkwlab/.github#69):
SHA256SUMSproves integrity but not authenticity — it sits on the same Release as the binaries, so an attacker who can tamper with the release can swap both. This adds cosign keyless signing to close that gap.Changes
release.yml(release job):permissions: id-token: write— keyless signing via GitHub OIDC (no long-lived key/secret to manage or rotate).cosign(sigstore/cosign-installer, SHA-pinned).SHA256SUMS→SHA256SUMS.cosign.bundle(self-contained: signature + Fulcio cert + Rekor transparency-log proof), bound to this workflow's identity. Signing the manifest transitively covers both binaries.verify-blobthe bundle in-workflow so a broken signature fails the release. Uploaded by the existingrelease/*glob.README — optional "Verifying authenticity (cosign)" step:
cosign verify-blob \ --bundle SHA256SUMS.cosign.bundle \ --certificate-identity-regexp '^https://github.com/smkwlab/tdig/\.github/workflows/release\.yml@refs/tags/' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ SHA256SUMSVerification stays optional — the binary works without it; only verifiers pay the cost.
Verification
actionlintclean, YAML parses.verify-blobis the built-in self-check for that run. Recommend confirming on the next release (e.g.0.4.1) before relying on it.Refs smkwlab/.github#69 (E-5 signing follow-up).
🤖 Generated with Claude Code