コンテナビルドの保護

1. はじめに

ead1609267034bf7.png

ソフトウェアの脆弱性とは、偶発的なシステム障害の原因になったり、不正な行為者にソフトウェアを侵害する手段を提供したりする可能性がある弱点です。Container Analysis は、コンテナの脆弱性を検出するために、次の 2 種類の OS スキャンを提供します。

  • On-Demand Scanning API を使用すると、ローカルのコンピュータ上にあるコンテナ イメージ、または Container Registry や Artifact Registry にあるリモートのコンテナ イメージに対して、OS の脆弱性を手動でスキャンできます。
  • Container Scanning API を使用すると、OS の脆弱性検出を自動化し、Container Registry または Artifact Registry にイメージを push するたびにスキャンできます。この API を有効にすると、Go と Java の脆弱性を検出する言語パッケージ スキャンも有効になります。

On-Demand Scanning API を使用すると、ローカルのコンピュータに保存されているイメージ、または Container Registry または Artifact Registry にリモートで保存されているイメージをスキャンできます。これにより、脆弱性をスキャンするコンテナをきめ細かく制御できます。On-Demand Scanning API を使用すると、イメージをレジストリに保存するかどうかを決定する前に、CI/CD パイプラインでイメージをスキャンできます。

学習内容

このラボでは以下を行います。

  • Cloud Build を使用してイメージをビルドする
  • コンテナに Artifact Registry を使用する
  • 自動化された脆弱性スキャンを利用する
  • オンデマンド スキャンを設定する
  • Cloud Build の CICD にイメージ スキャンを追加する

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 

3. Cloud Build を使用したイメージのビルド

このセクションでは、コンテナ イメージをビルドしてスキャンし、結果を評価する自動化されたビルド パイプラインを作成します。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"

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

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

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: ['-']


EOF

CI パイプラインを実行する

ビルドを送信して処理する

gcloud builds submit

ビルドの詳細を確認する

ビルドプロセスが開始されたら、Cloud Build ダッシュボードで進行状況を確認します。

  1. Cloud Console で Cloud Build を開きます。
  2. ビルドをクリックして内容を表示します。

4. コンテナ用 Artifact Registry

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

Cloud Build パイプラインを更新する

作成されたイメージを Artifact Registry に push するようにビルド パイプラインを変更する

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: ['-']

# push 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']

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

CI パイプラインを実行する

ビルドを送信して処理する

gcloud builds submit

5. 脆弱性スキャンを自動化する

Artifact スキャンは、Artifact Registry または Container Registry に新しいイメージを push するたびに自動的にトリガーされます。脆弱性情報は、新しい脆弱性が発見されるたびに更新されます。このセクションでは、作成して Artifact Registry に push したイメージを確認し、脆弱性の結果を調べます。

イメージの詳細を確認する

前のビルドプロセスが完了したら、Artifact Registry のダッシュボードでイメージと脆弱性の結果を確認します。

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

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

361be7b3bf293fca.png

スキャンの自動化はデフォルトで有効になっています。自動スキャンをオフまたはオンにする方法は、Artifact Registry の設定で確認してください。

6. オンデマンド スキャン

イメージをリポジトリに 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

7. Cloud Build を使用した CI/CD でのスキャン

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. イメージの [脆弱性] タブをクリックする

8. 完了

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

学習した内容

  • Cloud Build を使用してイメージをビルドする
  • コンテナ用 Artifact Registry
  • 自動化された脆弱性スキャン
  • オンデマンド スキャン
  • Cloud Build を使用した CI / CD でのスキャン

次のステップ:

クリーンアップ

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

プロジェクトの削除

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

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