【実務・中級編】 コンテナイメージの脆弱性スキャンと署名検証 (Cosign) – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

コンテナの「中身」を誰が保証するのか?――サプライチェーン攻撃を無効化するデジタル署名の流儀

現場のエンジニア諸君、お疲れ様。今日もどこかでコンテナがデプロイされていることだろう。だが、君たちが docker pull しているそのイメージ、本当に「君たちがビルドしたもの」と同じものだろうか?

昨今、攻撃者はアプリの脆弱性そのものよりも、そのアプリが構築される「パイプライン」を狙う。悪意ある第三者がレジストリ上のイメージをすり替えたり、中間者攻撃で不正なバイナリを混入させたりすれば、いくらコードをセキュアに書いても一巻の終わりだ。

今日は、コンテナセキュリティの防波堤となる「脆弱性スキャン」と「署名検証(Cosign)」について、泥臭い実務の視点から解説する。

—

1. 脆弱性スキャン(Trivy)は「入り口」に過ぎない

まず前提として、Trivy 等を用いたスキャンは必須だ。だが、多くの現場で誤解されているのは「スキャンをパスすれば安全」という幻想だ。

スキャンはあくまで「既知の脆弱性(CVE)」に対する検知に過ぎない。未知のゼロデイ攻撃や、悪意を持って混入されたバックドアはスキャナーでは見抜けない。ここで重要になるのが「このイメージは、信頼できるビルドプロセスを経て生成されたものである」という証明(署名)だ。

—

2. Sigstore/Cosignによる署名:誰が保証するか

Cosign は、公開鍵暗号(ECC: 楕円曲線暗号)を利用してコンテナイメージにデジタル署名を行うツールだ。RSAよりも計算コストが低く、かつ同等の強度を持つECCの採用は、高負荷なCI/CD環境において非常に理にかなっている。

なぜ署名が必要なのか(攻撃シナリオ)

攻撃者がレジストリに侵入し、正規のイメージに cryptominer(仮想通貨マイナー)を仕込んだマルウェアを紛れ込ませたとしよう。署名がない場合、Kubernetesクラスターは「最新のタグがついているから」という理由で、その汚染されたイメージをそのまま実行してしまう。

Cosignで署名検証を強制すれば、「署名がない」または「署名が改ざんされている」イメージは、実行時に即座に拒否される。 これがサプライチェーン攻撃を物理的に遮断するロジックだ。

—

3. 実践:Cosignによる署名と検証のワークフロー

まずは、GitHub Actions等でイメージをビルドした直後に署名を行う手順を見ていこう。

署名(CI側:GitHub Actionsの例)

秘密鍵をGitHubのシークレットに保存し、ビルド後のイメージに署名する。

# .github/workflows/build.yml の一部
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Build and Push
        run: |
          docker build -t my-repo/my-app:latest .
          docker push my-repo/my-app:latest
      
      # Cosignによる署名(秘密鍵を使用)
      - name: Sign the image
        env:
          COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
          COSIGN_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
        run: |
          cosign sign --key env://COSIGN_KEY my-repo/my-app:latest

検証(Kubernetes側:Admission Controller)

次に、クラスタ側で「署名がないものは動かさない」設定だ。Kyverno や Policy Controller を使うのが定石だが、ここではシンプルに Cosign コマンドで検証する仕組みを理解してほしい。

# 公開鍵を使ってイメージの正当性を検証するコマンド
# 署名が一致しなければ、ここで exit code 1 が返る
cosign verify --key cosign.pub my-repo/my-app:latest

—

4. 現場の盲点:鍵管理と信頼の連鎖

ここで一つ、実務的な警告をしておく。「署名鍵の管理」こそが最大の弱点だ。

もし秘密鍵が流出したら、攻撃者は正規の署名付きマルウェアを作成できてしまう。

  • 鍵のローテーション: 鍵は定期的に入れ替えろ。
  • Keyless署名: 最近のトレンドは、Sigstoreの仕組みを活用した「Keyless署名(OIDCトークンを利用した署名)」だ。これなら秘密鍵の管理リスクをゼロにできる。

Keyless署名のコマンド例

# 秘密鍵をファイルとして保持せず、GitHubのOIDC IDトークンで署名する
cosign sign --yes my-repo/my-app:latest

—

5. まとめ:エンジニアとしての矜持

セキュリティとは、ツールを導入して満足するチェックリストではない。「どこでデータが改ざんされる可能性があるか」「誰がこのコードを承認したのか」を技術的に強制する仕組みを構築することだ。

1. Trivy で既知の穴を塞ぐ。
2. Cosign で「誰が作ったか」を保証する。
3. Admission Controller で、署名なきデプロイを即座に拒否する。

この3段構えを構築できれば、君たちのシステムは一般的なWebアプリよりも数段堅牢な領域に達するはずだ。

「面倒くさい」と感じるかもしれない。だが、インシデント対応で徹夜することに比べれば、この実装のコストなど微々たるものだ。自分の書いたコードが、誰の手によって実行されるのか。最後まで責任を持つのが、一流のエンジニアの流儀だよ。

さて、次は君たちのレジストリに cosign.pub を配置するところから始めようか。健闘を祈る。

コメント

タイトルとURLをコピーしました