イメージ署名
AWS Signer によるプローバーコンテナイメージの署名と実行前検証を解説します。
STARK 証明は「特定のゲストプログラムが正しく実行された」ことを保証しますが、そもそもそのゲストプログラムを含むコンテナイメージ自体が改ざんされていないことも保証する必要があります。イメージ署名は、信頼されたビルドパイプラインが生成したイメージのみが証明生成に使用されることを担保するセキュリティゲートです。
脅威モデル
イメージ署名が防止する攻撃シナリオを示します。
flowchart TD
subgraph "署名なしの場合"
A1["攻撃者が ECR イメージを<br/>改ざんしたものに置換"] --> B1["改ざんされたプローバーが<br/>不正な集計結果を生成"]
B1 --> C1["不正な結果に対して<br/>有効な STARK 証明が存在"]
end
subgraph "署名ありの場合"
A2["攻撃者が ECR イメージを<br/>改ざんしたものに置換"] --> B2["Step Functions が<br/>署名検証を実行"]
B2 --> C2["署名不一致を検出<br/>タスク起動を拒否"]
end
STARK 証明は Image ID(ゲストバイナリの暗号的識別子)に紐づきますが、イメージ署名はそれとは別のレイヤで「コンテナイメージ全体の完全性」を保証します。両者は相補的な防御を形成します。
| 保証の種類 | メカニズム | 検出対象 |
|---|---|---|
| ゲストプログラムの同一性 | Image ID(RISC Zero) | ゲストバイナリの改変 |
| コンテナイメージの完全性 | AWS Signer | イメージ全体の改変(ホスト含む) |
署名フロー
ビルドと署名
CodeBuild がプローバーコンテナイメージをビルドして ECR に push し、Step Functions 実行時に署名ステータスを検証します。 ECR マネージド署名が有効な環境では、push 後に AWS Signer プロファイルに基づく署名ステータスが付与されます。
sequenceDiagram participant GH as GitHub participant CB as CodeBuild participant ECR as ECR participant SGN as AWS Signer GH->>CB: ソースコード取得 CB->>CB: Docker イメージビルド<br/>(ARM64) CB->>ECR: イメージをプッシュ<br/>(ダイジェスト付き) ECR->>SGN: (ECR managed signing 有効時)署名処理 SGN->>ECR: 署名ステータス更新 Note over ECR: イメージ + 署名が<br/>一体で保存される
注: このリポジトリでコード化されているのは署名ステータス検証(
DescribeImageSigningStatus)です。
署名付与そのもの(ECR managed signing の有効化)は、ECR 側の設定・運用が前提です。
ECR のイメージ URI は常にダイジェスト固定(@sha256:<64-hex>)で参照されます。タグベースの参照は使用しません。これにより、タグの上書きによるイメージのすり替えを防止します。
実行前検証
Step Functions ステートマシンの最初のステートで、check-image-signature Lambda がイメージの署名ステータスを検証します。
stateDiagram-v2
[*] --> VerifyImageSignature: Step Functions 開始
VerifyImageSignature --> CheckSignatureResult
state CheckSignatureResult <<choice>>
CheckSignatureResult --> RunProver: status = COMPLETE
CheckSignatureResult --> SignatureFailed: status ≠ COMPLETE
state SignatureFailed {
[*] --> CallbackFailed: エラー情報を通知
}
state RunProver {
[*] --> ECSTask: 署名検証済みイメージで実行
}
check-image-signature Lambda は以下の処理を行います。
- ECR の
DescribeImageSigningStatusAPI を呼び出す - 指定されたリポジトリ名とイメージダイジェストに対する署名ステータスを取得
- 取得した
status(COMPLETE/ それ以外)を Step Functions に返す
署名ステータスが COMPLETE でない場合、Step Functions の Choice ステートが FinalizeSignatureFailed に遷移し、コールバック Lambda に ImageSignatureVerificationFailed エラーを通知します。ECS タスクは一切起動されません。
ECR リポジトリとイメージ管理
リポジトリ構成
| リポジトリ | 用途 | ライフサイクル |
|---|---|---|
stark-ballot-simulator/zkvm-prover-{env} | プローバーコンテナイメージ | 最新 10 イメージを保持 |
stark-ballot-simulator/risc0-toolchain | RISC Zero ツールチェーンベースイメージ | 最新 5 イメージを保持 |
両リポジトリとも、プッシュ時の脆弱性スキャン(Scan on Push)が有効です。
ダイジェスト固定
Terraform の ecs_image_uri 変数には、ダイジェスト固定の URI のみが許可されます。バリデーションルールにより @sha256:<64-hex> 形式が強制されます。
Step Functions の定義に含まれるイメージダイジェストは、Terraform の変数から以下のように抽出されます。
- リポジトリ名: URI の
@より前の部分からレジストリホストを除去 - ダイジェスト: URI の
@より後の部分(sha256:...)
この分解により、check-image-signature Lambda は正確なリポジトリとダイジェストの組み合わせで署名を検証できます。
ビルドパイプライン
CodeBuild プロジェクト
2 つの CodeBuild プロジェクトがイメージのビルドを担当します。
| プロジェクト | ビルド対象 | タイムアウト | インスタンス |
|---|---|---|---|
stark-ballot-simulator-fargate-prover-{env} | プローバーイメージ | 30 分 | ARM64 Small |
stark-ballot-simulator-risc0-toolchain-builder | ベースイメージ | 120 分 | ARM64 Large |
RISC Zero ツールチェーンのビルドは低頻度(ツールチェーンバージョン更新時のみ)ですが、ビルドに時間を要するため Large インスタンスと長いタイムアウトが設定されています。
CodeBuild の IAM 権限
CodeBuild ロールには以下の権限が付与されています。
| 権限カテゴリ | 対象 API | 目的 |
|---|---|---|
| ECR | GetAuthorizationToken, PutImage 等 | イメージのプッシュ |
| AWS Signer | SignPayload, GetSigningProfile | 署名連携用の権限(運用/拡張時) |
| CloudWatch Logs | CreateLogGroup, PutLogEvents 等 | ビルドログの出力 |
| S3 | GetObject, PutObject | ビルドアーティファクト |
| CodeStar Connections | codestar-connections:UseConnection, GetConnectionToken | 接続方式切り替えに備えた権限(現行 CodeBuild source は GITHUB) |
DS-202 準拠
本システムのイメージ署名アーキテクチャは、デジタル署名に関する DS-202 ガイドラインの以下の原則に準拠しています。
| 原則 | 実装 |
|---|---|
| 署名の完全性 | AWS Signer による暗号学的署名 |
| 署名の検証可能性 | ECR の DescribeImageSigningStatus API による検証 |
| 実行前のゲート | Step Functions の Choice ステートによる強制 |
| 不変な参照 | ダイジェスト固定によるイメージ URI |
| 監査証跡 | CloudTrail + Step Functions 実行ログ |
Image ID との関係
イメージ署名と Image ID は異なるレイヤのセキュリティメカニズムですが、共に「正しいプログラムが実行されたこと」の信頼チェーンを構成します。
flowchart TD
subgraph "ビルド時"
BUILD["コンテナイメージ<br/>ビルド"] --> SIGN["AWS Signer で<br/>イメージ署名"]
BUILD --> IMGID["Image ID 計算<br/>(ゲストバイナリから導出)"]
IMGID --> MAP["imageId-mapping.json<br/>に記録"]
end
subgraph "実行時"
VERIFY_SIG["イメージ署名検証<br/>(Step Functions)"] --> RUN["プローバー実行"]
RUN --> RECEIPT["レシート生成<br/>(Image ID を含む)"]
end
subgraph "検証時"
RECEIPT --> VERIFY_RECEIPT["レシート検証<br/>(verifier-service)"]
MAP --> VERIFY_RECEIPT
VERIFY_RECEIPT --> MATCH{"Image ID<br/>一致?"}
end
SIGN --> VERIFY_SIG
| 検証ポイント | タイミング | 検証主体 | 失敗時の動作 |
|---|---|---|---|
| イメージ署名 | 証明生成前 | Step Functions + Lambda | ECS タスクの起動拒否 |
| Image ID 照合 | 検証時 | verifier-service | 検証失敗の報告 |