Binary Auth によるデプロイのゲーティング

1. はじめに

Binary Authorization は、Google Kubernetes Engine(GKE)や Cloud Run に、信頼できるコンテナ イメージのみをデプロイするためのデプロイ時のセキュリティ コントロールです。Binary Authorization を使用すると、開発プロセスにおいて信頼できる機関によるイメージへの署名を必須にして、デプロイ時にその署名を検証できます。検証プロセスを適用することで、適切であると認められたイメージのみがビルドとリリースのプロセスに組み込まれるため、コンテナ環境をより厳格に管理できます。

次の図は、Binary Authorization と Cloud Build の統合環境のコンポーネントを表しています。

Cloud Build と Binary Authorization による証明書パイプライン。**図 1.**Binary Authorization 証明書を作成する Cloud Build パイプライン。

このパイプラインでは次のことが行われます。

  1. コンテナ イメージをビルドするためのコードが Cloud Source Repositories などのソース リポジトリに push されます。
  2. 継続的インテグレーション(CI)ツールである Cloud Build が、コンテナをビルドしてテストします。
  3. ビルドがコンテナ イメージを Container Registry に push するか、ビルドされたイメージを格納する別のレジストリに push します。
  4. 暗号鍵ペアの鍵管理を行う Cloud Key Management Service が、コンテナ イメージに署名します。生成された署名は、新しく作成された証明書に保存されます。
  5. デプロイ時に、認証者が鍵ペアの公開鍵を使用して証明書を検証します。Binary Authorization が、コンテナ イメージをデプロイするための署名付き証明書を要求して、ポリシーを適用します。

このラボでは、デプロイされたアーティファクトを保護するためのツールと手法に焦点を当てます。ここでは、作成してからまだ特定の環境にデプロイされていないアーティファクト(コンテナ)に焦点を当てます。

学習内容

  • イメージの署名
  • アドミッション コントロール ポリシー
  • スキャンされたイメージへの署名
  • 署名付きイメージの承認
  • 署名なしイメージのブロック

2. 設定と要件

セルフペース型の環境設定

  1. Google Cloud Console にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Gmail アカウントも Google Workspace アカウントもまだお持ちでない場合は、アカウントを作成してください。

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列です。この設定はいつでも変更できます。
  • プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud コンソールでは一意の文字列が自動生成されます。通常は、この内容を意識する必要はありません。ほとんどの Codelab では、プロジェクト ID(通常は PROJECT_ID と識別されます)を参照する必要があります。生成された ID が好みではない場合は、ランダムに別の ID を生成できます。または、ご自身で試して、利用可能かどうかを確認することもできます。このステップ以降は変更できず、プロジェクトを通して同じ ID になります。
  • なお、3 つ目の値として、一部の API が使用するプロジェクト番号があります。これら 3 つの値について詳しくは、こちらのドキュメントをご覧ください。
  1. 次に、Cloud のリソースや API を使用するために、Cloud コンソールで課金を有効にする必要があります。この Codelab の操作をすべて行って、費用が生じたとしても、少額です。このチュートリアルの終了後に請求が発生しないようにリソースをシャットダウンするには、作成したリソースを削除するか、プロジェクト全体を削除します。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。

環境設定

Cloud Shell で、プロジェクトのプロジェクト ID とプロジェクト番号を設定します。これらの情報は、PROJECT_ID 変数と PROJECT_ID 変数として保存します。

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
    --format='value(projectNumber)')

サービスを有効化する

必要なサービスをすべて有効にします。

gcloud services enable \
  cloudkms.googleapis.com \
  cloudbuild.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  artifactregistry.googleapis.com \
  containerscanning.googleapis.com \
  ondemandscanning.googleapis.com \
  binaryauthorization.googleapis.com 

Artifact Registry リポジトリを作成する

このラボでは、Artifact Registry を使用してイメージを保存し、スキャンします。次のコマンドを使用してリポジトリを作成します。

gcloud artifacts repositories create artifact-scanning-repo \
  --repository-format=docker \
  --location=us-central1 \
  --description="Docker repository"

Artifact Registry にアクセスするときに gcloud 認証情報を使用するよう、Docker を構成します。

gcloud auth configure-docker us-central1-docker.pkg.dev

作業ディレクトリを作成して移動する

mkdir vuln-scan && cd vuln-scan

サンプル イメージを定義する

次の内容で Dockerfile という名前のファイルを作成します。

cat > ./Dockerfile << EOF
from python:3.8-slim  

# App
WORKDIR /app
COPY . ./

RUN pip3 install Flask==2.1.0
RUN pip3 install gunicorn==20.1.0

CMD exec gunicorn --bind :\$PORT --workers 1 --threads 8 main:app

EOF

次の内容で main.py という名前のファイルを作成します。

cat > ./main.py << EOF
import os
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    name = os.environ.get("NAME", "Worlds")
    return "Hello {}!".format(name)

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
EOF

イメージをビルドして AR に push する

Cloud Build を使用してコンテナをビルドし、Artifact Registry に自動的に push します。

gcloud builds submit . -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image

3. イメージの署名

認証者とは

認証者

  • このユーザーまたはプロセスは、システムの信頼チェーンにおけるリンクの 1 つを担当します。
  • 暗号鍵を保持し、承認プロセスに合格したイメージに署名する役割を担います。
  • ポリシー作成者がポリシーの抽象的な概要を決定するのに対し、認証者はポリシーの特定の側面を具体的に適用します。
  • 認証者は、QA テスターやマネージャーなどの実在する人物の場合もあれば、CI システムの bot の場合もあります。
  • システムのセキュリティは認証者の信頼性に依存するため、秘密鍵を安全に保管することが重要です。

これらのロールは、組織内の個人またはチームを表すことができます。本番環境では、ロールは別の Google Cloud Platform(GCP)プロジェクトで管理される可能性が高く、リソースへのアクセスは Cloud IAM を使用して制限された状態で共有されます。

a37eb2ed54b9c2eb.png

Binary Authorization の認証者は Cloud Container Analysis API の上に実装されているため、先にその API の仕組みを説明することが重要です。Container Analysis API は、メタデータを特定のコンテナ イメージに関連付けることができるように設計されています。

たとえば、Heartbleed 脆弱性を追跡するためのメモを作成できます。セキュリティ ベンダーは、コンテナ イメージの脆弱性をテストするスキャナを作成し、侵害された各コンテナに関連付けられるオカレンスを作成します。

208aa5ebc53ff2b3.png

Container Analysis は脆弱性の追跡に加えて、汎用メタデータ API の役割を担うように設計されています。Binary Authorization は Container Analysis を使用して、検証するコンテナ イメージに署名を関連付けます。Container Analysis のメモは単一の認証者を表すために使用されます。その認証者が承認した各コンテナに対してオカレンスが作成され、関連付けられます。

Binary Authorization API は「認証者」と「証明書」のコンセプトを使用しますが、これらはそれぞれ Container Analysis API のメモとオカレンスを使用して実装されます。

63a701bd0057ea17.png

認証者のメモを作成する

認証者のメモは、適用される署名のタイプを示すラベルとして機能する、ほんの小さなデータです。たとえば、脆弱性スキャンを示すメモもあれば、QA 承認に使用されるメモもあります。メモは署名プロセス中に参照されます。

919f997db0ffb881.png

メモを作成する

cat > ./vulnz_note.json << EOM
{
  "attestation": {
    "hint": {
      "human_readable_name": "Container Vulnerabilities attestation authority"
    }
  }
}
EOM

メモを保存する

NOTE_ID=vulnz_note

curl -vvv -X POST \
    -H "Content-Type: application/json"  \
    -H "Authorization: Bearer $(gcloud auth print-access-token)"  \
    --data-binary @./vulnz_note.json  \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/?noteId=${NOTE_ID}"

メモを確認する

curl -vvv  \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/${NOTE_ID}"

これで、メモが Container Analysis API に保存されました。

認証者の作成

認証者は、実際のイメージ署名プロセスを実施するために使用され、後で検証するためにメモのオカレンスをイメージに添付します。認証者を利用するには、メモを Binary Authorization に登録する必要もあります。

ed05d438c79b654d.png

認証者を作成

ATTESTOR_ID=vulnz-attestor

gcloud container binauthz attestors create $ATTESTOR_ID \
    --attestation-authority-note=$NOTE_ID \
    --attestation-authority-note-project=${PROJECT_ID}

認証者を確認する

gcloud container binauthz attestors list

最後の行で NUM_PUBLIC_KEYS: 0 と表示されますが、鍵は後の手順で指定します。

また、イメージを生成するビルドを実行すると、Cloud Build によって built-by-cloud-build 認証者が自動的に作成されます。したがって、上記のコマンドは 2 つの認証者(vulnz-attestorbuilt-by-cloud-build)を返します。イメージが正常にビルドされると、Cloud Build が自動的に署名して証明書を作成します。

IAM ロールの追加

Binary Authorization サービス アカウントには、証明書のメモを閲覧する権限が必要です。次の API 呼び出しを使用してアクセス権を付与します。

PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}"  --format="value(projectNumber)")

BINAUTHZ_SA_EMAIL="service-${PROJECT_NUMBER}@gcp-sa-binaryauthorization.iam.gserviceaccount.com"


cat > ./iam_request.json << EOM
{
  'resource': 'projects/${PROJECT_ID}/notes/${NOTE_ID}',
  'policy': {
    'bindings': [
      {
        'role': 'roles/containeranalysis.notes.occurrences.viewer',
        'members': [
          'serviceAccount:${BINAUTHZ_SA_EMAIL}'
        ]
      }
    ]
  }
}
EOM

このファイルを使用して IAM ポリシーを作成します。

curl -X POST  \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    --data-binary @./iam_request.json \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/${NOTE_ID}:setIamPolicy"

KMS 鍵を追加する

1e3af7c177f7a311.png

この認証者を使用するには、コンテナ イメージの署名に使用できる暗号鍵ペアを作成する権限が必要です。これは、Google Cloud Key Management Service(KMS)を使用して行うことができます。

まず、新しい鍵について記述する環境変数を追加します。

KEY_LOCATION=global
KEYRING=binauthz-keys
KEY_NAME=codelab-key
KEY_VERSION=1

鍵のセットを保持するキーリングを作成する

gcloud kms keyrings create "${KEYRING}" --location="${KEY_LOCATION}"

認証者用の新しい非対称署名鍵ペアを作成します。

gcloud kms keys create "${KEY_NAME}" \
    --keyring="${KEYRING}" --location="${KEY_LOCATION}" \
    --purpose asymmetric-signing   \
    --default-algorithm="ec-sign-p256-sha256"

Google Cloud コンソールの KMS ページに鍵が表示されます。

次に、gcloud binauthz コマンドを使用して、鍵を認証者に関連付けます。

gcloud beta container binauthz attestors public-keys add  \
    --attestor="${ATTESTOR_ID}"  \
    --keyversion-project="${PROJECT_ID}"  \
    --keyversion-location="${KEY_LOCATION}" \
    --keyversion-keyring="${KEYRING}" \
    --keyversion-key="${KEY_NAME}" \
    --keyversion="${KEY_VERSION}"

権限リストをもう一度出力すると、登録された鍵が表示されます。

gcloud container binauthz attestors list

署名付き証明書を作成する

イメージに署名できる機能の構成はできました。次に、すでに作成した認証者を使用して、作業中のコンテナ イメージに署名します。

858d7e6feeb6f159.png

証明書には、認証者が特定のコンテナ イメージを検証し、クラスタで安全に実行できることを示す暗号署名が含まれている必要があります。証明するコンテナ イメージを指定するには、そのダイジェストを特定する必要があります。

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image

DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:latest \
    --format='get(image_summary.digest)')

これで、gcloud を使用して証明書を作成できるようになりました。このコマンドは、署名に使用する鍵の詳細と、承認する特定のコンテナ イメージを取得します。

gcloud beta container binauthz attestations sign-and-create  \
    --artifact-url="${CONTAINER_PATH}@${DIGEST}" \
    --attestor="${ATTESTOR_ID}" \
    --attestor-project="${PROJECT_ID}" \
    --keyversion-project="${PROJECT_ID}" \
    --keyversion-location="${KEY_LOCATION}" \
    --keyversion-keyring="${KEYRING}" \
    --keyversion-key="${KEY_NAME}" \
    --keyversion="${KEY_VERSION}"

Container Analysis の観点では、これにより新しいオカレンスが作成され、それが認証者のメモに添付されます。すべてが想定どおりに動作したことを確認するために、証明書を一覧表示できます。

gcloud container binauthz attestations list \
   --attestor=$ATTESTOR_ID --attestor-project=${PROJECT_ID}

4. アドミッション コントロール ポリシー

Binary Authorization は、コンテナ イメージの実行を許可する前にルールを検証できるようにする、GKE と Cloud Run の機能です。どのイメージ実行リクエストに対しても検証が実施されます。これは、信頼できる CI / CD パイプラインからのリクエストであっても、ユーザーが手動でイメージをデプロイしようとするリクエストであっても同様です。この機能により、CI / CD パイプラインのチェックだけでは実現できない、より効果的なランタイム環境の保護が可能になります。

この機能を理解するために、デフォルトの GKE ポリシーを変更して厳格な認可ルールを適用します。

GKE クラスタを作成する

Binary Authorization を有効にした GKE クラスタを作成します。

gcloud beta container clusters create binauthz \
    --zone us-central1-a  \
    --binauthz-evaluation-mode=PROJECT_SINGLETON_POLICY_ENFORCE

Cloud Build がこのクラスタにデプロイできるようにします。

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/container.developer"

すべて許可のポリシー

まず、デフォルトのポリシーの状態と、任意のイメージをデプロイできることを確認します。

  1. 既存のポリシーを確認する
gcloud container binauthz policy export
  1. 適用ポリシーが ALWAYS_ALLOW に設定されていることを確認します。

evaluationMode: ALWAYS_ALLOW

  1. Sample をデプロイして、任意のイメージをデプロイできることを確認する
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. デプロイが正常に動作したことを確認する
kubectl get pods

次のような出力が表示されます。

161db370d99ffb13.png

  1. デプロイメントの削除
kubectl delete pod hello-server

すべて拒否のポリシー

次に、すべてのイメージを拒否するようにポリシーを更新します。

  1. 現在のポリシーを編集可能なファイルにエクスポートする
gcloud container binauthz policy export  > policy.yaml
  1. ポリシーの変更

テキスト エディタで、evaluationMode を ALWAYS_ALLOW から ALWAYS_DENY に変更します。

edit policy.yaml

ポリシーの YAML ファイルが次のように表示されます。

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_DENY
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy

このポリシーは比較的シンプルです。globalPolicyEvaluationMode 行は、Google によって定義されたグローバル ポリシーを拡張することを宣言しています。これにより、すべての公式 GKE コンテナがデフォルトで実行されます。また、他のすべての Pod が拒否されることを示す defaultAdmissionRule を宣言しています。アドミッション ルールには enforcementMode 行が含まれています。これは、このルールに準拠していないすべての Pod がブロックされ、クラスタで実行されないことを示しています。

より複雑なポリシーを構築する方法については、Binary Authorization のドキュメントをご覧ください。

657752497e59378c.png

  1. ターミナルを開いて新しいポリシーを適用し、変更が反映されるまで数秒待ちます。
gcloud container binauthz policy import policy.yaml
  1. サンプル ワークロードのデプロイを試す
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. デプロイが失敗し、次のメッセージが表示される
Error from server (VIOLATES_POLICY): admission webhook "imagepolicywebhook.image-policy.k8s.io" denied the request: Image gcr.io/google-samples/hello-app:1.0 denied by Binary Authorization default admission rule. Denied by always_deny admission rule

ポリシーを元に戻してすべてを許可する

次のセクションに進む前に、ポリシーの変更を元に戻してください。

  1. ポリシーの変更

テキスト エディタで、evaluationMode を ALWAYS_DENY から ALWAYS_ALLOW に変更します。

edit policy.yaml

ポリシーの YAML ファイルが次のように表示されます。

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_ALLOW
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. 元に戻したポリシーを適用する
gcloud container binauthz policy import policy.yaml

5. スキャンされたイメージへの署名

これで、イメージ署名を有効にし、認証者を手動で使用してサンプル イメージに署名できました。しかし実際には、CI/CD パイプラインなどの自動化されたプロセスで証明書を適用する必要があるでしょう。

このセクションでは、イメージを自動的に証明するように Cloud Build を構成します。

ロール

Cloud Build サービス アカウントに Binary Authorization 認証者 / 閲覧者のロールを追加します。

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/binaryauthorization.attestorsViewer

Cloud KMS 暗号鍵の署名者 / 検証者ロールを Cloud Build サービス アカウントに追加します。(KMS ベースの署名)

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/cloudkms.signerVerifier

Cloud Build サービス アカウントに Container Analysis メモ添付者のロールを追加します。

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/containeranalysis.notes.attacher

Cloud Build サービス アカウントにアクセス権を付与する

Cloud Build には、On-Demand Scanning API へのアクセス権が必要です。次のコマンドを使用してアクセス権を付与します。

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/iam.serviceAccountUser"
        
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/ondemandscanning.admin"

Cloud Build のカスタムビルド ステップを準備する

Cloud Build のカスタムビルド ステップを使用して、証明プロセスを簡素化します。Google は、プロセスを効率化するためのヘルパー関数を含むこのカスタム ビルドステップを提供しています。使用する前に、カスタム ビルドステップのコードをコンテナにビルドし、Cloud Build に push する必要があります。この操作を行うには、次のコマンドを実行します。

git clone https://github.com/GoogleCloudPlatform/cloud-builders-community.git
cd cloud-builders-community/binauthz-attestation
gcloud builds submit . --config cloudbuild.yaml
cd ../..
rm -rf cloud-builders-community

cloudbuild.yaml に署名ステップを追加する

このステップでは、Cloud Build パイプラインに証明書ステップを追加します。

  1. 以下の署名の手順を確認します。

確認のみ。コピーしないでください

#Sign the image only if the previous severity check passes
- id: 'create-attestation'
  name: 'gcr.io/${PROJECT_ID}/binauthz-attestation:latest'
  args:
    - '--artifact-url'
    - 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image'
    - '--attestor'
    - 'projects/${PROJECT_ID}/attestors/$ATTESTOR_ID'
    - '--keyversion'
    - 'projects/${PROJECT_ID}/locations/$KEY_LOCATION/keyRings/$KEYRING/cryptoKeys/$KEY_NAME/cryptoKeyVersions/$KEY_VERSION'
  1. 次の完全なパイプラインを含む cloudbuild.yaml ファイルを作成します。
cat > ./cloudbuild.yaml << EOF
steps:

# build
- id: "build"
  name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', '.']
  waitFor: ['-']

#Run a vulnerability scan at _SECURITY level
- id: scan
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
    (gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --location us \
    --format="value(response.scan)") > /workspace/scan_id.txt

#Analyze the result of the scan
- id: severity check
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
      gcloud artifacts docker images list-vulnerabilities \$(cat /workspace/scan_id.txt) \
      --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq CRITICAL; \
      then echo "Failed vulnerability check for CRITICAL level" && exit 1; else echo "No CRITICAL vulnerability found, congrats !" && exit 0; fi

#Retag
- id: "retag"
  name: 'gcr.io/cloud-builders/docker'
  args: ['tag',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#pushing to artifact registry
- id: "push"
  name: 'gcr.io/cloud-builders/docker'
  args: ['push',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#Sign the image only if the previous severity check passes
- id: 'create-attestation'
  name: 'gcr.io/${PROJECT_ID}/binauthz-attestation:latest'
  args:
    - '--artifact-url'
    - 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good'
    - '--attestor'
    - 'projects/${PROJECT_ID}/attestors/$ATTESTOR_ID'
    - '--keyversion'
    - 'projects/${PROJECT_ID}/locations/$KEY_LOCATION/keyRings/$KEYRING/cryptoKeys/$KEY_NAME/cryptoKeyVersions/$KEY_VERSION'



images:
  - us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good
EOF

ビルドを実行する

gcloud builds submit

Cloud Build の履歴でビルドを確認する

Cloud Console で Cloud Build の履歴ページを開き、最新のビルドと、ビルドステップの実行が成功したことを確認します。

6. 署名付きイメージの承認

このセクションでは、Binary Authorization を使用して、イメージに脆弱性スキャンの署名があることを検証してからイメージの実行を許可するように、GKE を更新します。

d5c41bb89e22fd61.png

GKE ポリシーを更新して証明書を必須にする

GKE BinAuth ポリシーに clusterAdmissionRules を追加して、認証者によるイメージへの署名を必須にします。

現在、クラスタは 1 つのルール(公式リポジトリのコンテナを許可し、その他をすべて拒否する)を含むポリシーを実行しています。

次のコマンドを使用して、更新した構成でポリシーを上書きします。

COMPUTE_ZONE=us-central1-a

cat > binauth_policy.yaml << EOM
defaultAdmissionRule:
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
  evaluationMode: ALWAYS_DENY
globalPolicyEvaluationMode: ENABLE
clusterAdmissionRules:
  ${COMPUTE_ZONE}.binauthz:
    evaluationMode: REQUIRE_ATTESTATION
    enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
    requireAttestationsBy:
    - projects/${PROJECT_ID}/attestors/vulnz-attestor
EOM

ディスクに updated_policy.yaml という新しいファイルが作成されます。これにより、デフォルトのルールですべてのイメージが拒否されるのではなく、検証のためにまず認証者を確認するようになります。

822240fc0b02408e.png

新しいポリシーを Binary Authorization にアップロードします。

gcloud beta container binauthz policy import binauth_policy.yaml

署名付きイメージをデプロイする

署名付きイメージのイメージ ダイジェストを取得する

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image


DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:good \
    --format='get(image_summary.digest)')

Kubernetes 構成でダイジェストを使用する

cat > deploy.yaml << EOM
apiVersion: v1
kind: Service
metadata:
  name: deb-httpd
spec:
  selector:
    app: deb-httpd
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deb-httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deb-httpd
  template:
    metadata:
      labels:
        app: deb-httpd
    spec:
      containers:
      - name: deb-httpd
        image: ${CONTAINER_PATH}@${DIGEST}
        ports:
        - containerPort: 8080
        env:
          - name: PORT
            value: "8080"

EOM

アプリを GKE にデプロイします。

kubectl apply -f deploy.yaml

コンソールでワークロードを確認し、イメージのデプロイが成功したことを確認します。

7. 署名なしイメージのブロック

イメージをビルドする

このステップでは、ローカル Docker を使用してイメージをビルドし、ローカル キャッシュに保存します。

docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:bad .

署名なしイメージをリポジトリに push する

docker push us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:bad

署名なしイメージのイメージ ダイジェストを取得する

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image


DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:bad \
    --format='get(image_summary.digest)')

Kubernetes 構成でダイジェストを使用する

cat > deploy.yaml << EOM
apiVersion: v1
kind: Service
metadata:
  name: deb-httpd
spec:
  selector:
    app: deb-httpd
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deb-httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deb-httpd
  template:
    metadata:
      labels:
        app: deb-httpd
    spec:
      containers:
      - name: deb-httpd
        image: ${CONTAINER_PATH}@${DIGEST}
        ports:
        - containerPort: 8080
        env:
          - name: PORT
            value: "8080"

EOM

アプリの GKE へのデプロイを試みます。

kubectl apply -f deploy.yaml

コンソールでワークロードで、デプロイが拒否されたことを示すエラーを確認します。

No attestations found that were valid and signed by a key trusted by the attestor

8. 完了

お疲れさまでした。これでこの Codelab は終了です。

学習した内容

  • イメージの署名
  • アドミッション コントロール ポリシー
  • スキャンされたイメージへの署名
  • 署名付きイメージの承認
  • 署名なしイメージのブロック

次のステップ:

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。

プロジェクトの削除

課金をなくす最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。

最終更新日: 2023 年 3 月 21 日