コドモン Product Team Blog

株式会社コドモンの開発チームで運営しているブログです。エンジニアやPdMメンバーが、プロダクトや技術やチームについて発信します!

AWS Signerによるイメージ署名を用いてコンテナビルドパイプラインの妥当性を検証する

こちらは「コドモン Advent Calendar 2025」18日目の記事です。
18日目は、SREチームの小西が執筆いたします。

概要

「コンテナビルドパイプラインの中で各種テスト工程(静的コード解析・自動テスト・脆弱性スキャン 等)を実施し、通過した場合のみレジストリへのPush・デプロイを実施する」というのは、コンテナアプリケーションにおけるデプロイフロー構築時によく用いられる戦略かと思います。

一方で、「Push済のコンテナイメージが、コンテナビルドパイプラインの中で必要なテスト工程を実施済か否か」ということは、意外と抜けがちな観点かなと思います。

AWSの場合、Amazon ECRにPush可能な権限を絞ることで手動デプロイは防げますが、「必要なテスト工程を実施していないコンテナイメージ」は検知することができません。

そこで今回は、AWS Signer によるイメージ署名を用いて、コンテナビルドパイプラインの妥当性(必要なテスト工程を実施済か否か)を検証・検知する手法の検討を行ったので、ご紹介します。

情報整理

イメージ署名とは

イメージ署名とは、コンテナイメージに暗号化された電子署名を付与することで、イメージの真正性と完全性を保証する仕組みのことです。

ソフトウェアサプライチェーンセキュリティの重要な要素として位置付けられており、以下のような保証を提供します。

  • イメージの改ざん検知:コンテナイメージが署名後に変更されていないことを検証できます
  • 信頼できるソースの証明:イメージが承認されたCI/CDパイプラインから作成されたことを証明できます
  • 監査証跡の確立:どのイメージがいつ、どこで署名されたかの追跡が可能になります

AWS Signer とは

AWS Signerは、AWSが提供するフルマネージド型の署名サービスです。
コード署名やコンテナイメージ署名に必要な暗号化キーとデジタル証明書のライフサイクルを管理します。
コンテナイメージ署名においては、以下のような特徴を持ちます。

Notationとの統合

AWS Signerは、Cloud Native Computing Foundation(CNCF)配下のオープンソースプロジェクトであるNotationと統合されています。
NotationはNotary Projectが開発するコンテナイメージ署名・検証のためのCLIツールであり、AWS Signerはこのツールのプラグインとして動作します。

署名プロファイル

AWS Signerでは「署名プロファイル」という単位で署名設定を管理します。署名プラットフォームとして「Notation for container registries」を選択することで、イメージ署名が可能になります。
署名プロファイルには署名の有効期間(最大135ヶ月)などを設定できます。

AWS Signer を用いたイメージ署名の仕組み

AWS Signerを用いたイメージ署名は、以下のような仕組みで動作します。

  1. Notation CLIのインストール

    事前に、Notation CLIとAWS Signerプラグインをインストールする必要があります。
    AWS Signerインストーラーを用いることで、双方のインストールが可能です。

  2. イメージへの署名

    notation signコマンドを実行することで、コンテナイメージに署名を付与します。

     notation sign <IMAGE_DIGEST> \
       --plugin "com.amazonaws.signer.notation.plugin" \
       --id <SIGNER_PROFILE_ARN>
    

    このコマンドは内部的にAWS SignerのSignPayload APIを呼び出し、署名を生成します。

  3. 署名のレジストリへの保存

    生成された署名は、コンテナイメージと紐付けて保存されます。

AWS Signer を用いた署名検証の仕組み

AWS Signerを用いた署名検証は、以下のような仕組みで動作します。

  1. 信頼ポリシーの設定

    署名を検証する前に、「どの署名プロファイルを信頼するか」を定義した信頼ポリシー(Trust Policy)をNotationにインポートします。↓設定例

     {
       "version": "1.0",
       "trustPolicies": [{
         "name": "aws-signer-tp",
         "registryScopes": ["*"],
         "signatureVerification": {
           "level": "strict"
         },
         "trustStores": ["signingAuthority:aws-signer-ts"],
         "trustedIdentities": [
           "arn:aws:signer:region:account-id:/signing-profiles/profile-name"
         ]
       }]
     }
    
  2. 署名の検証

    notation verifyコマンドを実行することで、イメージの署名を検証します。

     notation verify <IMAGE_DIGEST>
    
  3. 検証結果の判定

    検証に成功した場合は、そのイメージが信頼できるコンテナビルドパイプラインで署名されたことが証明されます。検証に失敗した場合(署名がない、署名が不正、信頼できない署名者 等)は、エラーとして検知できます。

実現方法

AWS Signer によるイメージ署名を用いて、要件を実現する構成は以下です。
大きく分けて、2つの工程があります。

1:イメージ署名部分

コンテナビルドパイプラインの中で、コンテナイメージのビルド & 各種テスト工程を実施します。

従来であればテスト通過後、コンテナレジストリ(Amazon ECR)にコンテナイメージがPushされて終了なのですが、イメージPush後にAWS Signerを用いたイメージ署名を行うようにします。

2:署名検証・不正検知部分

次に、イメージ署名の検証を行い、署名されていないコンテナイメージが利用された場合に検知する部分です。

何らかのトリガーを契機として、署名の検証を行います。
AWS公式ドキュメントでは、ECS Service のライフサイクルフックを用いて、デプロイプロセスの特定の時点で AWS Lambda 関数をトリガーし、検証を行う手法が紹介されています。

Amazon ECS には、サービスのデプロイ中にカスタムロジックを実行できるようにするサービスライフサイクルフックが用意されています。これらのフックは、デプロイプロセスの特定の時点で AWS Lambda 関数をトリガーできるため、サービスの開始を許可する前にコンテナイメージの署名を検証できます。 https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/image-signing-verification.html#image-signing-verification-lambda

一方、コドモンの場合、ECS Serviceを介さず、スタンドアロン方式でECSタスクを起動するアーキテクチャ(ECS Scheduled Tasks 等)も存在していました。

そこで、ECS Serviceのデプロイ時ではなく、「ECSタスク定義の新規登録時」に検証を行う方法を試行しました。

ECSタスク定義の新規登録は、EventBridgeで検知します。
検知後、CodeBuildプロジェクトを発火し、その中でECSタスク定義に紐付くコンテナイメージの署名検証を行います。

署名が作成されていない場合は、コンテナビルドパイプライン内にて必要な手続き(各種テスト工程)を経ていないとみなし、検知します。

実装手順(イメージ署名部分)

まずは、コンテナビルドパイプライン、テスト工程を経たコンテナイメージに対して、署名を行うプロセスを実装していきます。

1:AWS Signer Notation署名プロファイルを作成する

AWS Signer より、署名プロファイルを作成します。

作成項目では以下のように入力します。

  • 署名プラットフォーム:Notation for container registries
  • 署名の有効期間:135カ月

2:コンテナイメージへの署名を行う権限を付与する

以下の権限をコンテナビルドパイプライン用IAM Roleに付与します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": [
                "signer:SignPayload"
            ],
            "Resource": "*"
        }
    ]
}

3:コンテナビルドパイプラインにイメージ署名プロセスを追加する

コンテナビルドパイプラインに、イメージ署名プロセス(notation sign)を追加します。

弊社では、コンテナビルドパイプライン用ツールとしてGIthubActionsを用いています。
GithubActionsの場合、署名部分のコードは以下のようになります。

env:
  ECR_REPOSITORY: xxxxx
  SIGNER_PROFILE_ARN: arn:aws:signer:ap-northeast-1:xxxxxxxxxx:/signing-profiles/xxxxx

jobs:
  build-push:
    runs-on: ubuntu-latest
    steps:
      
      - コンテナイメージビルド
      - 脆弱性スキャン
      - 自動テスト実行

      - name: Push image to Amazon ECR
        id: push-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: test-${{ github.sha }}
        run: |
          docker image tag test-${{ github.sha }} $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          docker image push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          # コンテナイメージのダイジェストを保持しておく
          echo "IMAGE_DIGEST=$(docker image inspect --format='{{index .RepoDigests 0}}' $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG)" >> $GITHUB_ENV

      # Notation CLI & Signer plugin のインストール
      - name: Setup Notation & Signer plugin
        run: |
          wget https://d2hvyiie56hcat.cloudfront.net/linux/amd64/installer/deb/latest/aws-signer-notation-cli_amd64.deb
          sudo dpkg -i aws-signer-notation-cli_amd64.deb
          notation version
          notation plugin ls

      # イメージ署名
      - name: Sign Container Image with AWS Signer
        run: |
          notation sign ${{ env.IMAGE_DIGEST }} --plugin "com.amazonaws.signer.notation.plugin" --id ${{ env.SIGNER_PROFILE_ARN }}

上記を実行すると、コンテナイメージに加え、Artifact media type: application/vnd.cncf.notary.signature というものがプッシュされていることが分かります。
これが、コンテナイメージの署名です。

また、イメージインデックスには、コンテナイメージの署名を参照するマニフェストが含まれています。

実装手順(署名検証・不正検知部分)

次に、署名されていないコンテナイメージを検知するプロセスを実装していきます。

1:CodeBuild Projectの作成

署名検証を行うCodeBuild Projectを作成します。

イメージ署名を検証するためには、信頼ポリシーをNotationにインポートする必要があります。

信頼ポリシーとして、以下のようなJsonファイルを作成します。

{
   "version":"1.0",
   "trustPolicies":[
      {
         "name":"aws-signer-tp",
         "registryScopes":[
            "*"
         ],
         "signatureVerification":{
            "level":"strict"
         },
         "trustStores":[
            "signingAuthority:aws-signer-ts"
         ],
         "trustedIdentities":[
            "arn:aws:signer:ap-northeast-1:xxxxxxxxxx:/signing-profiles/xxxxx"
         ]
      }
   ]
}

上記Jsonはソースプロバイダに配置して、CodeBuild内で利用できるようにしておきます。

buildspec.ymlは以下のように定義します。
Notationのセットアップ & 信頼ポリシーJsonをNotationにインポートした上で、ECSタスク定義に紐付くコンテナイメージの署名検証(notation verify)を行います。

version: 0.2

phases:
  install:
    commands:
      # インストールコマンドは、CodeBuildプロジェクトの環境イメージに合わせて設定してください
      - wget https://d2hvyiie56hcat.cloudfront.net/linux/amd64/installer/deb/latest/aws-signer-notation-cli_amd64.deb
      - sudo dpkg -i aws-signer-notation-cli_amd64.deb
      - notation version
      - notation plugin ls
      - notation policy import trust_policy.json

  pre_build:
    commands:
      - IMAGE_URI=$(aws ecs describe-task-definition --task-definition "$TASK_DEFINITION_ARN" --query "taskDefinition.containerDefinitions[?name=='$TARGET_CONTAINER_NAME'].image" --output text)
      - echo "Verifying $IMAGE_URI"
      - aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin "$REGISTRY"
      - docker image pull "$IMAGE_URI"

  build:
    commands:
      - IMAGE_DIGEST=$(docker image inspect --format='{{index .RepoDigests 0}}' $IMAGE_URI)
      - notation verify "$IMAGE_DIGEST"

2:コンテナイメージに対する署名検証を行う権限を付与する

以下の権限をCodeBuild用 IAM Roleに付与します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ECSDescribeTaskDefinition",
      "Effect": "Allow",
      "Action": "ecs:DescribeTaskDefinition",
      "Resource": "*"
    },
    {
      "Sid": "ECRAuth",
      "Effect": "Allow",
      "Action": "ecr:GetAuthorizationToken",
      "Resource": "*"
    },
    {
      "Sid": "ECRPull",
      "Effect": "Allow",
      "Action": [
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchCheckLayerAvailability"
      ],
      "Resource": "arn:aws:ecr:ap-northeast-1:xxxxxxxxx:repository/*"
    },
    {
      "Sid": "SignerVerify",
      "Effect": "Allow",
      "Action": "signer:GetRevocationStatus",
      "Resource": "*"
    }
  ]
}

3:EventBridge Rule の作成

CloudTrail経由で、特定のECSタスク定義が登録された際に検知できるようにします。

以下のようなイベントパターンにて、EventBridge Ruleを作成します。

{
  "source": ["aws.ecs"],
  "detail-type": ["AWS API Call via CloudTrail"],
  "detail": {
    "eventSource": ["ecs.amazonaws.com"],
    "eventName": ["RegisterTaskDefinition"],
    "requestParameters": {
      "family": ["{対象のECSTask定義名}"]
    }
  }
}

ターゲットには手順1で作成したCodeBuildプロジェクトを指定します。

また、以下のように入力トランスフォーマーを定義し、対象のタスク定義ARN・イメージ稼働対象のコンテナ名が、後段のCodeBuildに環境変数として渡されるようにしておきます。

入力パス

{
  "taskDefinitionArn": "$.detail.responseElements.taskDefinition.taskDefinitionArn"
}

入力テンプレート

{
  "environmentVariablesOverride": [
    {
      "name": "TASK_DEFINITION_ARN",
      "value": <taskDefinitionArn>,
      "type": "PLAINTEXT"
    },
    {
      "name": "TARGET_CONTAINER_NAME",
      "value": "{対象のコンテナ名}",
      "type": "PLAINTEXT"
    }
  ]
}

動作確認

コンテナイメージ署名を実施している場合

署名を実施しているコンテナイメージを参照したECSタスクが登録された場合は、CodeBuild実行内でイメージ署名検証に成功します。

[Container] 2025/12/07 19:03:40.487050 Running command notation verify "$IMAGE_DIGEST"
Successfully verified signature for xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/scan-demo@sha256:242fe629393aaeb9ad7ee7fa29d6a8a0f398274000efd846f3498e56b0247079

イメージ署名を実施していない場合

一方で、イメージ署名を実施していないコンテナイメージを参照したECSタスクが登録された場合は、CodeBuild実行内でイメージ署名検証に失敗します。

これによって、コンテナビルドパイプラインにて必要なテスト工程を実施せず、レジストリにPush & デプロイされたコンテナイメージを検知することができます。

[Container] 2025/12/07 19:08:41.241495 Running command notation verify "$IMAGE_DIGEST"
Error: signature verification failed: no signature is associated with "xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/scan-demo@sha256:90ac81c78f2229cc8c38826ec445b00cad6f05e9f029a1317e507cd368a3ed69", make sure the artifact was signed successfully

[Container] 2025/12/07 19:08:41.516788 Command did not exit successfully notation verify "$IMAGE_DIGEST" exit status 1
[Container] 2025/12/07 19:08:41.520918 Phase complete: BUILD State: FAILED

まとめ

AWS Signer によるイメージ署名を用いて、コンテナビルドパイプラインの妥当性を検証する手法を紹介しました。

今回は検知だけでしたが、署名が存在しない / 検証に失敗した場合はデプロイを中断するなど、アドミッションコントローラとして活用していくと、よりセキュアなワークロードも実現可能かと思います。

参考