イメージおよび非イメージ成果物の署名の検証

イメージの署名を検証することにより、デプロイメント前に継続的統合 (CI) パイプラインで作成されたイメージの整合性を維持します。

開始前に

画像認証の作業を開始する前に、以下の前提条件が満たされていることを確認してください。

  • GPG 公開鍵を持っている必要があります。 GPG キーの生成については、 資料 を参照してください。
  • base 64 エンコード GPG 公開鍵を使用して環境変数 code-signing-certificate を追加する必要があります。
  • 検証中にコンテナイメージにアクセスするには、レジストリの認証情報を設定する必要があります( 「レジストリの認証情報の設定 」を参照)。

イメージの検証

新しい prod-verify-artifact ステージは、 Continuous Delivery (CD) パイプライン内のイメージの署名を検証します。 このステージでは、以下のステップを実行します。

  1. ユーザー提供のエンコードされた GPG 公開鍵を一時ファイルにデコードします。

  2. 公開鍵を使用して Docker コンテナー・ポリシー (/etc/containers/policy.json) を作成します。

     {
       "default": [
         {
           "type": "reject"
         }
       ],
       "transports": {
         "docker-daemon": {
           "": [
             {
               "type": "reject"
             }
           ]
         },
         "docker": {
           "": [
             {
               "type": "signedBy",
               "keyType": "GPGKeys",
               "keyPath": "/tmp/GPGPublicKey"
             }
           ]
         }
       }
     }
    
  3. Skopeo を使用してコンテナー・ポリシーでイメージをプルすることにより、各成果物の成果物のリストを取得します。

        skopeo copy docker://"${image}" dir:"${tmp_sign_dir}" --src-creds iamapikey:"${ibmcloud_api_key}"
    

署名が有効であり、ユーザーによって提供された公開鍵によって検証される場合、イメージ・プルは成功します。

検証用のレジストリ認証情報の設定

コンテナイメージの署名を検証する際、パイプラインではコンテナレジストリへの認証や署名済みイメージへのアクセスを行うために、認証情報が必要となります。 DevSecOps パイプラインは、実行時の動的な認証情報の解決をサポートしており、自動フォールバック機能を利用して、さまざまな方法で認証情報を設定することができます。

認証情報の解決階層

このパイプラインは、実行時に以下の階層構造に従って、ユーザー名とAPIキーの両方の認証情報を動的に解決します:

APIキーの解決順序:

  1. ネームスペース固有のAPIキーsigning-token-apikey-{registry}-{namespace} (シークレット)
  2. レジストリ固有のAPIキーsigning-token-apikey-{registry} (シークレット)
  3. Docker 設定JSON: signing-dockerconfigjson (非公開)
  4. ICR固有のフォールバック
    • ciso-ibmcloud-api-key (秘密)
    • ibmcloud-api-key (秘密)

ユーザー名の解決順序:

  1. 名前空間固有のユーザー名: signing-token-username-{registry}-{namespace} (環境変数)
  2. レジストリ固有のユーザー名signing-token-username-{registry} (環境変数)
  3. デフォルト: iamapikey (ユーザー名が設定されていない場合)

ここで、

  • {registry} レジストリのホスト名です(例: us.icr.iode.icr.io
  • {namespace} スラッシュとドットがアンダースコアに置き換えられた完全な名前空間パスです(例: my_namespace_path

ネームスペース固有の認証情報の設定

きめ細かなアクセス制御を行うには、レジストリの名前空間ごとに固有の認証情報を設定できます:

APIキー(シークレット)signing-token-apikey-{registry}-{namespace}

ユーザー名(環境変数)signing-token-username-{registry}-{namespace}

:画像の場合 us.icr.io/my-namespace/my-app:latest

  • registry: us.icr.io
  • 名前空間: my-namespace
  • APIキーのシークレット: signing-token-apikey-us.icr.io-my_namespace
  • ユーザー名環境変数: signing-token-username-us.icr.io-my_namespace
  • ユーザー名が指定されていない場合、デフォルトは次の通りです: iamapikey

レジストリ固有の認証情報の設定

レジストリ内のすべての名前空間に広くアクセスするには:

APIキー(シークレット)signing-token-apikey-{registry}

ユーザー名(環境変数)signing-token-username-{registry}

:内の任意の画像について us.icr.io

  • APIキーのシークレット: signing-token-apikey-us.icr.io
  • ユーザー名環境変数: signing-token-username-us.icr.io
  • ユーザー名が指定されていない場合、デフォルトは次の通りです: iamapikey

Docker の設定JSONファイルの設定

複数のレジストリの認証情報を含む、 base64-encoded Docker の設定JSONを指定できます:

秘密の名前signing-dockerconfigjson

形式 : Base64-encoded ( Docker の config.json 形式に準拠した JSON):

{
  "auths": {
    "us.icr.io": {
      "username": "iamapikey",
      "password": "your-api-key"
    },
    "us.icr.io/my-namespace": {
      "username": "iamapikey",
      "password": "namespace-specific-key"
    }
  }
}

パイプラインは最も具体的なパスから順に一致させるため、 Docker の設定内で名前空間レベルでの上書きが可能になります。

構成の例

画像については us.icr.io/production/my-app:v1.0.0 をご覧ください:

オプション 1: 名前空間固有(本番環境での使用を推奨)

  • APIキーのシークレット: signing-token-apikey-us.icr.io-production = your-namespace-api-key
  • ユーザー名環境変数(任意): signing-token-username-us.icr.io-production = iamapikey
  • ユーザー名が指定されていない場合、デフォルトでは iamapikey

オプション 2: レジストリ全体

  • APIキーのシークレット: signing-token-apikey-us.icr.io = your-registry-api-key
  • ユーザー名環境変数(任意): signing-token-username-us.icr.io = iamapikey
  • ユーザー名が指定されていない場合、デフォルトでは iamapikey

オプション 3: Docker の設定 JSON

  • 秘密: signing-dockerconfigjson = base64-encoded-docker-config
  • ユーザー名は、 Docker の設定JSONから抽出されます

オプション 4: IBM Cloud デフォルト(ICRの場合は自動)

  • APIキーのシークレット: ibmcloud-api-key = your-ibmcloud-api-key
  • ユーザー名はデフォルトで iamapikey

イメージ以外の成果物の検証

DevSecOps のパイプラインでは、署名付きアーティファクトの署名が検証の対象となります。 このセクションでは、成果物の検証に進む前にユーザーが満たす必要がある前提条件について概説します。

開始前に

イメージ検証を操作する前に、以下の前提条件を満たしていることを確認してください。

  1. パイプライン構成を変更して、検証プロセスを有効にします。 .pipeline-config.yaml ファイルに、以下のコードスニペットを追加してください。

    verify-artifact:
      image: icr.io/continuous-delivery/pipeline/image-signing:1.0.0@sha256:e9d8e354668ba3d40be2aaee08298d2aa7f0e1c8a1829cca4094ec93830e3e6a
      image_pull_policy: IfNotPresent
      abort_on_failure: false
      dind: true
      script: |
        #!/usr/bin/env bash
        source /opt/commons/verify-artifact/verify_non_image_artifact.sh
    
  2. 成果物をダウンロードして、パイプライン・ランタイムに保管します。

    verify-artifact ステージを呼び出す前に、必要な成果物をダウンロードして Cocoa ランタイムに保管します。 デフォルトでは、 list_artifacts コレクションにリストされているすべてのアーティファクトが検証されます。 skip-sign-artifact-type テキストプロパティを使用すると、指定されたアーティファクトタイプ(例:log、report、documentation)に対して、イメージの署名検証をスキップできます。 以下のサンプルコードは、 Cloud Object Storage からアーティファクトをダウンロードし、Cocoa ランタイムに保存する方法を示しています:

    #!/usr/bin/env bash
    
    export SECRET_PATH="/config/ibmcloud-api-key"
    . "${ONE_PIPELINE_PATH}"/iam/get_token
    
    SKIP_SIGN_ARTIFACTS_TYPE="$(get_env skip-sign-artifact-type "")"
    IFS=';' read -ra ARTIFACT_LIST <<<"$SKIP_SIGN_ARTIFACTS_TYPE"
    
    list_artifacts | while IFS= read -r artifact; do
    
       type="$(load_artifact "$artifact" "type")"
    
       if [[ ! "${ARTIFACT_LIST[@]}" =~ "$type" ]] && [[ "$type" != "image" ]]; then
    
          inventory_entry="$(load_artifact "$artifact" "inventory-entry")"
          inventory_entry=${inventory_entry///artifacts/$WORKSPACE}
          artifact_url=$(jq -r .'app_artifacts.artifact_url' ${inventory_entry})
          echo "downloading the artifact  ${artifact_url}"
          curl -H "Authorization: bearer ${IAM_ACCESS_TOKEN}" --output outputfile ${artifact_url}
          save_file "${artifact}" outputfile
    
       fi
    done
    

成果物の検証

成果物検証プロセスでは、ユーザー提供の GPG 公開鍵が GPG 鍵リングにインポートされます。 list_artifacts コレクションにリストされているすべての成果物が検証されます。 このステージでは、インベントリーから署名を取得し、以下の検証を実行します。

gpg --verify  "${signature}" "${artifactName}"

署名が有効であり、提供された公開鍵を使用して検証された場合、ステージは証拠を記録し、ステージに成功のマークを付けます。