安全なビルドと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 スキャンは、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) 

出力には、イメージ内のすべての脆弱性に関する大量のデータが含まれています。

重大な問題を報告する

レポートに保存されているデータが直接使用されることはほとんどありません。通常、結果は自動プロセスで使用されます。次のコマンドを使用して、レポートの詳細を読み取り、CRITICAL の脆弱性が見つかった場合はログに記録します。

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. ビルド パイプラインのスキャン

このセクションでは、コンテナ イメージをビルドしてスキャンし、結果を評価する自動化されたビルド パイプラインを作成します。CRITICAL の脆弱性が検出されなかった場合、イメージがリポジトリに push されます。CRITICAL の脆弱性が検出された場合、ビルドは失敗して終了します。

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 パイプラインを作成する

次のコマンドを実行すると、自動プロセスで使用される 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

ビルドの成功を確認する

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

Cloud Build の [履歴] ページで、ビルドが成功していることを確認します。

スキャン結果を確認する

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

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

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-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 鍵を追加する

Attestor は、メモを添付して検証可能な署名を提供するために暗号鍵を必要とします。このステップでは、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 Console で 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. 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
  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 での脆弱性のブロック

このセクションでは、これまでに学習した内容を組み合わせて、イメージをスキャンし、脆弱性をチェックしてからイメージに署名してデプロイを試みる CI/CD パイプラインを Cloud Build で実装します。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 アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。

プロジェクトの削除

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