安全なビルドとCloud Build、Artifact Registry、GKE を使用してデプロイする

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. 設定と要件

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

  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 米ドル分の無料トライアル プログラムをご利用いただけます。

Cloudshell エディタを起動する

このラボは、Google Cloud Shell エディタでの使用を想定して設計、テストされています。エディタにアクセスするには、次の操作を行います。

  1. https://console.cloud.google.com で Google プロジェクトにアクセスします。
  2. 右上にある Cloud Shell エディタ アイコンをクリックします。

8560cc8d45e8c112.png

  1. ウィンドウの下部に新しいペインが開きます。

環境設定

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 ダッシュボードでイメージと脆弱性の結果を確認します。

  1. Cloud コンソールで Artifact Registry を開きます。
  2. artifact-scanning-repo をクリックして内容を表示します。
  3. 画像の詳細をクリックします。
  4. 画像の最新のダイジェストをクリックします。
  5. スキャンが完了したら、イメージの [脆弱性] タブをクリックします。

[脆弱性] タブには、ビルドしたイメージの自動スキャンの結果が表示されます。

361be7b3bf293fca.png

スキャンの自動化はデフォルトで有効になっています。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 されます。CRITICAL の脆弱性が見つかった場合、ビルドは失敗して終了します。

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 履歴] ページでビルドエラーを確認します。

脆弱性を修正する

CRITICAL の脆弱性が含まれていないベースイメージを使用するように 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 プロセスを実行する

ビルドを送信して処理し、重大度 CRITICAL の脆弱性が見つからない場合にビルドが成功することを確認します。

gcloud builds submit

ビルドの成功を確認する

更新されたイメージに CRITICAL 脆弱性がないため、送信したビルドは成功します。

Cloud Build の履歴ページでビルドの成功を確認します。

スキャン結果を確認する

Artifact Registry で正常なイメージを確認する

  1. Cloud コンソールで Artifact Registry を開きます。
  2. artifact-scanning-repo をクリックして内容を表示します。
  3. 画像の詳細をクリックします。
  4. 画像の最新のダイジェストをクリックします。
  5. イメージの [脆弱性] タブをクリックします。

6. イメージの署名

認証者メモを作成する

構成証明ノートとは、適用される署名の種類のラベルとして機能する少量のデータです。たとえば、1 つのメモは脆弱性スキャンを示し、別のメモは 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-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 鍵の追加

構成証明者は、メモを添付して検証可能な署名を提供するために、暗号鍵が必要です。この手順では、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 パイプラインに構成証明ステップを追加します。

  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 コンソールを開き、[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"

すべて許可ポリシー

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

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

evaluationMode: ALWAYS_ALLOW

  1. サンプルをデプロイして、任意のデプロイが可能であることを確認する
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
  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

9. GKE で脆弱性をブロックする

このセクションでは、これまで学習したことを組み合わせて、Cloud Build を使用して CI/CD パイプラインを実装します。このパイプラインは、イメージをスキャンし、脆弱性をチェックしてからイメージに署名してデプロイを試みます。GKE は、Binary Authorization を使用して、イメージの実行を許可する前に、イメージに脆弱性スキャンの署名があることを確認します。

d5c41bb89e22fd61.png

構成証明を必須にするように 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 を構成する方法

次のステップ:

クリーンアップ

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

プロジェクトの削除

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