成果物署名と検証(Sigstore/keyless)
鍵の管理なしでコンテナイメージの正体と来歴を証明できます。OIDCに紐づく短命証明書、透明性ログ、デプロイ時の検証ゲートまで、keyless署名の仕組みを原理から掴めます。
- 1.keyless署名は長期秘密鍵を持たない。OIDCで本人性を証明し、Fulcioが数分で失効する短命証明書を発行、その鍵で署名後すぐ鍵を捨てる。漏洩する長期鍵が存在しない。
- 2.Rekorは署名を追記専用の透明性ログへ記録する。証明書が失効した後でも『その時刻に正規発行された鍵で署名された』ことを包含証明で後から検証でき、改竄は署名済みツリーヘッドで検出される。
- 3.admission検証ゲートはデプロイ前にイメージのダイジェスト・署名・来歴を照合し、不一致なら入場を拒否する。タグでなくダイジェストで固定することが完全性保証の前提。
何を保証したいのか:完全性と来歴
成果物署名が答えるのは二つの問いです。完全性(integrity)——「このイメージはビルド以降ビット単位で改竄されていないか」。そして来歴(provenance)/真正性(authenticity)——「これは本当に正規のパイプラインが作ったものか、攻撃者が差し替えた偽物ではないか」。レジストリへの不正pushや中間者によるすり替えを、人手のレビューではなく暗号的検証で機械的に弾くのが目的です。
ここで前提となるのがコンテンツアドレス指定です。コンテナイメージは sha256:abcd... というダイジェスト(マニフェストのハッシュ)で一意に識別されます。タグ(v1.2.3 や latest)は可変ポインタにすぎず、後から別のダイジェストへ付け替えられます。署名はタグではなくダイジェストに対して行う——これが完全性保証の出発点です。ダイジェストが一致すれば中身は同一、署名がそのダイジェストに紐づいていれば「誰が」承認したかも辿れます(/devops/sbom-slsa-provenance/ の来歴と地続きの話)。
なぜkeylessなのか:長期鍵という根本問題
従来の署名は、署名者が長期秘密鍵を持ち、それで署名し、検証者が対応する公開鍵を信頼します。原理は正しいのですが、運用が破綻しがちです。鍵をCIに置けば漏洩面になり、HSMに入れれば取り回しが重く、誰の鍵か・失効したかの管理(鍵配布と失効)が膨れ上がります。鍵が漏れた瞬間、攻撃者は正規署名を無制限に偽造できるのが最大の弱点です。
Sigstoreのkeyless署名はこの問題を「長期鍵をそもそも持たない」ことで解きます。流れはこうです。
keyless署名の流れ:
1. 署名者がOIDCで本人性を証明(CI のワークロードIDなど)
→ IDトークン(発行者・subject・ワークフロー情報を含む)を取得
2. その場で使い捨ての鍵ペアを生成
3. Fulcio(CA)へ IDトークン+公開鍵 を提出
→ Fulcio が本人性を検証し、subject を埋め込んだ
短命証明書(有効期間 約10分)を発行
4. 秘密鍵で成果物ダイジェストへ署名
5. 署名・証明書・公開鍵を Rekor(透明性ログ)へ記録
6. 秘密鍵を破棄(メモリから捨てる)
肝は、秘密鍵が「署名する数秒〜数分」しか存在しないことです。署名後すぐ捨てるので、保管・ローテーション・失効という長期鍵運用が丸ごと消えます。漏洩しうる持続的な秘密が存在しない、というのがkeylessの核心です。
keylessでは信頼の根が「鍵を持っている事実」から「OIDCで証明されたアイデンティティ」へ移ります。検証で確かめるのは公開鍵の指紋ではなく、証明書に埋め込まれた発行者(例 GitHub Actions のOIDC発行者)と subject(例 特定リポジトリの特定ワークフロー)です。つまり「repo:org/app の release.yml が署名したものだけ信頼する」とポリシーを書けます。ワークロードのアイデンティティ確立は /devops/workload-identity-mtls/ のmTLSと同じ発想で、人間でなくパイプラインを主体に据える点が現代的です。
Fulcio:短命証明書を発行する認証局
FulcioはSigstoreのCA(認証局)で、OIDCのIDトークンと引き換えにX.509証明書を発行します。普通のCAと違うのは二点。第一に、証明書の有効期間が約10分と極端に短い。第二に、証明書の subject にOIDCで検証したアイデンティティを焼き込む(GitHub Actions なら発行者URL・ワークフローパス・コミットSHAなどが拡張フィールドに入る)。
ここで当然の疑問が湧きます。証明書が10分で切れるなら、後で検証する時にはとっくに失効しているのでは? その通りで、検証時点では証明書は期限切れです。これを成立させるのが次の透明性ログです。短命証明書は「署名したその瞬間、確かに正規のアイデンティティが正規CAから発行を受けた」ことだけを示し、その事実を永続的に固定する役割をRekorが担う——この分業がkeylessの設計を支えています。
Rekor:透明性ログと包含証明
Rekorは署名イベントを記録する追記専用(append-only)の透明性ログです。署名・証明書・成果物ダイジェストのエントリを時系列に積み、全体をMerkleツリーで束ねます。これにより二つの強い性質が得られます。
| 性質 | 意味 | 実現する仕組み |
|---|---|---|
| 包含証明 | あるエントリがログに確かに含まれる証拠 | ルートまでのMerkle経路(兄弟ハッシュ列) |
| 一貫性証明 | 過去のログが書き換え・削除されていない証拠 | 旧ツリーヘッドが新ツリーヘッドの部分木である証明 |
| タイムスタンプ | 署名がいつ記録されたかの信頼できる時刻 | ログが署名したエントリ時刻 |
| 失効後検証 | 証明書失効後も署名の正当性を判定できる | 記録時刻が証明書有効期間内かを照合 |
検証者の論理はこうです。記録時刻 が 証明書の有効期間内 であり、かつそのエントリがログに包含されている(Merkle経路がルートハッシュに繋がる)なら、「正規アイデンティティが、証明書が生きていた時刻に、このダイジェストへ署名した」と結論できます。証明書が今は失効していても問題ありません。時刻という証拠が透明性ログに固定されているからです。
透明性ログは「Rekorが嘘をつかない」ことに依存しません。ログは現在のルートを署名済みツリーヘッド(STH)として公開し、誰でも一貫性証明で「過去のエントリが消されたり差し替えられたりしていない」ことを検証できます。もしRekorが過去を改竄すれば、以前のSTHと一貫性が破れて機械的に検出されます。さらに第三者のモニタが定常的にログを監視し、自分のアイデンティティを騙る署名が記録されていないかを見張れます(不正発行の事後検知)。透明性ログの安全性は「秘密の信頼」ではなく「公開された改竄検出可能性」に立脚します。
検証時に確認する連鎖:
成果物ダイジェスト
└ 署名(短命鍵による) ── 公開鍵
└ Fulcio証明書(subject=OIDCアイデンティティ, 有効期間)
└ Fulcioルート(信頼アンカー)
Rekorエントリ(記録時刻)
└ 包含証明(Merkle経路)→ 署名済みツリーヘッド
ポリシー: subject が許可した発行者/ワークフローに一致するか
デプロイ時の検証ゲート:admissionで弾く
署名を作っても、検証して不正を止める関所がなければ意味がありません。Kubernetesではこれをadmission検証ゲート——具体的には検証用admission webhook(Sigstore policy-controller や Kyverno、Connaisseur 等)——として実装します。Podが作られる前、APIサーバーがオブジェクトをetcdへ保存する直前に割り込み、イメージの署名と来歴を照合して**不合格なら入場を拒否(deny)**します。
admissionの実行順序と失敗時の挙動は、検証ゲートの安全性そのものを左右します(詳細は /devops/admission-control-policy/)。
検証ゲートの典型的な穴はTOCTOU(検証時と使用時の食い違い)です。マニフェストが app:v1.2.3 というタグを指していると、webhookが v1.2.3 を解決して署名検証した後、kubelet がイメージをpullする瞬間にタグが別ダイジェストへ付け替えられていれば、検証した物と実行する物が別物になります。対策は、admissionの段階でタグを sha256:... ダイジェストへ解決してその場でマニフェストを書き換え固定し、以降は不変のダイジェストでpullさせること。署名検証は必ずダイジェストに対して行い、実行も同じダイジェストで行う——この一致がなければ完全性保証は成立しません。
検証ゲートが落とすべき典型は、(1) 署名が一切ない無名イメージ、(2) 署名はあるが subject が許可ポリシーに合わない(許可外のリポジトリやワークフローが署名)、(3) 署名はあるが対応するRekorエントリが見つからない、(4) 来歴(SLSA provenance)が要求レベルを満たさない、です。CIで署名を付け(/devops/ci-cd/)、本番入口で検証する、という生成と検証の分離がサプライチェーン防御の基本構図になります。
境界条件と運用上の勘所
第一に、署名は「誰が承認したか」を示すが「中身が安全か」は示しません。正規パイプラインが脆弱性入りイメージを署名すれば、署名は有効でも危険です。署名は真正性、脆弱性スキャンや来歴レベル評価は別レイヤ——両者は補完関係です。
第二に、可用性の依存が増えます。検証ゲートが外部のRekor/Fulcioや署名情報の取得に依存すると、それらが落ちた時にデプロイ全体が詰まりえます。失敗時ポリシー(fail-closed で安全側に倒すか、fail-open で可用性を優先するか)、検証結果のキャッシュ、ログのミラーといった設計判断が要ります。fail-closed は安全だが署名基盤の障害が即デプロイ停止に直結する、というトレードオフを意識します。
第三に、信頼アンカーの管理です。検証者はFulcioのルートとRekorの公開鍵(およびそれらを束ねる信頼ルート)を正しく持っていなければなりません。ここを差し替えられると検証全体が無意味になるため、信頼ルートの配布と更新が最終的な安全性を握ります。
keyless署名の核は「長期秘密鍵を持たず、OIDCアイデンティティで本人性を証明し、Fulcioが約10分の短命証明書を発行、署名後に鍵を破棄する」こと。短命証明書が失効後も検証できる理由は、Rekor透明性ログが記録時刻を固定し『証明書有効期間内に署名された』ことを包含証明で示すから、と即答できること。Rekorの改竄が機械的に検出される根拠は署名済みツリーヘッドと一貫性証明。検証で照合するのは公開鍵の指紋ではなく証明書 subject のOIDC発行者・ワークフロー。そしてデプロイ時のadmission検証ゲートでは、タグでなくダイジェストへ固定して TOCTOU を塞ぐ点、署名は真正性を示すが中身の安全性は別レイヤである点を押さえること。
まとめ
- 成果物署名は完全性(改竄なし)と来歴・真正性(正規パイプライン製)を暗号的に保証する。署名は可変のタグでなく不変のダイジェストに対して行うのが大前提。
- keyless署名は長期秘密鍵を持たない。OIDCで本人性を証明し、Fulcioが約10分の短命証明書を発行、署名後に鍵を破棄するため、漏洩する持続的秘密が存在しない。信頼の根は鍵でなくアイデンティティへ移る。
- Rekor透明性ログが署名と記録時刻を追記専用で固定し、証明書失効後も「有効期間内に署名された」ことを包含証明で検証可能にする。改竄は署名済みツリーヘッドと一貫性証明で機械的に検出される。
- admission検証ゲートがデプロイ直前に署名・subject・来歴を照合し不合格を拒否する。タグをダイジェストへ固定して TOCTOU を塞ぐことが完全性保証の条件。
- 署名は真正性を示すが中身の安全性は別レイヤ(脆弱性スキャン・SLSAレベル評価と補完)。検証ゲートは署名基盤への可用性依存と信頼アンカー管理というトレードオフを伴う。
DevOps/インフラ Article
成果物署名と検証(Sigstore/keyless)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
DevOps
比較で見る軸
難易度: advanced / カテゴリ: DevOps/インフラ / タグ数: 6
導入後に効く点
Rekorは署名を追記専用の透明性ログへ記録する。証明書が失効した後でも『その時刻に正規発行された鍵で署名された』ことを包含証明で後から検証でき、改竄は署名済みツリーヘッドで検出される。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- DevOps/インフラ
- タグ数
- 6
判断チェックリスト
- 自社の用途が「DevOps / サプライチェーンセキュリティ」に近いか確認する。
- 強みである「keyless署名は長期秘密鍵を持たない。OIDCで本人性を証明し、Fulcioが数分で失効する短命証明書を発行、その鍵で署名後すぐ鍵を捨てる。漏洩する長期鍵が存在しない。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。