Amazon Web Services ブログ

AWS Signer と Amazon EKS におけるコンテナイメージ署名の提供開始

導入

本日、AWS は AWS Signer によるコンテナイメージ署名の提供を開始しました。これは、Amazon Elastic Container Registry (Amazon ECR) などのコンテナレジストリに保存されたコンテナイメージの署名と検証を AWS ネイティブにサポートする新機能です。AWS Signer は、コードの信頼性と完全性を確保するためのフルマネージド型のコード署名サービスです。AWS Signer は、コード署名証明書、公開鍵、秘密鍵を管理し、ライフサイクル管理を簡素化する機能を提供しており、コードの署名と検証の機能に集中することができます。

この度、AWS Signer はコンテナイメージの署名と検証のサポートを開始し、Cloud Native Computing Foundation (CNCF) 内のオープンソース Notary プロジェクトである Notation と統合されました。AWS などの貢献により、Notation はオープンスタンダードかつ、鍵管理などの統合のためのベンダー固有のプラグインが可能なクライアント実装となっています。AWS Signer は、署名キーの管理、キーローテーション、PKI 管理を行い、シンプルなクライアントベースのワークフローを提供するキュレーションプラグインを通じて Notation と統合されています。

Notation は、Amazon ECR に組み込まれた新しい Open Containers Initiative (OCI) Distribution 機能を使用しており、署名やその他のアーティファクトを、それらが参照するイメージと一緒にレジストリに保存することができます。これにより、AWS Signer が署名に必要なアセットを管理し、ライフサイクル管理と失効の操作を簡素化する一方で、Amazon ECR とのやりとりを透過的に行うシンプルなコマンドでコンテナイメージに署名することができます。

背景

コンテナのイメージと配布は、完全性チェックが行われるようにすでに設計されています。すべてのイメージとアーティファクトはマニフェストによって記述され、そのコンテンツのハッシュであるダイジェストによって参照されます。これにより、イメージがレジストリからビルド環境やワークロード環境へ移動する際に、クライアントは取得したイメージの完全性を容易に検証することができます。では、なぜ署名が重要となるのでしょうか。署名は真正性と来歴を確立し、コンテンツが特定の当事者からのものであるかどうかを判断できるようになります。これにより、信頼できる相手からのイメージのみをコンテナイメージのビルドやデプロイで使用するように許可したり、デプロイポリシーを実装するために自身のコンテンツに署名したりできます。

Notation に実装されたコンテナイメージ署名のアプローチは、コンテナイメージの完全性の機能を活用し、コンテナイメージのレイヤーではなく、イメージマニフェストへ暗号的に署名するシンプルなメカニズムを使用しています。イメージマニフェストは、イメージコンテンツの検証可能なレコードであり、それらが記述するイメージのすべてのレイヤーのハッシュ化されたダイジェストを含んでいます。イメージレイヤーではなくイメージダイジェストに署名することは、効率的ではるかに高速です。この方法では、イメージがリモートにあったとしても、イメージ全体を署名環境に pull する必要がなく、リモートのイメージに対して署名が可能です。署名が作成されると、OCI アーティファクトとしてエンコードされ、コンテナイメージと一緒にイメージリポジトリに push されます。クライアントは任意の時点で、対象となるイメージの署名を発見し、信頼できるコンテンツ発行者のアイデンティティに対して検証ができます。これは、ソフトウェアアーティファクトやパッケージ配布で何十年にもわたって使用されてきた署名と検証のアプローチと同じで、コンテナのワークフローにうまく適合するツールを使っています。

コンテナイメージの検証は、Kyverno や OPA Gatekeeper などを使用して、デプロイに信頼できるコンテンツポリシーを構築するための論理ゲートを提供します。これにより、検証済みの信頼されたイメージのみを、ワークロードで実行することができるようになります。イメージ検証を行わない場合は、特定のレジストリやリポジトリのイメージに対してのみデプロイを許可したり、本番環境へのデプロイにおいて特定のイメージタグのみを許可するようなポリシーを強制することができます。イメージ検証を行う場合は、さらに一歩進んで、コンテンツの信頼性をポリシーの決定に反映させることができ、既知の検証済みソースからのイメージのみを使用するように許可することができます。

今回のリリースによって、AWS Signer は Notation との統合により、コンテナイメージの署名と検証のフルマネージドな機能をシンプルなワークフローにもたらしました。以下のソリューションウォークスルーに示すように、AWS Signer と Notation を使用することで、シンプルなコマンドでイメージの署名と検証を開始できます。

コンテナイメージ署名と AWS Signer

AWS Signer は、コードの信頼性と完全性を確保するためのフルマネージド型のコード署名サービスです。組織は、デジタル署名に対してイメージを検証して、イメージが変更されていないことや、イメージが信頼できる発行元からのものであることを確認します。AWS Signer を使用すると、セキュリティ管理者は署名環境を単一の場所で定義できます。たとえば、どの AWS Identity and Access Management (AWS IAM) ロールがどのリージョンでコードに署名できるか、などが設定できます。AWS Signer は、コード署名証明書と秘密鍵を管理し、コード署名のライフサイクルを一元管理できるようにします。AWS CloudTrail との統合により、誰が署名を生成しているかを追跡でき、コンプライアンス要件を満たすのに役立ちます。

AWS Signer は、クロスアカウント署名、署名の有効期間、キャンセルや取り消し操作を伴うプロファイルのライフサイクル管理などの機能をサポートしています。クロスアカウント署名を使用すると、セキュリティ管理者は制限されたアカウントで署名プロファイルを作成および管理でき、開発者アカウントやパイプラインアカウントなど他のアカウントに、アーティファクトに署名するための明示的な権限を与えることができます。これにより、両方のアカウントからの AWS CloudTrail ログを使用してガバナンスと監査を自動化することもできます。署名プロファイルを作成する際に、署名の有効期間を指定できます。これは、有効期間の終了後に誰かが署名を検証した場合に、署名の検証を警告または失敗させるための最適な日付制御と考えてください。最後に、署名プロファイルをキャンセルして、そのプロファイルからそれ以上署名を生成できないようにしたり、プロファイルを失効させて、失効日時以降に生成された既存の署名を無効化したりできます。キャンセルと失効の操作はガバナンスの変更やセキュリティインシデントに対応するためのコントロールを提供し、検証作業を完全にコントロールできます。AWS Signer では、個々のイメージにある特定の署名を無効化する必要がある場合に、個々の署名を失効することができる柔軟性を備えています。

開始方法

今回のリリースでは、Amazon ECR やその他の OCI 準拠のレジストリに保存されているコンテナイメージを、AWS Signer プラグインを使用して Notation で署名することができます。AWS Signer によるコンテナイメージ署名を開始するには、AWS Signer 署名プロファイルを作成し、Notation クライアントと AWS Signer プラグインをインストールして、それを署名プロファイルと関連付けるように設定します。このシンプルなプロセスが完了すると、Notation クライアントを使用してシンプルなコマンドでイメージの署名や検証ができます。

コンテナワークロードを検証するために、今回の初回リリースでは Kubernetes がサポートされます。Kyverno は Kubernetes 向けに設計されたオープンソースのポリシーエンジンです。Kyverno のポリシーは Kubernetes のリソースであり、kubectl や kustomize などのツールを使ってポリシーを管理します。AWS パートナーの Nirmata は、AWS Signer プラグインを Kyverno の Notation サポートと統合しました。以下のソリューションには、それを使用したチュートリアルが含まれています。

さらに、オープンソースの検証エンジンであり、Open Policy Agent (OPA) Gatekeeper と併用することで検証ベースのポリシーを定義することができる Ratify プロジェクトもあります。Amazon Elastic Kubernetes Service (Amazon EKS) および AWS Signer チームのメンバーは、Ratify オープンソースプロジェクトに貢献し、その保守を支援しています。このプロジェクトは 1.0 リリース間近で、AWS Signer プラグインを含む Notation をサポートしています。

最後に、Dynamic Admission Control のアプローチを使用して、Kubernetes 用の独自のカスタムアドミッションコントローラを構築することもできます。OPA Gatekeeper や Kyverno のような Policy-as-Code (PaC) ソリューションを使用しておらず、またそれらを採用する予定もない場合でも、AWS Signer と Notation を使用して、Kubernetes クラスターにワークロードをデプロイする際に信頼できるイメージのみが使用されるようにすることができます。AWS によるサポート対象ではありませんが、アプローチの例として k8s-notary-admission という OSS プロジェクトを公開しています。

ソリューションの概要

このセクションでは、Notation と AWS Signer によるコンテナイメージの署名について詳しく説明します。その後、Amazon EKS で Kyverno ポリシーエンジンを使用して、Amazon EKS クラスターで使用するコンテナイメージの署名を検証する方法を例として説明します。

注意: この記事で説明しているソリューションは、AWS で実行されているセルフマネージドな Kubernetes クラスターでも動作します。

ユースケース – Notation および AWS Signer を使用した Amazon ECR コンテナイメージの署名

このソリューションでは、Notation コマンドラインインターフェイス (CLI) を使用して、Amazon ECR プライベートリポジトリに保存されているコンテナイメージに署名します。暗号署名に必要なアセットである証明書、公開鍵、秘密鍵は、AWS Signer の署名プロファイルと AWS CLI で作成および管理されます。

はじめに、AWS CLI が最新バージョンにアップデートされていることを確認します。アップデートの手順は AWS コマンドラインインターフェイスユーザーガイドの「AWS CLI の最新バージョンをインストールまたは更新します」に記載されています。

次に、Notation CLI と必要な AWS Signer プラグイン、そしてルート証明書をインストールします。AWS Signer の開発者ガイドには、Linux、macOS、Windows 用のインストーラーのリストが掲載されています。この例では、macOS の arm64 バージョンをインストールしています。インストーラーは、/usr/local/bin/notation に Notation を、/Users/<USERNAME>/Library/Application Support/notation に Notation CLI の設定をインストールします。

インストール後、本記事を執筆時点での Notation バージョンは以下の通りです。

# Get Notation CLI version
$ notation version
Notation - a tool to sign and verify artifacts.

Version:     1.0.0-rc.7
Go version:  go1.20.4
Git commit:  ebfb9ef707996e1dc11898db8b90faa8e8816ae6

Notation ディレクトリのツリーには、ディレクトリ構成が表示されます。ツリー出力では、AWS Signer プラグイン、Notation truststore ディレクトリ、Notation trustpolicy ドキュメントがインストールされていることが確認できます。

# Notation directory structure
$ tree
.
├── LICENSE
├── THIRD_PARTY_LICENSES
├── plugins
│   └── com.amazonaws.signer.notation.plugin
│       ├── LICENSE
│       ├── THIRD_PARTY_LICENSES
│       └── notation-com.amazonaws.signer.notation.plugin
├── signingkeys.json
├── trustpolicy.json
└── truststore
    └── x509
        └── signingAuthority
            └── aws-signer-ts
                 └──aws-signer-notation-root.crt

インストーラーを使用して、Notation クライアントに正しい AWS Signer プラグインと AWS Signer ルート証明書を含む truststore が設定されます。Notation が AWS Signer プラグインを使用するように正しく設定されているかどうかは、次の notation plugin ls コマンドで確認できます。

# List configured Notation plugins
$ notation plugin ls
NAME                                   DESCRIPTION                      VERSION         CAPABILITIES                                                                                             ERROR
com.amazonaws.signer.notation.plugin   AWS Signer plugin for Notation   1.0.0-fa04d83   [SIGNATURE_GENERATOR.ENVELOPE SIGNATURE_VERIFIER.TRUSTED_IDENTITY SIGNATURE_VERIFIER.REVOCATION_CHECK]   <nil>

AWS Signer で Notation を使用するには、AWS Signer で署名プロファイルを作成する必要があります。次のコマンドを使用して notation_test 署名プロファイルを作成します。

# Create a AWS Signer signing profile with default validity period
$ aws signer put-signing-profile \
    --profile-name notation_test \
    --platform-id Notation-OCI-SHA384-ECDSA

上記のコマンドでは、新しくプロビジョニングされた署名プロファイルに対して、署名プロファイルの有効期間を 135 ヶ月 (11 年 3 ヶ月) に自動的に設定します。特定の期間より古いアーティファクトを拒否するような高度なユースケースでは、以下のように署名の有効期間を短く設定できます。

# Create a AWS Signer signing profile with specific validity period
$ aws signer put-signing-profile \
--profile-name notation_test \
--platform-id Notation-OCI-SHA384-ECDSA \
--signature-validity-period ‘value=12, type=MONTHS’ \

その後、以下のコマンドで AWS Signer の署名プロファイルを一覧表示できます。

# List existing signing profiles
$ aws signer list-signing-profiles
{
    "profiles": [
        {
            "profileName": "notation_test",
            "profileVersion": "vjPSTMwGW3",
            "profileVersionArn": "arn:aws:signer:<AWS_REGION>:<AWS_ACCOUNT_ID>:/signing-profiles/notation_test/vjPSTMwGW3",
            "signingMaterial": {},
            "signatureValidityPeriod": {
                "value": 12,
                "type": "MONTHS"
            },
            "platformId": "Notation-OCI-SHA384-ECDSA",
            "platformDisplayName": "Notation for Container Registries",
            "status": "Active",
            "arn": "arn:aws:signer:<AWS_REGION>:<AWS_ACCOUNT_ID>:/signing-profiles/notation_test",
            "tags": {}
        }
    ]
}

AWS Signer プラグインをインストールし、署名プロファイルを用意したので、コンテナイメージに署名する準備はほぼ完了です。この例では、Amazon ECR リポジトリに保存されている Kubernetes pause コンテナを使用します。以下の notation sign コマンドは、イメージタグの代わりにコンテナイメージダイジェストを使用します。タグはミュータブル (可変) で非決定的である可能性があるため、ユニークなダイジェストを使用することがベストプラクティスとされています。

Amazon ECR 認証情報と CLI ツール

Amazon ECR コンテナイメージに署名する前に、Notation CLI が Amazon ECR レジストリにアクセスするための認証情報を持っていることを確認する必要があります。Amazon ECR の認証情報を Notation に提供するオプションとして、次の 2 つの方法を検討します。

  • notation login コマンドの使用
  • credential-helper (認証情報ヘルパー) の使用

どちらのオプションでも、最初に AWS STS アカウントプロファイル設定を行う必要があります。

以下の notation login コマンドは、aws ecr get-login-password コマンドを使用して AWS CLI からパスワードを取得し、認証情報を使用してリージョン固有の Amazon ECR レジストリにログインします。Amazon ECR の認証情報は AWS リージョンに固有であるため、Notation は運用する各 Amazon ECR リージョンで認証される必要があります。

# Login to Amazon ECR region with the Notation CLI
$ aws ecr get-login-password | notation login \
--username AWS \
--password-stdin <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com
Login Succeeded

notation login コマンドで使用される認証情報は、設定された Notation の認証情報ストアに保存されます。M1 Mac では、この設定は notation ディレクトリの config.json にあります。

# Notation config.json file
{
    "auths": {},
    "credsStore": "osxkeychain"
}

上記のファイルでは、credsStore 要素が osxkeychain を指しています。これは Notation が使用するデフォルトの MacOS システム認証情報ストアで、「キーチェーンアクセス」アプリケーションからアクセスできます。この認証情報ストアは、DockerORAS など他の CLI ツールでも使用されます。osxkeychain では、認証情報は「Docker 認証情報タイプ」として保存されます。

認証情報ヘルパーは CLI ツールの下で動作し、必要な認証情報を収集して CLI 操作で使用できるようにします。Amazon ECR Docker Credential Helper は、Amazon ECR 認証情報を使用する CLI ツールで、aws ecr get-login-password コマンドの代わりに使用できます。

Amazon ECR Docker Credential Helper は、AWS SDK for Go v2 と、AWS CLI で使用される AWS STS プロファイルを使用して Amazon ECR 認証情報を収集し、それらを一時的に保存して、notation sign などの CLI コマンドでインライン使用できるようにします。つまり、CLI の loginlogout コマンドは使用されず、「Docker 認証情報」は osxkeychain 認証情報ストアに保存されません。これにより、CLI 認証情報の衝突や上書きエラーの可能性を排除します。

署名

ログインするか、もしくは Amazon ECR Docker Credential Helper を使用すると、設定されている AWS Signer プラグインと署名プロファイルを参照する以下の Notation コマンドは、イメージダイジェストを使用してコンテナイメージに署名します。

notation sign \
<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/pause\
@sha256:fcaff905397ba63fd376d0c3019f1f1cb6e7506131389edbcb3d22719f1ae54d \
--plugin com.amazonaws.signer.notation.plugin \
--id arn:aws:signer:<AWS_REGION>:<AWS_ACCOUNT_ID>:/signing-profiles/notation_test

注意: Amazon ECR はリポジトリレベルの設定としてイミュータブル (不変) タグをサポートしていますが、このコンテナイメージ署名アプローチで使用される OCI 1.0 のリファレンス仕様では、タグのイミュータビリティはサポートされていません。

OCI 1.0 のリファレンス仕様では、コンテナイメージの署名は、タグ付けされたコンテナイメージと共に OCI レジストリに保存されます。Amazon ECR リポジトリでは、以下のスクリーンショットのように署名が保存されます。タグ付けされていないアーティファクトが実際の署名です。イメージインデックスには、コンテナイメージの署名を参照するマニフェストが含まれています。

タグ付けされていないアーティファクトの「アーティファクトタイプ」列の「その他」の値をクリックすると、そのタイプが application/vnd.cncf.notary.signature であることがわかります。

署名が完了したら、以下の inspect コマンドを使用して、証明書チェーンやフィンガープリントを含む署名を検査できます。繰り返しですが、コンテナイメージの指定にはダイジェストを使用します。

# Inspect container image signatures with Notation
$ notation inspect <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/pause\
@sha256:fcaff905397ba63fd376d0c3019f1f1cb6e7506131389edbcb3d22719f1ae54d

Inspecting all signatures for signed artifact
<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/pause@sha256:fcaff905397ba63fd376d0c3019f1f1cb6e7506131389edbcb3d22719f1ae54d
└── application/vnd.cncf.notary.signature
    └── sha256:ca78e5f730f9a789ef8c63bb55275ac12dfb9e8099e6a0a64375d8a95ed501c4
        ├── media type: application/jose+json
        ├── signature algorithm: ECDSA-SHA-384
        ├── signed attributes
        │   ├── expiry: Wed May 22 19:24:19 2024
        │   ├── com.amazonaws.signer.signingJob: arn:aws:signer:<AWS_REGION>:<AWS_ACCOUNT_ID>:/signing-jobs/3a0b44b4-714a-4296-8d94-dcc0a7bdfeb2
        │   ├── com.amazonaws.signer.signingProfileVersion: arn:aws:signer:<AWS_REGION>:<AWS_ACCOUNT_ID>:/signing-profiles/notation_test/vjPSTMwGW3
        │   ├── io.cncf.notary.verificationPlugin: com.amazonaws.signer.notation.plugin
        │   ├── signingScheme: notary.x509.signingAuthority
        │   └── signingTime: Mon May 22 19:24:19 2023
        ├── user defined attributes
        │   └── (empty)
        ├── unsigned attributes
        │   └── (empty)
        ├── certificates
        │   ├── SHA256 fingerprint: 581899293591f48b4fd82b6f636431a84784d79b10eeff340f44f887d328acc5
        │   │   ├── issued to: CN=AWS Signer,OU=AWS Cryptography,O=AWS,L=Seattle,ST=WA,C=US
        │   │   ├── issued by: CN=AWS Signer <AWS_REGION> Code Signing CA G1,OU=Cryptography,O=AWS,ST=WA,C=US
        │   │   └── expiry: Thu May 25 16:31:29 2023
        │   ├── SHA256 fingerprint: f0e6d676ae9ff152451f149c737a31f02ddcb093a1e3a5afefa6e931a7a59473
        │   │   ├── issued to: CN=AWS Signer <AWS_REGION> Code Signing CA G1,OU=Cryptography,O=AWS,ST=WA,C=US
        │   │   ├── issued by: CN=AWS Signer Code Signing Int CA G1,OU=Cryptography,O=AWS,ST=WA,C=US
        │   │   └── expiry: Sun Jan  7 03:23:48 2024
        │   ├── SHA256 fingerprint: eaaac975dcc0d5d160fca1e39834834f014a238cd224d053670982388ccbfca1
        │   │   ├── issued to: CN=AWS Signer Code Signing Int CA G1,OU=Cryptography,O=AWS,ST=WA,C=US
        │   │   ├── issued by: CN=AWS Signer Code Signing Root CA G1,OU=Cryptography,O=AWS,ST=WA,C=US
        │   │   └── expiry: Thu Oct 28 23:18:32 2027
        │   └── SHA256 fingerprint: 90a87d0543c3f094dbff9589b6649affe2f3d6e0f308799be2258461c686473f
        │       ├── issued to: CN=AWS Signer Code Signing Root CA G1,OU=Cryptography,O=AWS,ST=WA,C=US
        │       ├── issued by: CN=AWS Signer Code Signing Root CA G1,OU=Cryptography,O=AWS,ST=WA,C=US
        │       └── expiry: Tue Oct 27 22:33:22 2122
        └── signed artifact
            ├── media type: application/vnd.docker.distribution.manifest.v2+json
            ├── digest: sha256:fcaff905397ba63fd376d0c3019f1f1cb6e7506131389edbcb3d22719f1ae54d
            └── size: 527

上記のコマンド出力では、署名されたアーティファクトが階層的に表示され、署名、関連する証明書チェーン、証明書フィンガープリントを確認できます。これは、トラブルシューティングや、署名に必要なアセットの出所を追跡する必要がある場合に非常に役立ちます。

ユースケース – Notation CLI によるコンテナイメージの検証

Notation CLI と AWS Signer 署名プロファイルを使用してコンテナイメージに署名したら、適用されたコンテナイメージの署名の検証に移ります。Kubernetes 内でイメージ署名の検証を試す前に、Notation CLI を使用して署名を検証できます。このプロセスでは、有効な Notation trustpolicy ドキュメントが必要です。

Notation のインストールの箇所で前述したように、trustpolicy は notation ディレクトリツリーにありました。ただし、インストーラーがインストールした trustpolicy は空のファイルであるため、有効な trustpolicy ドキュメントを作成する必要があります。この例で使用する trustpolicy を以下に示します。

# Notation trustpolicy document
{
  "version": "1.0",
  "trustPolicies": [
    {
      "name": "aws-signer-tp",
      "registryScopes": [
        "<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/pause"
      ],
      "signatureVerification": {
        "level": "strict"
      },
      "trustStores": [
        "signingAuthority:aws-signer-ts"
      ],
      "trustedIdentities": [
        "arn:aws:signer:<AWS_REGION>:<AWS_ACCOUNT_ID>:/signing-profiles/notation_test"
      ]
    }
  ]
}

注意: registryScopes のリストは単一のワイルドカード “*” をサポートしています。これは、スコープ内のすべてのレジストリとリポジトリの組み合わせを設定します。

前述の Notation trustpolicy ドキュメントは、コンテナイメージの署名に使用した AWS Signer 署名プロファイルを使用して、コンテナイメージの署名を検証するように Notation を設定します。trustpolicy は、以下の項目を設定します。

  • レジストリのスコープ
  • 署名検証 – AWS Signer 署名プロファイルの失効チェックを設定する
  • 検証に使用する truststore
  • 信頼されたアイデンティティ – AWS Signer 署名プロファイル

上記の trustpolicy を設定する最も簡単な方法は、notation policy import コマンドを使用して、既知の JSON ドキュメントをインポートすることです。

# Import a known-good Notation trustpolicy JSON document
$ notation policy import trustpolicy.json

Trust policy configuration imported successfully.

インポートおよび設定された trustpolicy は、notation policy show コマンドで確認できます。

# Show the current Notation trustpolicy document
$ notation policy show{
  "version": "1.0",
  "trustPolicies": [
    {
      "name": "aws-signer-tp",
      "registryScopes": [
        "<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/pause"
      ],
      "signatureVerification": {
        "level": "strict"
      },
      "trustStores": [
        "signingAuthority:aws-signer-ts"
      ],
      "trustedIdentities": [
        "arn:aws:signer:<AWS_REGION>:<AWS_ACCOUNT_ID>:/signing-profiles/notation_test"
      ]
    }
  ]
}

Notation の trustpolicy を設定したので、以下のように Notation を使用してコンテナイメージの署名を検証できます。

# Verify the signed images with the currently configured Notation CLI
$ notation verify \
<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/pause\
@sha256:fcaff905397ba63fd376d0c3019f1f1cb6e7506131389edbcb3d22719f1ae54d
Successfully verified signature for <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/pause@sha256:fcaff905397ba63fd376d0c3019f1f1cb6e7506131389edbcb3d22719f1ae54d

ユースケース – Kubernetes における Kyverno を用いたコンテナイメージの検証

Notation CLI と AWS Signer によるコンテナイメージの署名と検証が完了したので、次は Kubernetes でのコンテナイメージの検証に進みます。この例では、Kubernetes の Dynamic Admission Control を用いた Amazon EKS と、Kyverno ポリシーエンジンを使用します。Kyverno-Notation-AWS Signer ソリューションは、OSS プロジェクト kyverno-notation-aws にあります。

まず、Kyverno を実行してソリューションをテストするための Amazon EKS クラスターが必要です。クラスターは、Amazon EKS クラスターの作成でプロビジョニングできます。クラスターが作成されたら、インストール手順に従って Kyverno-Notation-AWS Signer ソリューションを開始できます。すべてのクラスターに共通する最初のステップは、以下に要約されます。

  • cert-manager のインストール
  • Kyverno ポリシーエンジンのインストール
  • kyverno-notation-aws アプリケーションのインストール
  • Notation の TrustPolicy と TrustStore リソースの Kubernetes custom resources definitions (CRD) を適用

Kyverno の設定

前述のステップが完了したら、コンテナイメージの署名とその署名の検証に使用した Notation と AWS Signer の設定を使用するように Kyverno ソリューションを設定する最後のステップに進みます。まず、適切な TrustStore リソースを適用します。

apiVersion: notation.nirmata.io/v1alpha1
kind: TrustStore
metadata:
  name: aws-signer-ts
spec:
  trustStoreName: aws-signer-ts
  type: signingAuthority
  caBundle: |-
    -----BEGIN CERTIFICATE-----
    MIICWTCCAd6g...
    -----END CERTIFICATE-----

上記の TrustStore リソースの caBundle 要素は、Notation と AWS Signer のインストール時に設定した AWS Signer ルート証明書です。

次に、Kyverno ソリューションで使用されている Notation Golang ライブラリに適切な AWS Signer プロファイルと TrustPolicy を使用するよう指示するために、適切な TrustPolicy リソースを適用する必要があります。これは、Notation CLI の設定でも行ったものです。

apiVersion: notation.nirmata.io/v1alpha1
kind: TrustPolicy
metadata:
  name: trustpolicy-sample
spec:
  version: '1.0'
  trustPolicies:
  - name: aws-signer-tp
    registryScopes:
    - "*"
    signatureVerification:
      level: strict
      override: {}
    trustStores:
    - signingAuthority:aws-signer-ts
    trustedIdentities:
    - "arn:aws:signer:<AWS_REGION>:<AWS_ACCOUNT_ID>:/signing-profiles/notation_test"

次に、以下のコマンド使用して、Secret kyverno-notation-aws-tls に含まれる TLS 証明書チェーンを使用するように、Kyverno のクラスターポリシー check-images を更新および適用する必要があります。これにより、クラスターポリシーは Kyverno ポリシーエンジンの外部にある Kubernetes Service kyverno-notation-aws を呼び出すことができるようになります。これは、AWS Signer 向けの Kyverno の拡張サービスです。

更新後の Kyverno ポリシーは以下のようになります。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-images     
spec:
  validationFailureAction: Enforce
  webhookTimeoutSeconds: 30
  rules:
  - name: call-aws-signer-extension
    match:
      any:
      - resources:
          namespaces:
          - test-notation
          kinds:
          - Pod
    context:
    - name: result
      apiCall:
        method: POST
        data:
        - key: images
          value: "{{ request.object.spec.[ephemeralContainers, initContainers, containers][].image }}"
        service:
          url: https://svc.kyverno-notation-aws/checkimages
          caBundle: |-
            -----BEGIN CERTIFICATE-----
            MIICiTCCAjCg...
            -----END CERTIFICATE-----
            -----BEGIN CERTIFICATE-----
            MIIBdzCCAR2g...
            -----END CERTIFICATE-----
    validate:
      message: "not allowed"
      deny:
        conditions:
          all:
          - key: "{{ result.verified }}"
            operator: EQUALS
            value: false

TrustPolicyTrustStore リソース、クラスターポリシー check-images が適用されたら、IAM Roles for Service Accounts (IRSA) を使用するように Namespace kyverno-notation-aws に適切な Service Account kyverno-notation-aws を設定します。IRSA を使用すると、Service Account を使用する Pod に、 IAM ロールプリンシパルとポリシーに基づく IAM 認証情報を供給します。これらの認証情報は、以下のアクセスに使用されます。

  • コンテナイメージの署名を pull するための、リージョン固有の Amazon ECR 認証情報の取得
  • コンテナイメージの署名検証に必要な AWS Signer API へのアクセス

Service Account kyverno-notation-aws は、すでに kyverno-notation-aws アプリケーションと一緒にインストールされています。IRSA を使用するために、Service Account の設定を上書きします。この上書きを行う最も簡単な方法は、以下の eksctl コマンドを使用することです。

# Create/Update IRSA
NAME=kyverno-notation-aws
NAMESPACE=kyverno-notation-aws
CLUSTER=kyverno-notary-uw2
ECR_POLICY=arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
SIGNER_POLICY=arn:aws:iam::<AWS_ACCOUNT_ID>:policy/notary-admission-signer

eksctl create iamserviceaccount \
  --name $NAME \
  --namespace $NAMESPACE \
  --cluster $CLUSTER \
  --attach-policy-arn $ECR_POLICY \
  --attach-policy-arn $SIGNER_POLICY \
  --approve \
  --override-existing-serviceaccounts

注意: eksctl コマンドは、セルフマネージドな Kubernetes では動作しません。

Service Account kyverno-notation-aws の更新が完了したら、現在の Pod kyverno-notation-aws を削除します。新しい Pod は、新しく設定された Service Account kyverno-notation-aws から AWS 認証情報を受け取り、Kyverno が Amazon ECR および AWS Signer API と通信できるようになります。

# Delete the current pods that are using non-IRSA credentials
$ kubectl -n kyverno-notation-aws delete po kyverno-notation-aws-6545d654cd-6qfzj
pod "kyverno-notation-aws-6545d654cd-6qfzj" deleted

# Verify pods come back up and are ready
$ kubectl -n kyverno-notation-aws get po -w
NAME                                    READY   STATUS              RESTARTS   AGE
kyverno-notation-aws-6545d654cd-wjtpd   0/2     ContainerCreating   0          5s
kyverno-notation-aws-6545d654cd-wjtpd   2/2     Running             0          12s

注意: 既存の Service Account kyverno-notation-aws を上書きして既存の Pod kyverno-notation-aws を削除する代わりに、手動で AWS IAM ロールを作成し、kyverno-notation-aws ソリューションの install.yaml ファイル内の既存の Service Account リソースに eks.amazonaws.com/role-arn アノテーションを追加することもできます。

新しい Pod kyverno-notation-aws が新しい IRSA 認証情報を使用するように実行されたら、前述の Notation CLI を用いたテストと同様に、Kyverno を用いたソリューションをテストできます。正常な Pod (有効な署名) と不正な Pod (無効な署名) の Deployment を適用して、イメージの署名を検証できます。

# Apply the test resources to test the container image signature validation policies
$ kubectl apply -f .
namespace/test-notation created
pod/notary-admit created
deployment.apps/test created
Error from server: error when creating "3-test-pod-bad.yaml": admission webhook "validate.kyverno.svc-fail" denied the request:

resource Pod/test-notation/notary-admit-bad was blocked due to the following policies

check-images:
  call-aws-signer-extension: |
    failed to check deny preconditions: failed to substitute variables in condition key: failed to resolve result.verified at path : failed to execute APICall: HTTP 500 Internal Server Error: failed to verify image <AWS_ACCOUNT_ID>.dkr.ecr.<AWS-_REGION>.amazonaws.com/pause:3.9: no signature is associated with "<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/pause@sha256:e58e7b93b7b41119528b189803971223eeccece0df6e2af3c2df9c81978c58cc", make sure the artifact was signed successfully

Error from server: error when creating "5-test-deploy-bad.yaml": admission webhook "validate.kyverno.svc-fail" denied the request:

resource Deployment/test-notation/test-bad was blocked due to the following policies

check-images:
  autogen-call-aws-signer-extension: |
    failed to check deny preconditions: failed to substitute variables in condition key: failed to resolve result.verified at path : failed to execute APICall: HTTP 500 Internal Server Error: failed to verify image <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/pause:3.9: no signature is associated with "<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/pause@sha256:e58e7b93b7b41119528b189803971223eeccece0df6e2af3c2df9c81978c58cc", make sure the artifact was signed successfully

予想通り、正常な Pod と Deployment はコンテナイメージの検証に合格しましたが、不正な Pod と Deployment は合格しませんでした。

注意: Amazon ECR はコンテナイメージの複数の署名をサポートしています。検証中に、Notationと AWS Signer のソリューションが複数の署名を検出した場合、TrustPolicy に記載されている署名プロファイルのいずれかで検証に合格した署名があれば、検証を合格とします。たとえば、TrustPolicy に 10 個のプロファイルがあり、また複数の署名がある場合、10 個のプロファイルのいずれかの署名が検証チェックに合格した場合、イメージの検証は成功することになります。

Pod コントローラー用 Kyverno 自動生成ルール

クラスターポリシー check-images では、Pod リソースの検証のみを指定していました。しかし、テスト中には Deployment リソースも検証されていました。どうして、このようなことが可能なのでしょうか。Kyverno には、提供された Pod ポリシーに基づいて、Pod コントローラーのポリシールールを作成する自動生成機能が含まれています。Kyverno が既存のポリシーを新しいルールで変更すると、新しく自動生成されたルールが元のクラスターポリシーの status 要素に表示されます。

# The cluster policy was modified to include pod-controller rules
$ kubectl get cpol check-images -o=jsonpath=’{.status.autogen.rules[0].match}’|jq .
{
  "any": [
    {
      "resources": {
        "kinds": [
          "DaemonSet",
          "Deployment",
          "Job",
          "StatefulSet",
          "ReplicaSet",
          "ReplicationController"
        ],
        "namespaces": [
          "test-notation"
        ]
      }
    }
  ],
  "resources": {}
}

ご覧のように、クラスターポリシー check-images は、Kyverno の自動生成機能によって新しいルールに更新され、Pod を作成するコントローラーを含むようになりました。これにより、対応するクラスターポリシールールをすべて手動で作成して管理する必要がなくなりました。

クリーンアップ

以下の手順で、プロビジョニングしたリソースのクリーンアップができます。

  1. Amazon EKS クラスターを削除します
  2. IAM Roles for Service Accounts (IRSA) 設定で使用した AWS IAM ロールとポリシーを削除します
  3. AWS CLI コマンドで、作成および使用した AWS Signer の署名プロファイルを失効させます
    1. aws signer revoke-signing-profile
  4. Amazon ECR リポジトリから署名を削除します

前述のように、現在 Notation は OCI 1.0 をサポートしており、OCI イメージインデックスを使用してレジストリ内の署名を追跡しています。Amazon ECR では、OCI イメージインデックスの中で参照されているアーティファクトやイメージの削除はできないため、マネジメントコンソールから署名を削除するだけでは効果がありません。

ORAS プロジェクトの oras クライアントを使用して、署名やその他の参照型アーティファクトを削除できます。最初にインデックスから参照を削除し、次にマニフェストを削除するように実装されています。oras manifest delete コマンドを使用して、署名アーティファクトのインデックスを参照できます。

上記のように、OCI 1.1 Distribution 仕様がリリースされると、クライアントは OCI レジストリ内でアーティファクト参照を管理する必要がなくなります。前述した oras クライアントの使用は、OCI 1.0 を使用する場合のみ必要です。

注意: 古くなったり使われなくなったりした、あるいは無効になったセキュリティリソースや設定を削除することは、常にセキュリティ上のグッドプラクティスです。

まとめ

この記事では、AWS Signer によるコンテナイメージ署名の詳細を説明しました。これは、フルマネージドなソリューションでコンテナイメージの署名や検証をするための新機能です。オープンソースの Notation クライアントと精選された AWS Signer プラグインを使用することで、コンテナイメージの署名と検証のためのシンプルなクライアントベースのワークフローを採用できます。この新機能に関する機能追加や統合拡張を続けていきますので、皆さんのフィードバックをお待ちしています。GitHub で公開しているコンテナロードマップにアクセスして進捗を確認し、Issue を起票して今後のアイデアについてお知らせください。

本記事は Announcing Container Image Signing with AWS Signer and Amazon EKS (2023 年 6 月 6 日公開) を翻訳したものです。翻訳は、ソリューションアーキテクトの落水が担当しました。