Docker イメージ署名と検証:cosign でサプライチェーンを防衛する運用手順
Docker コンテナがますます普及する一方で、その安全性を確保することが重要な課題となっています。特にサプライチェーン攻撃への対策は、企業のセキュリティ戦略において欠かせない要素です。
本記事では、Sigstore プロジェクトの cosign を使った Docker イメージの署名と検証手順を詳しく解説します。cosign を導入することで、イメージの改ざん検知や信頼性の担保が可能となり、サプライチェーンを防衛できるようになるでしょう。
背景
コンテナイメージのセキュリティリスク
コンテナ環境では、パブリックレジストリから取得したイメージをそのままデプロイすることが一般的です。しかし、イメージの出所や内容が本当に信頼できるかを確認する仕組みがなければ、以下のようなリスクに晒されます。
- イメージが改ざんされている可能性
- 悪意のあるコードが埋め込まれている危険性
- ビルド過程での不正な操作
- 中間者攻撃によるイメージのすり替え
以下の図は、従来のコンテナイメージの配信フローを示しています。
mermaidflowchart LR
dev["開発者"] -->|ビルド| img["Docker イメージ"]
img -->|プッシュ| registry["コンテナレジストリ"]
registry -->|プル| prod["本番環境"]
attacker["攻撃者"] -.->|改ざん?| registry
attacker -.->|中間者攻撃?| prod
このフローでは、レジストリやネットワーク経路で攻撃者がイメージを改ざんする可能性があり、本番環境に不正なコードが混入するリスクが存在します。
Sigstore プロジェクトと cosign の登場
Sigstore は、ソフトウェアのサプライチェーンセキュリティを強化するために Linux Foundation が主導するオープンソースプロジェクトです。その中核ツールである cosign は、コンテナイメージに対してデジタル署名を行い、検証することで、イメージの完全性と真正性を保証します。
cosign の主な特徴は以下の通りです。
| # | 特徴 | 説明 |
|---|---|---|
| 1 | 鍵ペア方式 | 秘密鍵で署名し、公開鍵で検証 |
| 2 | Keyless 署名 | OIDC 認証を利用した鍵管理不要の署名 |
| 3 | OCI レジストリ対応 | 既存のコンテナレジストリと互換性あり |
| 4 | 軽量で高速 | Go 言語で実装され、CI/CD パイプラインに組み込みやすい |
cosign を使うことで、開発者が署名したイメージのみを本番環境にデプロイする運用が実現できます。
課題
イメージの信頼性を担保する難しさ
Docker イメージの信頼性を確保するには、以下の課題をクリアする必要があります。
1. 改ざん検知の仕組み
イメージがレジストリに保存された後、誰かが内容を書き換えたとしても、従来の仕組みでは検知が困難です。SHA256 ハッシュ値でイメージを特定することはできますが、そのハッシュ値自体が正しいかを保証する手段がありません。
2. 署名鍵の管理
デジタル署名を行うには秘密鍵が必要ですが、その鍵を安全に保管・管理することは容易ではありません。鍵が漏洩すれば、攻撃者が偽の署名を作成できてしまいます。
3. CI/CD パイプラインへの組み込み
署名と検証のプロセスを既存の CI/CD ワークフローに統合するには、自動化の仕組みが必要です。手動での運用は現実的ではなく、開発速度を損なわずにセキュリティを強化する必要があるでしょう。
以下の図は、署名と検証が欠けた場合の課題を示しています。
mermaidflowchart TD
build["イメージビルド"] --> push["レジストリへプッシュ"]
push --> question1{"改ざん<br/>されていない?"}
question1 -->|不明| pull["プル"]
pull --> question2{"本当に正規の<br/>イメージ?"}
question2 -->|不明| deploy["デプロイ"]
deploy --> risk["セキュリティリスク"]
このように、各段階で信頼性を確認する手段がないため、リスクを抱えたままデプロイせざるを得ない状況となります。
サプライチェーン攻撃の脅威
近年、ソフトウェアサプライチェーンを標的とした攻撃が増加しています。2021 年の SolarWinds 事件や、2022 年の Log4Shell 脆弱性など、信頼されているコンポーネントが侵害されることで、広範囲に被害が拡大する事例が後を絶ちません。
コンテナイメージも例外ではなく、以下のような攻撃経路が考えられます。
- ビルド環境への侵入によるマルウェア混入
- レジストリアカウントの乗っ取り
- ベースイメージへのバックドア埋め込み
これらの脅威に対抗するには、イメージの出所を明確にし、改ざんを検知できる仕組みが不可欠です。
解決策
cosign による署名と検証の基本フロー
cosign を導入することで、Docker イメージに対して暗号学的な署名を付与し、その署名を検証することで信頼性を保証できます。基本的な運用フローは以下の通りです。
以下の図は、cosign を使った署名・検証フローを示しています。
mermaidflowchart LR
dev["開発者"] -->|ビルド| img["Docker イメージ"]
img -->|署名| signed["署名付きイメージ"]
signed -->|プッシュ| registry["レジストリ"]
registry -->|プル| verifier["検証プロセス"]
verifier -->|公開鍵で検証| result{検証結果}
result -->|成功| deploy["デプロイ"]
result -->|失敗| reject["拒否"]
この仕組みにより、署名されていないイメージや改ざんされたイメージは検証段階で弾かれ、安全性が確保されます。
cosign の 2 つの署名方式
cosign には、鍵ペアを使った署名と Keyless 署名の 2 つの方式があります。
鍵ペア方式
秘密鍵と公開鍵のペアを生成し、秘密鍵で署名を行い、公開鍵で検証します。鍵の管理は必要ですが、オンプレミス環境や厳格なセキュリティ要件がある場合に適しています。
Keyless 署名
OIDC(OpenID Connect)認証を利用し、鍵の管理を不要にする方式です。GitHub Actions や GitLab CI などの CI/CD 環境で、開発者の ID を証明として署名を行います。鍵の保管リスクを回避でき、運用が簡素化されるでしょう。
| # | 方式 | メリット | デメリット |
|---|---|---|---|
| 1 | 鍵ペア方式 | 完全な制御が可能 | 鍵の管理が必要 |
| 2 | Keyless 署名 | 鍵管理不要、運用が簡単 | OIDC プロバイダーへの依存 |
本記事では、まず鍵ペア方式での基本的な運用手順を解説し、その後 Keyless 署名の方法も紹介します。
署名と検証がもたらす安全性
cosign による署名と検証を導入することで、以下のセキュリティ効果が得られます。
- 完全性の保証: イメージが改ざんされていないことを確認
- 真正性の保証: イメージが正規の開発者によって作成されたことを証明
- 監査証跡: 誰がいつ署名したかを記録し、追跡可能に
- ポリシー適用: 署名されていないイメージのデプロイを自動的に拒否
これらの効果により、サプライチェーン攻撃のリスクを大幅に低減できます。
具体例
環境準備
cosign を使った署名と検証を行うための環境を準備します。以下の手順では、ローカル環境での検証を想定していますが、CI/CD パイプラインにも同様に適用できます。
cosign のインストール
cosign は Go 言語で実装されており、各プラットフォーム向けのバイナリが提供されています。
macOS の場合、Homebrew を使ってインストールできます。
bashbrew install cosign
Linux の場合は、以下のコマンドでバイナリを直接ダウンロードします。
bashcurl -Lo cosign https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign
sudo mv cosign /usr/local/bin/
インストール後、バージョンを確認しましょう。
bashcosign version
正常にインストールされていれば、バージョン情報が表示されます。
Docker のインストール確認
Docker がインストールされていることを確認します。
bashdocker --version
まだインストールしていない場合は、Docker 公式サイトから最新版を入手してください。
鍵ペアの生成
cosign で署名を行うには、まず秘密鍵と公開鍵のペアを生成する必要があります。
以下のコマンドで鍵ペアを生成します。
bashcosign generate-key-pair
実行すると、パスワードの入力を求められます。このパスワードは秘密鍵を保護するために使用されるため、安全なものを設定してください。
textEnter password for private key:
Enter password for private key again:
生成が完了すると、以下の 2 つのファイルが作成されます。
| # | ファイル名 | 説明 |
|---|---|---|
| 1 | cosign.key | 秘密鍵(署名に使用) |
| 2 | cosign.pub | 公開鍵(検証に使用) |
重要: cosign.key` は絶対に外部に漏らさないように管理してください。Git リポジトリにコミットしたり、公開レジストリにアプロードしたりしないよう注意が必要です。
秘密鍵を安全に保管するために、以下のような方法を検討しましょう。
- 暗号化されたストレージに保存
- KMS(Key Management Service)を利用
- CI/CD の Secrets 機能で管理
Docker イメージのビルドとプッシュ
署名対象となる Docker イメージを準備します。ここでは簡単なサンプルアプリケーションを使います。
サンプル Dockerfile の作成
以下の内容で Dockerfile を作成します。
dockerfileFROM node:18-alpine
WORKDIR /app
# アプリケーションの依存関係をコピー
COPY package*.json ./
# 依存パッケージをインストール
RUN npm ci --only=production
# アプリケーションコードをコピー
COPY . .
# アプリケーションを起動
CMD ["node", "index.js"]
このシンプルな Dockerfile は、Node.js アプリケーションをコンテナ化します。
イメージのビルド
イメージをビルドします。ここでは myapp:v1.0.0 というタグを付けます。
bashdocker build -t myapp:v1.0.0 .
ビルドが完了したら、イメージが作成されたことを確認しましょう。
bashdocker images | grep myapp
レジストリへのプッシュ
署名を付与する前に、イメージをコンテナレジストリにプッシュします。ここでは Docker Hub を例としますが、AWS ECR や Google Container Registry など他のレジストリでも同様に動作します。
まず、Docker Hub にログインします。
bashdocker login
次に、イメージにレジストリのタグを付けます。yourusername は自分の Docker Hub ユーザー名に置き換えてください。
bashdocker tag myapp:v1.0.0 yourusername/myapp:v1.0.0
レジストリにプッシュします。
bashdocker push yourusername/myapp:v1.0.0
これでレジストリにイメージが保存されました。次のステップで、このイメージに署名を付与します。
イメージへの署名
cosign を使ってイメージに署名を付与します。
以下のコマンドで署名を実行します。
bashcosign sign --key cosign.key yourusername/myapp:v1.0.0
実行すると、秘密鍵のパスワード入力を求められます。
textEnter password for private key:
パスワードを入力すると、署名プロセスが開始されます。
textPushing signature to: yourusername/myapp
署名が完了すると、署名データがレジストリに保存されます。cosign は署名情報をイメージと同じレジストリに保存するため、別途署名ファイルを管理する必要はありません。
署名の確認
署名が正しく付与されたかを確認するには、以下のコマンドを実行します。
bashcosign tree yourusername/myapp:v1.0.0
以下のような出力が表示されれば、署名が正常に保存されています。
text📦 yourusername/myapp:v1.0.0
└── 🔐 Signatures
└── sha256:abc123...
この出力から、イメージに署名が付与されていることが確認できます。
署名の検証
署名されたイメージの検証を行います。検証には公開鍵を使用します。
以下のコマンドで検証を実行します。
bashcosign verify --key cosign.pub yourusername/myapp:v1.0.0
署名が有効であれば、以下のような JSON 形式の出力が表示されます。
json[
{
"critical": {
"identity": {
"docker-reference": "index.docker.io/yourusername/myapp"
},
"image": {
"docker-manifest-digest": "sha256:abc123..."
},
"type": "cosign container image signature"
},
"optional": null
}
]
この出力には、署名されたイメージの詳細情報が含まれています。検証が成功したことで、イメージが改ざんされておらず、正規の秘密鍵で署名されたことが証明されました。
もし検証に失敗した場合は、以下のようなエラーが表示されます。
textError: no matching signatures
このエラーが出た場合、イメージが署名されていないか、署名が改ざんされている可能性があります。
CI/CD パイプラインへの組み込み
実際の開発現場では、署名と検証を CI/CD パイプラインに組み込むことで、自動化された安全なデプロイフローを構築できます。
GitHub Actions での署名例
GitHub Actions を使った自動署名の例を示します。
以下の内容で .github/workflows/build-and-sign.yml を作成します。
yamlname: Build and Sign Docker Image
on:
push:
branches:
- main
jobs:
build-and-sign:
runs-on: ubuntu-latest
steps:
# リポジトリをチェックアウト
- name: Checkout code
uses: actions/checkout@v3
次に、Docker のセットアップを行います。
yaml# Docker のセットアップ
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# Docker Hub にログイン
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
イメージをビルドしてプッシュします。
yaml# イメージのビルドとプッシュ
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: yourusername/myapp:${{ github.sha }}
cosign をインストールして署名を実行します。
yaml# cosign のインストール
- name: Install cosign
uses: sigstore/cosign-installer@v3
# イメージに署名
- name: Sign Docker image
env:
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
run: |
echo "${{ secrets.COSIGN_KEY }}" > cosign.key
cosign sign --key cosign.key yourusername/myapp:${{ github.sha }}
rm cosign.key
この設定により、main ブランチにプッシュされるたびに、イメージが自動的にビルドされ、署名が付与されます。
重要: COSIGN_KEYとCOSIGN_PASSWORD` は、GitHub の Secrets 機能で安全に管理してください
Kubernetes での検証例
Kubernetes 環境では、署名されていないイメージのデプロイを防ぐために、Admission Controller を使った検証が有効です。
Sigstore Policy Controller を使うことで、署名検証を自動化できます。
Policy Controller をインストールするには、以下のコマンドを実行します。
bashkubectl apply -f https://github.com/sigstore/policy-controller/releases/latest/download/policy-controller.yaml
次に、検証ポリシーを定義します。以下の内容で policy.yaml を作成します。
yamlapiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-signed-images
spec:
images:
- glob: 'yourusername/*'
authorities:
- key:
data: |
-----BEGIN PUBLIC KEY-----
# ここに cosign.pub の内容を貼り付け
-----END PUBLIC KEY-----
このポリシーを適用します。
bashkubectl apply -f policy.yaml
これにより、yourusername 配下のイメージは署名検証が必須となり、検証に失敗したイメージはデプロイが拒否されます。
Keyless 署名の利用
鍵管理の負担を軽減するために、Keyless 署名を利用することもできます。Keyless 署名では、OIDC 認証を利用して開発者の ID を証明します。
Keyless 署名の実行
GitHub Actions で Keyless 署名を行う例を示します。
yaml# Keyless 署名
- name: Sign Docker image (Keyless)
run: |
cosign sign yourusername/myapp:${{ github.sha }}
この方法では、秘密鍵を管理する必要がなく、GitHub の OIDC トークンを使って署名が行われます。
Keyless 署名の検証
Keyless 署名の検証には、証明書情報を指定します。
bashcosign verify \
--certificate-identity=https://github.com/yourorg/yourrepo/.github/workflows/build.yml@refs/heads/main \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
yourusername/myapp:v1.0.0
この検証により、特定のワークフローから署名されたイメージのみを信頼することができます。
署名付きイメージの運用フロー全体像
以下の図は、cosign を使った署名と検証の運用フロー全体を示しています。
mermaidsequenceDiagram
participant Dev as 開発者
participant CI as CI/CD
participant Reg as レジストリ
participant K8s as Kubernetes
participant PC as Policy Controller
Dev->>CI: コードをプッシュ
CI->>CI: イメージをビルド
CI->>Reg: イメージをプッシュ
CI->>CI: cosign で署名
CI->>Reg: 署名を保存
K8s->>Reg: イメージをプル
K8s->>PC: 署名検証を要求
PC->>Reg: 署名を取得
PC->>PC: 公開鍵で検証
alt 検証成功
PC->>K8s: デプロイ許可
K8s->>K8s: Pod を起動
else 検証失敗
PC->>K8s: デプロイ拒否
K8s->>K8s: エラーを記録
end
このフローにより、署名されていないイメージや改ざんされたイメージは自動的に弾かれ、安全性が担保されます。
トラブルシューティング
cosign の運用中に発生しうるエラーと解決方法を紹介します。
エラー 1: Error: signing [yourusername/myapp .0.0]: getting signer: reading key: no such file or directory
発生条件: 秘密鍵ファイルが見つからない場合に発生します。
解決方法:
- 秘密鍵ファイル
cosign.keyが正しいパスに存在するか確認 --keyオプションで正しいパスを指定
bashcosign sign --key /path/to/cosign.key yourusername/myapp:v1.0.0
エラー 2: Error: verifying [yourusername/myapp .0.0]: no matching signatures
エラーコード: 検証エラー
発生条件: イメージに署名が付与されていないか、公開鍵が間違っている場合に発生します。
解決方法:
- イメージに署名が付与されているか確認
bashcosign tree yourusername/myapp:v1.0.0
- 正しい公開鍵を使用しているか確認
- 署名を再度実行してみる
エラー 3: Error: failed to verify signature: crypto/rsa: verification error
エラーコード: 署名検証エラー
発生条件: 署名と公開鍵が一致しない場合に発生します。
解決方法:
- 署名時と検証時で同じ鍵ペアを使用しているか確認
- イメージが改ざんされていないか確認
- 鍵ペアを再生成して署名をやり直す
まとめ
Docker イメージの署名と検証は、サプライチェーンセキュリティにおいて極めて重要な対策です。cosign を導入することで、イメージの完全性と真正性を保証し、改ざんや不正なコードの混入を防ぐことができます。
本記事では、cosign の基本的な使い方から、CI/CD パイプラインへの組み込み、Kubernetes での検証まで、実践的な運用手順を解説しました。鍵ペア方式と Keyless 署名の両方を理解することで、環境や要件に応じた最適な運用が可能となるでしょう。
署名と検証の仕組みを導入することで、以下のメリットが得られます。
- イメージの改ざん検知
- 正規の開発者によるイメージであることの証明
- サプライチェーン攻撃への対策強化
- 監査証跡の確保
セキュリティは一度導入して終わりではなく、継続的に運用・改善していく必要があります。cosign を活用して、安全なコンテナ環境を構築していきましょう。
関連リンク
articleDocker イメージ署名と検証:cosign でサプライチェーンを防衛する運用手順
articleDocker で DNS 解決に失敗する時の原因と対処:overlay2・systemd-resolved・WSL2 対応
articleDocker を用いた統一ローカル環境:新人オンボーディングを 1 日 → 1 時間へ
articleDocker で Dev Container を構築:VS Code/Codespaces で即戦力環境を配布
articleDocker マルチステージビルド設計大全:テスト分離・依存最小化・キャッシュ戦略
articleDocker コマンド早見表:build/run/exec/logs/prune を 1 枚で網羅
articleJotai でフォームを分割統治:フィールド粒度の atom 設計と検証戦略
articleElectron アーキテクチャ超図解:Main/Renderer/Preload の役割とデータフロー
articleJest の ESM/NodeNext 設定完全ガイド:transformIgnorePatterns と resolver 設計
articleDocker イメージ署名と検証:cosign でサプライチェーンを防衛する運用手順
articleGitHub Copilot Enterprise 初期構築:SSO/SCIM・ポリシー・配布ロールアウト設計
articleDevin が強い開発フェーズはどこ?要件定義~運用までの適合マップ
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来