コンテナビルドの保護

1. はじめに

ead1609267034bf7.png

ソフトウェアの脆弱性は、偶発的なシステム障害の原因になる場合や、悪意のある攻撃者にソフトウェアを侵害する手段を提供する可能性がある弱点です。Container Analysis では、コンテナ内の脆弱性を検出するために、次の 2 種類の OS スキャンを行います。

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

On-Demand Scanning API を使用すると、自分のコンピュータにローカルに保存されているイメージ、または Container Registry や Artifact Registry にリモートで保存されているイメージをスキャンできます。これにより、脆弱性をスキャンするコンテナをきめ細かく制御できます。オンデマンド スキャンを使用して、イメージをレジストリに保存するかどうかを決定する前に、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 を生成できます。または、ご自身でお試しになることもできます。このステップを終えた後は変更できず、プロジェクト期間中は維持されます。
  • なお、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 を使用したイメージのビルド

このセクションでは、コンテナ イメージをビルドしてスキャンし、結果を評価する自動ビルド パイプラインを作成します。重大な脆弱性が見つからなければ、イメージがリポジトリに 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"

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

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 コンソールで 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 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) 

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

重大な問題を報告する

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

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 を使用した CICD でのスキャン

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 プロセスを実行する

重大度の高い脆弱性が見つからなくてもビルドが成功することを確認するために、処理対象のビルドを送信します。

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 を使用した CICD でのスキャン

次のステップ:

クリーンアップ

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

プロジェクトの削除

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

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