この Codelab について
1. はじめに
Container Analysis は、コンテナの脆弱性スキャンとメタデータ ストレージを提供します。スキャン サービスは、Artifact Registry と Container Registry 内のイメージに対して脆弱性スキャンを実行し、生成されたメタデータを保存し、API を通じて利用できるようにします。メタデータ ストレージを使用すると、脆弱性スキャン、Google Cloud サービス、サードパーティ プロバイダなど、さまざまなソースからの情報を保存できます。
脆弱性スキャンは、自動またはオンデマンドで起動できます。
- 自動スキャンを有効にすると、Artifact Registry または Container Registry に新しいイメージを push するたびに、自動的にスキャンがトリガーされます。脆弱性情報は、新しい脆弱性が発見されると継続的に更新されます。
- オンデマンド スキャンが有効になっている場合は、ローカル イメージ、または Artifact Registry または Container Registry 内のイメージをスキャンするコマンドを実行する必要があります。オンデマンド スキャンでは、コンテナを柔軟にスキャンできます。たとえば、レジストリに保存する前に、ローカルで作成されたイメージをスキャンして脆弱性を修正できます。スキャンの結果は、スキャン完了後最大 48 時間利用できます。スキャン後、脆弱性情報は更新されません。
Container Analysis を CI/CD パイプラインに統合すると、そのメタデータに基づいて意思決定を行うことができます。たとえば、Binary Authorization を使用して、信頼できるレジストリにあるコンプライアンス イメージのデプロイのみを許可するデプロイ ポリシーを作成できます。
学習内容
- 自動スキャンを有効にする方法
- オンデマンド スキャンの実行方法
- ビルド パイプラインにスキャンを統合する方法
- 承認済みのイメージに署名する方法
- GKE アドミッション コントローラを使用してイメージをブロックする方法
- 署名付きの承認されたイメージのみを許可するように GKE を構成する方法
2. 設定と要件
セルフペース型の環境設定
- Google Cloud Console にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Gmail アカウントも Google Workspace アカウントもまだお持ちでない場合は、アカウントを作成してください。
- プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列です。この値はいつでも更新できます。
- プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud コンソールでは一意の文字列が自動生成されます。通常、それが何であるかは関係ありません。ほとんどの Codelab では、プロジェクト ID を参照する必要があります(通常は
PROJECT_ID
として識別されます)。生成された ID が気に入らない場合は、別のランダムな ID を生成できます。または、ご自身でお試しになることもできます。このステップを終えた後は変更できず、プロジェクト期間中は維持されます。 - なお、3 つ目の値は、一部の API で使用される [プロジェクト番号] です。これら 3 つの値について詳しくは、こちらのドキュメントをご覧ください。
- 次に、Cloud のリソースや API を使用するために、Cloud コンソールで課金を有効にする必要があります。この Codelab の操作をすべて行って、費用が生じたとしても、少額です。このチュートリアルの終了後に課金が発生しないようにリソースをシャットダウンするには、作成したリソースを削除するか、プロジェクト全体を削除します。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。
Cloudshell エディタを起動する
このラボは、Google Cloud Shell エディタで使用するように設計、テストされています。エディタにアクセスするには、
- https://console.cloud.google.com から Google プロジェクトにアクセスします。
- 右上にある Cloud Shell エディタのアイコンをクリックします。
- ウィンドウの下部に新しいペインが開きます
環境設定
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
3. 自動スキャン
新しいイメージを Artifact Registry または Container Registry に push するたびに、アーティファクト スキャンが自動的にトリガーされます。脆弱性情報は、新しい脆弱性が発見されると継続的に更新されます。このセクションでは、イメージを Artifact Registry に push し、結果を確認します。
作業ディレクトリを作成してそのディレクトリに移動する
mkdir vuln-scan && cd vuln-scan
サンプル画像を定義する
次の内容のファイルを Dockerfile という名前で作成します。
cat > ./Dockerfile << EOF
FROM gcr.io/google-appengine/debian9@sha256:ebffcf0df9aa33f342c4e1d4c8428b784fc571cdf6fbab0b31330347ca8af97a
# System
RUN apt update && apt install python3-pip -y
# App
WORKDIR /app
COPY . ./
RUN pip3 install Flask==1.1.4
RUN pip3 install gunicorn==20.1.0
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 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 する。イメージの bad
タグに注目してください。これは、後のステップで識別するのに役立ちます。
gcloud builds submit . -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:bad
画像の詳細を確認
ビルドプロセスが完了したら、Artifact Registry ダッシュボードでイメージと脆弱性の結果を確認します。
- Cloud コンソールで Artifact Registry を開きます。
- artifact-scanning-repo をクリックして内容を表示します
- 画像の詳細をクリックします。
- イメージの最新ダイジェストをクリックします
- スキャンが完了したら、イメージの [脆弱性] タブをクリックします。
[脆弱性] タブに、ビルドしたイメージの自動スキャンの結果が表示されます。
デフォルトでは、スキャンの自動化が有効になっています。Artifact Registry の設定を確認し、自動スキャンのオンとオフを切り替える方法を確認する。
4. オンデマンド スキャン
イメージをリポジトリに push する前にスキャンの実行が必要になるシナリオはさまざまです。たとえば、コンテナ デベロッパーはコードをソース管理に push する前に、イメージをスキャンして問題を修正できます。以下の例では、結果をローカルでビルドして分析してから、結果を処理します。
イメージをビルドする
このステップでは、ローカル Docker を使用してローカル キャッシュにイメージをビルドします。
docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image .
画像をスキャン
イメージがビルドされたら、イメージのスキャンをリクエストします。スキャンの結果はメタデータ サーバーに保存されます。ジョブはメタデータ サーバー内の結果の場所を指定して完了します。
gcloud artifacts docker images scan \
us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
--format="value(response.scan)" > scan_id.txt
出力ファイルの確認
前のステップの出力(scan_id.txt ファイルに保存されていた)を確認します。メタデータ サーバーでのスキャン結果のレポートの場所に注目してください。
cat scan_id.txt
詳細なスキャン結果を確認する
実際のスキャン結果を表示するには、出力ファイルに記載されているレポートの場所に対して list-vulnerabilities
コマンドを使用します。
gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt)
出力には、イメージ内のすべての脆弱性に関する大量のデータが含まれます。
重大な問題を報告する
ユーザーがレポートに保存されたデータを直接使用することはほとんどありません。通常、結果は自動プロセスによって使用されます。以下のコマンドを使用してレポートの詳細を確認し、重大な脆弱性が見つかった場合はログに記録します。
export SEVERITY=CRITICAL
gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt) --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq ${SEVERITY}; then echo "Failed vulnerability check for ${SEVERITY} level"; else echo "No ${SEVERITY} Vulnerabilities found"; fi
このコマンドの出力は、
Failed vulnerability check for CRITICAL level
5. パイプライン スキャンの構築
このセクションでは、コンテナ イメージをビルドしてスキャンし、結果を評価する自動ビルド パイプラインを作成します。重大な脆弱性が見つからなければ、イメージがリポジトリに push されます。重大な脆弱性が見つかった場合、ビルドは失敗して終了します。
Cloud Build サービス アカウントにアクセス権を付与する
Cloud Build がオンデマンド スキャン 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 パイプラインを作成する
次のコマンドにより、自動プロセスに使用される 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']
images:
- us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image
EOF
CI パイプラインを実行する
重大度が「CRITICAL」の脆弱性が見つかった場合、ビルドが中断されることを確認するため、処理のためにビルドを送信します。
gcloud builds submit
ビルドエラーを確認
イメージに重大な脆弱性が含まれているため、送信したビルドは失敗します。
Cloud Build の履歴ページでビルドエラーを確認する
脆弱性の修正
重大な脆弱性を含まないベースイメージを使用するように Dockerfile を更新する。
次のコマンドを使用して、Debian 10 イメージを使用するように 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
適切なイメージで CI プロセスを実行する
そのビルドを送信して処理し、重大な脆弱性が見つからなくてもビルドが成功することを確認します。
gcloud builds submit
ビルドの成功を確認
更新されたイメージには重大な脆弱性がないため、送信したビルドは成功します。
Cloud Build の履歴ページでビルドの成功を確認する
スキャン結果の確認
Artifact Registry で正常なイメージを確認する
- Cloud コンソールで Artifact Registry を開きます。
- artifact-scanning-repo をクリックして内容を表示します
- 画像の詳細をクリックします。
- イメージの最新ダイジェストをクリックします
- イメージの脆弱性タブをクリックする
6. イメージに署名する
認証者のメモを作成する
認証者メモは、適用する署名タイプのラベルとして機能する小さなデータです。たとえば、脆弱性スキャンと QA の承認を示すメモがあります。このメモは署名プロセス中に参照されます。
メモを作成する
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}"
認証者の作成
認証者は実際のイメージ署名プロセスを実行するために使用され、後で検証できるようにメモのオカレンスをイメージに添付します。後で使用するために認証者を作成します。
認証者を作成
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-attestor
と built-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 鍵の追加
認証者には、メモを添付して検証可能な署名を提供するために暗号鍵が必要です。このステップでは、Cloud Build が後でアクセスできるように、鍵を作成して 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
署名付き構成証明の作成
この時点で、イメージに署名できる機能が構成されました。前に作成した認証者を使用して、作業中のコンテナ イメージに署名します
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}
7. Cloud Build を使用した署名
イメージ署名を有効にし、認証者を手動で使用してサンプル イメージに署名しました。実際には、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 でカスタムビルド ステップを使用して、証明書プロセスを簡素化します。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 パイプラインに証明書ステップを追加します。
- 追加する新しいステップを確認します。
審査のみ。コピーしない
#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'
- 更新した完全なパイプラインで 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 コンソールを開いて Cloud Build の履歴ページを開き、最新のビルドとビルドステップが正常に実行されたことを確認します。
8. アドミッション コントロール ポリシー
Binary Authorization は GKE と Cloud Run の機能で、コンテナ イメージの実行を許可する前にルールを検証できます。検証は、信頼できる CI/CD パイプラインからのリクエストでも、ユーザーが手動でイメージをデプロイしようとしている場合でも、イメージを実行するあらゆるリクエストに対して実行されます。この機能により、CI/CD パイプラインのチェックのみの場合よりも効果的にランタイム環境を保護できます。
この機能を理解するために、デフォルトの GKE ポリシーを変更して厳格な認可ルールを適用します。
GKE クラスタを作成する
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"
すべて許可ポリシー
まず、デフォルトのポリシーの状態と任意のイメージのデプロイが可能であることを確認します。
- 既存のポリシーを確認する
gcloud container binauthz policy export
- 適用ポリシーが
ALWAYS_ALLOW
に設定されていることに注意してください。
evaluationMode: ALWAYS_ALLOW
- サンプルをデプロイして、何でもデプロイできることを確認する
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
- デプロイが成功したことを確認する
kubectl get pods
次のような出力が表示されます。
- デプロイの削除
kubectl delete pod hello-server
すべて拒否ポリシー
次に、すべてのイメージを禁止するようにポリシーを更新します。
- 現在のポリシーを編集可能なファイルにエクスポートします
gcloud container binauthz policy export > policy.yaml
- ポリシーの変更
テキスト エディタで、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
- ターミナルを開いて新しいポリシーを適用し、変更が反映されるまで数秒待ちます。
gcloud container binauthz policy import policy.yaml
- サンプル ワークロードのデプロイを試す
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
- デプロイに失敗し、次のメッセージが表示される
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
ポリシーをすべて許可するように元に戻します。
次のセクションに進む前に、必ずポリシーの変更を元に戻してください
- ポリシーの変更
テキスト エディタで、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
- 元に戻したポリシーを適用する
gcloud container binauthz policy import policy.yaml
9. GKE で脆弱性をブロックする
このセクションでは、CI/CD パイプラインを Cloud Build に実装し、イメージのスキャンと脆弱性のチェックを行ってから、イメージに署名してデプロイを試みることで、これまでに学んだことを組み合わせます。GKE は Binary Authorization を使用して、イメージの実行を許可する前に、脆弱性スキャンによる署名がイメージにあることを確認します。
証明書を要求するように GKE ポリシーを更新する
GKE BinAuth ポリシーに clusterAdmissionRules を追加して、認証者によるイメージへの署名を必須にする
次のコマンドを使用して、更新した構成でポリシーを上書きします。
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
ポリシーを適用する
gcloud beta container binauthz policy import binauth_policy.yaml
未署名のイメージのデプロイを試行する
次のコマンドを使用して、先ほどビルドしたアプリケーションのデプロイ記述子を作成します。ここで使用するイメージは、前にビルドしたイメージで、重大な脆弱性があり、署名済み証明書は含まれていません。
GKE アドミッション コントローラは、署名を一貫して検証するために、デプロイされる正確なイメージを知っている必要があります。これを実現するには、イメージ ダイジェストと単純なタグを使用する必要があります。
不正なイメージのイメージ ダイジェストを取得する
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
署名付きイメージをデプロイする
不正なイメージのイメージ ダイジェストを取得する
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
コンソールでワークロードを確認し、イメージが正常にデプロイされたことを確認します。
10. お疲れさまでした
お疲れさまでした。これでこの Codelab は終了です。
学習した内容
- 自動スキャンを有効にする方法
- オンデマンド スキャンの実行方法
- ビルド パイプラインにスキャンを統合する方法
- 承認済みのイメージに署名する方法
- GKE アドミッション コントローラを使用してイメージをブロックする方法
- 署名付きの承認されたイメージのみを許可するように GKE を構成する方法
次のステップ:
- Cloud Run と Google Kubernetes Engine へのイメージ デプロイの保護 |Cloud Build のドキュメント
- クイックスタート: GKE を使用して Binary Authorization ポリシーを構成する |Google Cloud
クリーンアップ
このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。
プロジェクトの削除
課金を停止する最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。