CI/CD 統合を構成する

Security Command Center のお客様は、継続的インテグレーション/継続的デリバリー(CI/CD)環境のスキャン結果を一元的に包括的に把握することが難しい場合があります。統一されたビューがないと、脆弱性管理が複雑になります。CI/CD 統合は、複数の CI/CD パイプラインを接続して、ソフトウェア開発 ライフサイクル全体で脆弱性を検出できるようにする アーティファクト保護 (プレビュー) のコンポーネントです。

CI/CD 統合は、GitHub Actions、Cloud Build、 および Jenkins をサポートしています。接続すると、これらの CI/CD プラットフォームを使用してアーティファクト保護ポリシーを構成し、セキュリティ ポスチャーの可視化と事前制御を行うことができます。

概要

CI/CD 統合には次の利点があります。

  • エンドツーエンドのポリシー適用: アーティファクト保護が コードからクラウドまで一貫したセキュリティ ポリシーを適用するのに役立ちます。
  • 包括的な脅威検出: 脆弱性、公開された シークレット、問題のあるライセンス、悪意のあるパッケージをスキャンします。
  • 幅広い CI/CD 互換性: Jenkins と GitHub Actions で動作します。
  • CI/CD 向けに最適化: 軽量のバイナリにより、 ローカル環境で効率的に動作します。
  • 柔軟な出力: 業界標準の JSON 形式と SARIF 形式で結果を提供します。
  • 一元化されたセキュリティ分析情報: ポリシー準拠ビューを セキュリティ ダッシュボードに直接表示します。

このスキャナは Security Command Center にネイティブにデータを入力し、アーティファクト作成からのセキュリティ検出結果をアセット中心の統一されたビューで提供します。

CI/CD 統合により、ソフトウェア ライフサイクル全体でアーティファクト保護ポリシーを適用できます。これにより、アーティファクトがビルドからデプロイまで準拠していることが検証されます。

オーディエンス

CI/CD 統合は、次の関係者のタスクに役立ちます。

  • 主なユーザー: Devops チームとプラットフォーム エンジニアリング チーム
    • 管理と統合: スキャン ツールの管理と、CI/CD パイプラインへの直接統合を担当します。
    • ポリシーの構成: スキャン ステップを設定します。
    • コンプライアンスのモニタリング: ビルドのコンプライアンスを追跡し、 セキュリティ管理者と協力してポリシーを改善します。
  • セカンダリ ユーザー: セキュリティ管理者
    • ポリシー作成者: ビジネスの重要度に基づいてセキュリティ ポリシーを定義して適用します。
    • 適用の自動化: CI/CD 内のセキュリティを自動化して、 脆弱性のノイズを減らし、ゲート基準を設定します。
    • 監督の提供: DevOps と連携し、管理者がゲート付きの脆弱性を追跡するための ダッシュボードを提供します。
  • セカンダリ ユーザー: アプリケーション デベロッパー
    • 確認と修正: ポリシー評価 の結果を確認し、ビルドの結果を確認して、フラグが設定されたセキュリティの問題を修正します。
    • 評価: CI パイプラインの ビルドプロセスを介して、ポリシー評価を間接的に開始します。
    • コンプライアンスの維持: 開発ワークフローを中断することなく、セキュリティ 要件への準拠を確保します。

主な用語と概念

  • 共通脆弱性識別子(CVE): 公開されている コンピュータ セキュリティの脆弱性で、一意の識別子が割り当てられています。これらの識別子は、修正のために脆弱性を追跡するのに役立ちます。
  • ソフトウェア部品構成表(SBOM): ソフトウェア コンポーネントと依存関係の機械可読なインベントリ。SBOM には、各コンポーネントのバージョン、オリジン、その他の関連情報が含まれます。SBOM を使用して、CVE やその他のセキュリティ リスクを特定できます。
  • アーティファクト: ビルドプロセス中に作成されたデータやアイテムなど、ソフトウェア開発のバージョン管理された検証済みの出力。
  • コネクタ: イメージのタグ。CI パイプラインからアーティファクト保護サービスに渡されると、コネクタはビルド中のイメージに対して実行するポリシーを決定します。
  • CI ポリシー: 環境で許可される脆弱性とパッケージを制御するルールまたは基準を定義する脆弱性ポリシー。

大まかなワークフロー

  1. CI コネクタを作成します
  2. アーティファクト保護ポリシーを作成 し、前の手順で構成したコネクタを使用してポリシーの スコープを定義します。
  3. アーティファクトの評価を開始します

評価中、イメージがビルドされ、事前定義されたポリシーに対して評価されます。ポリシーが失敗すると、ビルドが失敗します。DevOps エンジニアまたはアプリケーション エンジニアは、失敗の詳細を調べて特定の脆弱性を特定し、CVE の詳細に従って依存関係を更新して、パイプラインを再実行する必要があります。

始める前に

CI/CD 統合を使用するには、アーティファクト保護を有効にする必要があります。手順については、アーティファクト保護のドキュメントの始める前にをご覧ください。

コンソールまたは Google Cloud CLI を使用してコネクタを作成できます。Google Cloud

コンソールでコネクタを作成する Google Cloud

コネクタを作成するには、次の手順に沿って操作します。

  1. コンソールで、[セキュリティ > 設定] に移動します。 Google Cloud

  2. [アーティファクト保護] カードで、[設定の管理] をクリックします。

  3. [コネクタを作成] をクリックし、コネクタの詳細を次のように入力します。

    • コネクタ ID: コネクタの ID を追加します。
    • 説明: コネクタの説明を入力します。
    • CI/CD プラットフォーム: リストから対応する CI/CD プラットフォームを選択します。このコネクタは、提供された CI/CD プラットフォームでビルドされたパイプラインでのみ使用してください。
  4. [作成] をクリックします。

通知により、コネクタが正常に作成されたことが確認されます。利用可能なコネクタは、[コネクタ] テーブルに一覧表示されます。

コネクタを削除するには、コネクタの横にある をクリックして [**コネクタを削除**] を選択し、プロンプトに沿って操作します。中止するには、[キャンセル] をクリックします。

ポリシーをコネクタにリンクするには、コネクタの横にある をクリックして [ポリシーを追加] を選択します。手順に沿ってアーティファクト保護ポリシーを作成します。詳細については、 ポリシーを作成するをご覧ください。

Google Cloud CLI を使用してコネクタを作成する

このセクションでは、CI/CD アーティファクト スキャンで使用できる gcloud CLI コマンドとその使用方法について説明します。

Google Cloud CLI の前提条件

  • gcloud CLI のバージョンが 559.0.0 以降であることを確認します。
  • プロジェクトを構成プロジェクトとして設定します。

これを行うには、次の gcloud CLI コマンドを実行します。

   gcloud components update --version=559.0.0
   gcloud config set project PROJECT_ID

Google Cloud CLI コマンド

create

gcloud alpha scc artifact-guard connectors create CONNECTOR_ID \
    --location=LOCATION \
    (--organization=ORGANIZATION_ID | --project=PROJECT_NUMBER) \
    --pipeline-type=PIPELINE_TYPE \
    [--description=DESCRIPTION] \
    [--display-name=DISPLAY_NAME]
  • CONNECTOR_ID: 作成するコネクタの ID。
  • PIPELINE_TYPE: CI/CD パイプラインのタイプ。次のいずれかの値を設定する必要があります。
    • GOOGLE_CLOUD_BUILD
    • GITHUB_ACTIONS
    • JENKINS_PIPELINE
  • DESCRIPTION: コネクタのテキスト説明。
  • DISPLAY_NAME: コネクタのわかりやすい表示名。

get

gcloud alpha scc artifact-guard connectors describe CONNECTOR_ID \
    --location=LOCATION \
    (--organization=ORGANIZATION_ID | --project=PROJECT_NUMBER)
  • CONNECTOR_ID: 説明するコネクタの ID。

list

gcloud alpha scc artifact-guard connectors list PARENT
  • PARENT: 組織またはプロジェクト。親リソースに使用できる形式は次のとおりです。
    • {organizations/ORGANIZATION_ID/locations/LOCATION}
    • {projects/PROJECT_NUMBER/locations/LOCATION}

delete

gcloud alpha scc artifact-guard connectors delete CONNECTOR_ID \
    --location=LOCATION \
    (--organization=ORGANIZATION_ID | --project=PROJECT_NUMBER)
  • CONNECTOR_ID: 削除するコネクタの ID。

評価を実行する

脆弱性スキャンは、GitHub Actions パイプラインと Jenkins パイプラインでサポートされています。 評価を行うには、次の操作を行う必要があります。

  1. 認証用のシークレットを作成します
  2. パイプラインに固有の統合テンプレート ファイルを作成します
  3. 評価を開始します

シークレットの構成

の外部で実行されている CI/CD パイプラインは、 Google Cloud サービス アカウント キーまたは Workload Identity 連携を使用して認証できます。シークレットの作成方法の詳細については、以下をご覧ください。

サービス アカウント キー

Workload Identity 連携 (GitHub Actions の場合)

次のいずれかの方法で、CI/CD 環境にシークレットを追加する必要があります。

サービス アカウント キーの方法

1 つのシークレット:

  • GCP_CREDENTIALS: ダウンロードした サービス アカウントの JSON キーファイルの内容。

Workload Identity 連携の方法

2 つのシークレット:

  • GCP_WORKLOAD_IDENTITY_PROVIDER: Workload Identity プロバイダの完全なリソース名。例: projects/12345/locations/global/workloadIdentityPools/my-pool/providers/my-provider

  • GCP_SERVICE_ACCOUNT: なり代わるサービス アカウントのメールアドレス。

パイプライン統合テンプレート

評価をトリガーするには、次のテンプレート例を使用して、パイプライン(Cloud Build、GitHub Actions、Jenkins)に固有のファイルを作成する必要があります。

Cloud Build

  • 各フィールドの詳細については、変数の定義をご覧ください。

    steps:
          # Step 1: Generate auth token
          - name: 'gcr.io/cloud-builders/gcloud'
          id: 'Generate Token'
          entrypoint: 'bash'
          args:
                - '-c'
                - |
                echo "Starting token generation..."
                gcloud auth print-access-token > /workspace/gcp_token.txt
                if [ $? -eq 0 ]; then
                      echo "Token generated successfully."
                else
                      echo "Failed to generate token." >&2
                      exit 1
                fi
    
          # Step 2: Build the image locally
          - name: 'gcr.io/cloud-builders/docker'
          id: 'Build Image'
          entrypoint: 'bash'
          args:
                - '-c'
                - |
                echo "🚧 Building Docker image from source code..."
                docker build -t ${_IMAGE_NAME_TO_SCAN}:${_IMAGE_TAG} .
                if [ $? -ne 0 ]; then
                      echo "❌ Docker build failed."
                      exit 1
                fi
                echo "✅ Docker image built successfully: ${_IMAGE_NAME_TO_SCAN}:${_IMAGE_TAG}"
    
          # Step 3: Image scan for vulnerabilities
          - id: 'Image-Analysis'
          name: '${_SCANNER_IMAGE}'
          entrypoint: 'bash'
          args:
                - '-c'
                - |
                echo "Starting image scan with scanner: ${_SCANNER_IMAGE}"
    
                exit_code=0
    
                docker run --rm \
                      -v /var/run/docker.sock:/var/run/docker.sock \
                      -v /workspace:/workspace \
                      -e GCP_PROJECT_ID="${_PROJECT_ID}" \
                      -e ORGANIZATION_ID="${_ORGANIZATION_ID}" \
                      -e IMAGE_NAME="${_IMAGE_NAME_TO_SCAN}" \
                      -e IMAGE_TAG="${_IMAGE_TAG}" \
                      -e CONNECTOR_ID="${_CONNECTOR_ID}" \
                      -e TRIGGER_ID="${_TRIGGER_ID}" \
                      -e IGNORE_ERRORS="${_IGNORE_ERRORS}" \
                      -e GCP_ACCESS_TOKEN="$(cat /workspace/gcp_token.txt)" \
                      "${_SCANNER_IMAGE}" || exit_code=$?
    
                echo "Docker run finished with exit code: $exit_code"
    
                if [ $exit_code -eq 0 ]; then
                      echo "✅ Evaluation succeeded: Conformant image."
                elif [ $exit_code -eq 1 ]; then
                      echo "❌ Scan failed: Non-conformant image."
                      exit 1
                else
                      if [ "${_IGNORE_ERRORS}" = "true" ]; then
                            echo "⚠️ Server/internal error ignored. Continuing."
                      else
                            echo "❌ Server/internal error. Exiting."
                            exit 1
                      fi
                fi
    
          # Step 4: Configure Docker authentication for Artifact Registry
          - name: 'gcr.io/cloud-builders/gcloud'
          id: 'Configure Docker Auth'
          entrypoint: 'bash'
          args:
                - '-c'
                - |
                echo "🔐 Configuring Docker authentication for Artifact Registry..."
                gcloud auth configure-docker us-east1-docker.pkg.dev -q
                echo "✅ Docker authentication configured."
    
          # Step 5: Push image to Artifact Registry
          - name: 'gcr.io/cloud-builders/docker'
          id: 'Push Image to Artifact Registry'
          entrypoint: 'bash'
          args:
                - '-c'
                - |
    
                docker tag "${_IMAGE_NAME_TO_SCAN}:${_IMAGE_TAG}" "us-east1-docker.pkg.dev/${_PROJECT_ID}/${_AR_REPOSITORY}/${_IMAGE_NAME_TO_SCAN}:${_IMAGE_TAG}"
    
                echo "🚀 Pushing $_FULL_AR_TAG..."
                docker push "us-east1-docker.pkg.dev/${_PROJECT_ID}/${_AR_REPOSITORY}/${_IMAGE_NAME_TO_SCAN}:${_IMAGE_TAG}"
    
                echo "✅ Image pushed successfully."
    
    substitutions:
          _IMAGE_NAME_TO_SCAN: 'checkout-image'
          _ORGANIZATION_ID: 'orgId'
          _CONNECTOR_ID: 'connectorId'
          _SCANNER_IMAGE: 'us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest'
          _IMAGE_TAG: 'latest'
          _TRIGGER_ID: 'cloud-build-job'
          _PROJECT_ID: 'projectId'
          _AR_REPOSITORY: 'images'
          _IGNORE_ERRORS: "false"
    
    serviceAccount: "projects/projectId/serviceAccounts/id-compute@developer.gserviceaccount.com"
    
    options:
          logging: CLOUD_LOGGING_ONLY
    

GitHub Actions(シークレット)

このテンプレートは、シークレット キーを使用する GitHub Actions 用です。

  • サービス アカウントのシークレット キー(GCP_CREDENTIALS)を追加します。
  • 追加のフィールドの詳細については、変数の定義をご覧ください。

    # A workflow to BUILD the app image, RUN the scanner, and PUSH to AR if scan passes
    name: Build, Scan and Push
    
    on:
     workflow_dispatch:
       inputs:
          IMAGE_NAME_TO_SCAN:
                description: 'The tag for your application image to be built (e.g., my-app:latest)'
                required: true
                default: 'checkout-image'
          GCP_PROJECT_ID:
                description: 'GCP Project ID for authentication'
                required: true
                default: 'projectId'
          AR_REPOSITORY:
                description: 'Artifact Registry repository name (e.g., app-repo)'
                required: false
                default: 'images'
          ORGANIZATION_ID:
                description: 'Your GCP Organization ID'
                required: true
                default: 'orgId'
          CONNECTOR_ID:
                description: 'The ID for your pipeline connector'
                required: true
                default: 'connectorId'
          SCANNER_IMAGE:
                description: 'The full registry path for your PRE-BUILT scanner tool'
                required: true
                default: 'us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest'
          IMAGE_TAG:
                description: 'The Docker image version (of the app image)'
                required: true
                default: 'latest'
          IGNORE_SERVER_ERRORS:
                description: 'Ignore server errors'
                required: false
                type: boolean
                default: false
          VERBOSITY:
                description: 'Verbosity flag'
                required: false
                default: 'HIGH'
    
    jobs:
     build-and-scan:
       runs-on: ubuntu-latest
       steps:
          # 1. Check out repository (for your app's Dockerfile)
          - name: Check out repository
            uses: actions/checkout@v4
    
          # 2. Authenticate to Google Cloud
          - name: Authenticate to GCP
            id: auth
            uses: 'google-github-actions/auth@v2'
            with:
                credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
    
          # 3. Set up the gcloud CLI
          - name: Set up Cloud SDK
            uses: 'google-github-actions/setup-gcloud@v2'
            with:
                project_id: ${{ inputs.GCP_PROJECT_ID }}
    
           # 4. Configure Docker (needed to pull SCANNER_IMAGE and push app image)
          - name: Configure Docker
            run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
    
          # 5. Build Application Image Locally (IMAGE_NAME_TO_SCAN)
          - name: Build Application Image Locally
            uses: docker/build-push-action@v5
            with:
                context: .
                file: ./Dockerfile
                push: false  # <-- Do not push
                load: true   # <-- Load image into the runner's local daemon
                # Tag the image with the name the scanner will look for
                tags: |
                ${{ inputs.IMAGE_NAME_TO_SCAN }}:${{ inputs.IMAGE_TAG }}
    
          # 6. Run Image Scan (Using the SCANNER_IMAGE)
          - name: 'Run Image Analysis Scan'
            if: steps.auth.outcome == 'success'
            run: |
                echo "📦 Pulling scanner image and running scan..."
    
          SCANNER_IMAGE="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.SCANNER_IMAGE || env.SCANNER_IMAGE }}"
          GCP_PROJECT_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.GCP_PROJECT_ID || env.GCP_PROJECT_ID }}"
          ORGANIZATION_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ORGANIZATION_ID || env.ORGANIZATION_ID }}"
          IMAGE_NAME="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IMAGE_NAME_TO_SCAN || env.IMAGE_NAME_TO_SCAN }}"
          IMAGE_TAG="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IMAGE_TAG || env.IMAGE_TAG }}"
          CONNECTOR_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.CONNECTOR_ID || env.CONNECTOR_ID }}"
          VERBOSITY="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.VERBOSITY || env.VERBOSITY }}"
          IGNORE_ERRORS="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IGNORE_SERVER_ERRORS || (env.IGNORE_SERVER_ERRORS == 'true') }}"
    
          exit_code=0
    
          # This 'docker run' pulls the SCANNER_IMAGE from the registry
          # and passes the name of the locally-built app image (IMAGE_NAME)
          docker run --rm \
                -v /var/run/docker.sock:/var/run/docker.sock \
                -v ${{ steps.auth.outputs.credentials_file_path }}:/tmp/scc-key.json \
                -e GCLOUD_KEY_PATH=/tmp/scc-key.json \
                -e GCP_PROJECT_ID="${GCP_PROJECT_ID}" \
                -e ORGANIZATION_ID="${ORGANIZATION_ID}" \
                -e IMAGE_NAME="${IMAGE_NAME}" \
                -e IMAGE_TAG="${IMAGE_TAG}" \
                -e CONNECTOR_ID="${CONNECTOR_ID}" \
                -e BUILD_TAG="${{ github.workflow }}" \
                -e BUILD_ID="${{ github.run_number }}" \
                -e VERBOSITY="${VERBOSITY}" \
                "${SCANNER_IMAGE}" \
                || exit_code=$?
    
          echo "Docker run finished with exit code: $exit_code"
    
          # --- Replicate Jenkins Exit Code Logic ---
          if [ $exit_code -eq 0 ]; then
            echo "✅ Evaluation succeeded: Conformant image."
          elif [ $exit_code -eq 1 ]; then
            echo "❌ Scan failed: Non-conformant image (vulnerabilities found)."
            exit 1 # Fail the step
          else
           if [ "$IGNORE_ERRORS" = "true" ]; then
                echo "⚠️ Server/internal error occurred (Code: $exit_code), but IGNORE_SERVER_ERRORS=true. Proceeding."
          else
            echo "❌ Server/internal error occurred (Code: $exit_code) during evaluation. Set IGNORE_SERVER_ERRORS=true to override."
            exit 1 # Fail the step
          fi
          fi
    
          # 8. Push Application Image (ONLY if scan succeeded)
          # This step only runs if the 'Run Image Analysis Scan' step above exited with 0
          - name: Push Application Image to Artifact Registry
            run: |
            # Define the local and remote tags
            LOCAL_IMAGE_NAME="${{ inputs.IMAGE_NAME_TO_SCAN }}:${{ inputs.IMAGE_TAG }}"
    
            # This path is based on your 'Configure Docker' step (us-central1)
            # and the new AR_REPOSITORY input.
            FULL_AR_TAG="us-central1-docker.pkg.dev/${{ inputs.GCP_PROJECT_ID }}/${{ inputs.AR_REPOSITORY }}/${{ inputs.IMAGE_NAME_TO_SCAN }}:${{ inputs.IMAGE_TAG }}"
    
            echo "Tagging local image ${LOCAL_IMAGE_NAME} as ${FULL_AR_TAG}"
            docker tag "${LOCAL_IMAGE_NAME}" "${FULL_AR_TAG}"
    
            echo "Pushing ${FULL_AR_TAG} to Artifact Registry..."
            docker push "${FULL_AR_TAG}"
    

GitHub Actions(WIF)

このテンプレートは、Workload Identity 連携を使用する GitHub Actions 用です。

  • GitHub シークレット(GCP_WORKLOAD_IDENTITY_PROVIDER)に Workload Identity プロバイダを追加します。
  • GitHub シークレット(GCP_SERVICE_ACCOUNT)にサービス アカウントを追加します。
  • 追加のフィールドの詳細については、変数の定義をご覧ください。

    # A workflow to BUILD the app image, RUN the scanner, and PUSH to AR if scan passes
    name: Build, Scan and Push
    
    on:
      push:
        branches:
          - main
      workflow_dispatch:
      inputs:
        IMAGE_NAME_TO_SCAN:
          description: 'The tag for your application image to be built (e.g., my-app:latest)'
          required: true
          default: 'checkout-image'
        GCP_PROJECT_ID:
          description: 'GCP Project ID for authentication and configuration'
          required: true
          default: 'projectId'
        ORGANIZATION_ID:
          description: 'Your GCP Organization ID'
          required: true
          default: 'orgId'
        CONNECTOR_ID:
          description: 'The ID for your pipeline connector'
          required: true
          default: 'connectorId'
        SCANNER_IMAGE:
          description: 'The Docker image that contains your scanner script'
          required: true
          default: 'us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest'
        IMAGE_TAG:
          description: 'The Docker image version'
          required: true
          default: 'latest'
        IGNORE_SERVER_ERRORS:
          description: 'If true, the pipeline continues on server/internal scanner errors.'
          required: false
          type: boolean
          default: false
    
    jobs:
      image-analysis-job:
      runs-on: ubuntu-latest
      permissions:
          contents: 'read'
          id-token: 'write'
    
      env:
          IMAGE_NAME_TO_SCAN: 'webgoat/webgoat'
          GCP_PROJECT_ID: 'projectId'
          ORGANIZATION_ID: 'orgId'
          CONNECTOR_ID: 'connectorId'
          SCANNER_IMAGE: 'us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest'
          IMAGE_TAG: 'imageTag'
          IGNORE_SERVER_ERRORS: 'false'
    
      steps:
          # Step 1: Authenticate and create credential file
          - name: 'Authenticate to Google Cloud'
          id: 'auth'
          uses: 'google-github-actions/auth@v2'
          with:
          workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
          create_credentials_file: true
    
          # Step 2: Set up gcloud SDK
          - name: 'Set up gcloud SDK'
          uses: 'google-github-actions/setup-gcloud@v2'
    
          # Step 3: Configure Docker for registries
          - name: 'Configure Docker for Artifact Registry'
          run: |
          gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
    
          # Step 4: Run Image Analysis Scan and Handle Exit Codes
          - name: 'Run Image Analysis Scan'
          run: |
          echo "📦 Running container from scanner image..."
    
          # Determine values: Use manual inputs if available (event_name=workflow_dispatch), otherwise use env defaults
          SCANNER_IMAGE="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.SCANNER_IMAGE || env.SCANNER_IMAGE }}"
          GCP_PROJECT_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.GCP_PROJECT_ID || env.GCP_PROJECT_ID }}"
          ORGANIZATION_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ORGANIZATION_ID || env.ORGANIZATION_ID }}"
          IMAGE_NAME="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IMAGE_NAME_TO_SCAN || env.IMAGE_NAME_TO_SCAN }}"
          IMAGE_TAG="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IMAGE_TAG || env.IMAGE_TAG }}"
          CONNECTOR_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.CONNECTOR_ID || env.CONNECTOR_ID }}"
          IGNORE_ERRORS="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IGNORE_SERVER_ERRORS || (env.IGNORE_SERVER_ERRORS == 'true') }}"
    
          # Variable to store exit code
          exit_code=0
    
          # Run docker and capture exit code using || trick
          docker run --rm \
                -v /var/run/docker.sock:/var/run/docker.sock \
                -v ${{ steps.auth.outputs.credentials_file_path }}:/gcp-creds.json \
                -e GOOGLE_APPLICATION_CREDENTIALS=/gcp-creds.json \
                -e GCP_PROJECT_ID="${GCP_PROJECT_ID}" \
                -e ORGANIZATION_ID="${ORGANIZATION_ID}" \
                -e IMAGE_NAME="${IMAGE_NAME}" \
                -e IMAGE_TAG="${IMAGE_TAG}" \
                -e CONNECTOR_ID="${CONNECTOR_ID}" \
                -e RUN_ID="${{ github.run_number }}" \
                "${SCANNER_IMAGE}" \
                || exit_code=$?
    
          echo "Docker run finished with exit code: $exit_code"
    
          if [ $exit_code -eq 0 ]; then
                echo "✅ Evaluation succeeded: Conformant image."
          elif [ $exit_code -eq 1 ]; then
                echo "❌ Scan failed: Non-conformant image (vulnerabilities found)."
                exit 1 # Fail the step
          else
                if [ "$IGNORE_ERRORS" = "true" ]; then
                echo "⚠️ Server/internal error occurred (Code: $exit_code), but IGNORE_SERVER_ERRORS=true. Proceeding."
                # Do nothing, step passes
                else
                echo "❌ Server/internal error occurred (Code: $exit_code) during evaluation. Set IGNORE_SERVER_ERRORS=true to override."
                exit 1 # Fail the step
                fi
          fi
    

Jenkins(シークレット)

  • サービス アカウントのシークレット キー(GCP_CREDENTIALS)を追加します。
  • 追加のフィールドの詳細については、変数の定義をご覧ください。

    pipeline {
          agent any
    
          parameters {
                string(
                      name: 'IMAGE_NAME_TO_SCAN',
                      defaultValue: 'checkout-image',
                      description: 'The tag for your application image to be built (e.g., my-app:latest)'
                )
                string(
                      name: 'GCP_PROJECT_ID',
                      defaultValue: 'projectId',
                      description: 'GCP Project ID for authentication'
                )
                string(
                      name: 'AR_REPOSITORY',
                      defaultValue: 'images',
                      description: 'Artifact Registry repository name (e.g., app-repo)'
                )
                string(
                      name: 'ORGANIZATION_ID',
                      defaultValue: 'orgId',
                      description: 'Your GCP Organization ID'
                )
                string(
                      name: 'CONNECTOR_ID',
                      defaultValue: 'connectorId',
                      description: 'The ID for your pipeline connector'
                )
                string(
                      name: 'SCANNER_IMAGE',
                      defaultValue: 'us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest',
                      description: 'The full registry path for your PRE-BUILT scanner tool'
                )
                string(
                      name: 'IMAGE_TAG',
                      defaultValue: 'latest',
                      description: 'The Docker image version (of the app image)'
                )
                booleanParam(
                      name: 'IGNORE_SERVER_ERRORS',
                      defaultValue: false,
                      description: 'Ignore server errors'
                )
                string(
                      name: 'VERBOSITY',
                      defaultValue: 'HIGH',
                      description: 'Verbosity flag'
                )
          }
    
    stages {
          // Stage 1: Check out the source code
          stage('Checkout') {
                steps {
                echo "Checking out source code..."
                checkout scm
                }
          }
    
          // Stage 2: Build application image
          stage('Build Application Image') {
                steps {
                echo "Building application image: ${params.IMAGE_NAME_TO_SCAN}:${params.IMAGE_TAG}"
                sh "docker build -t ${params.IMAGE_NAME_TO_SCAN}:${params.IMAGE_TAG} -f ./Dockerfile ."
                }
          }
    
          // Stage 3: Authenticate to Google Cloud and run scanner
          stage('Scan Image') {
                steps {
                script {
                      withCredentials([file(credentialsId: 'GCP_CREDENTIALS', variable: 'GCP_KEY_FILE')]) {
                            // Authenticate
                            sh "gcloud auth activate-service-account --key-file=\"$GCP_KEY_FILE\""
                            sh 'gcloud auth list'
                            sh 'gcloud auth configure-docker gcr.io --quiet'
                            sh 'gcloud auth configure-docker us-central1-docker.pkg.dev --quiet'
    
                            // Run scanner container
                            def exitCode = sh(
                            script: """
                                  echo "📦 Running scanner container from image: ${params.SCANNER_IMAGE}"
    
                                  docker run --rm \\
                                        -v /var/run/docker.sock:/var/run/docker.sock \\
                                        -v "$GCP_KEY_FILE":/tmp/scc-key.json \\
                                        -e GCLOUD_KEY_PATH=/tmp/scc-key.json \\
                                        -e GCP_PROJECT_ID="${params.GCP_PROJECT_ID}" \\
                                        -e ORGANIZATION_ID="${params.ORGANIZATION_ID}" \\
                                        -e IMAGE_NAME="${params.IMAGE_NAME_TO_SCAN}" \\
                                        -e IMAGE_TAG="${params.IMAGE_TAG}" \\
                                        -e CONNECTOR_ID="${params.CONNECTOR_ID}" \\
                                        -e BUILD_TAG="${env.JOB_NAME}" \\
                                        -e BUILD_ID="${env.BUILD_NUMBER}" \\
                                        "${params.SCANNER_IMAGE}"
                            """,
                            returnStatus: true
                            )
    
                            if (exitCode == 0) {
                            echo "✅ Evaluation succeeded: Conformant image."
                            } else if (exitCode == 1) {
                            error("❌ Scan failed: Non-conformant image (vulnerabilities found).")
                            } else {
                            if (params.IGNORE_SERVER_ERRORS) {
                                  echo "⚠️ Server/internal error occurred, but IGNORE_SERVER_ERRORS=true. Proceeding with pipeline."
                            } else {
                                  error("❌ Server/internal error occurred during evaluation. Set IGNORE_SERVER_ERRORS=true to override.")
                            }
                            }
                      }
                }
                }
          }
    
          // Stage 4: Push Application Image
          stage('Push Application Image') {
                steps {
                script {
                      def localImage = "${params.IMAGE_NAME_TO_SCAN}:${params.IMAGE_TAG}"
                      def remoteTag = "us-central1-docker.pkg.dev/${params.GCP_PROJECT_ID}/${params.AR_REPOSITORY}/${params.IMAGE_NAME_TO_SCAN}:${params.IMAGE_TAG}"
    
                      echo "Tagging local image ${localImage} as ${remoteTag}"
                      sh "docker tag ${localImage} ${remoteTag}"
    
                      echo "Pushing ${remoteTag} to Artifact Registry..."
                      sh "docker push ${remoteTag}"
                }
                }
          }
    }
    

変数の定義

このセクションでは、 パイプライン統合テンプレートで使用される変数フィールドについて説明します。

IMAGE_NAME_TO_SCAN(必須)

  • ビルドするアプリケーション イメージのタグを指定します。

GCP_PROJECT_ID(必須)

  • 認証と Google Cloud 構成に使用する プロジェクト ID を指定します。

AR_REPOSITORY(省略可)

  • ビルドが成功した場合にイメージが公開される Artifact Registry リポジトリの名前を指定します。

ORGANIZATION_ID(必須)

  • 組織 ID。 Google Cloud

CONNECTOR_ID(必須)

  • 使用するパイプラインのコネクタ ID を指定します。

SCANNER_IMAGE(必須)

  • 事前ビルド済みのスキャナ イメージは、コードを分析し、ビルド中にポリシーに対してイメージを評価して脆弱性を特定し、CI/CD パイプラインの合格または不合格を判断するための準拠結果を生成します。

    イメージの詳細: us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest

VERBOSITY(省略可)

  • スキャナは、スキャン出力に表示される詳細レベルを制御するオプションの VERBOSITY フラグをサポートしています。スキャナの出力は、詳細レベルと準拠結果(合格または不合格)によって異なります。
  • 詳細レベル フラグは、LOW または HIGH に設定できます。指定しない場合は、デフォルトで LOW になります。

詳細レベル: 低(簡潔)

ArtifactGuard Conformance : False

  • 失敗の理由の詳細を示します。
  • 失敗の原因となった特定の CVE のみを一覧表示します。
  • 重大度別に分類された CVE の合計数を表示します。

ArtifactGuard Conformance : Pass

  • ポリシー名を詳しく説明します。
  • 重大度別の CVE の合計数のみを表示します。

詳細レベル: 高(詳細)

検出されたすべての脆弱性の包括的なリストを提供します。

ArtifactGuard Conformance : False

  • 失敗の理由が含まれます。
  • 失敗の原因となった CVE を一覧表示します。
  • ポリシーで検出されたすべての CVE のリストを提供します。
  • 重大度別に分類された CVE の合計数を提供します。
  • 検出されたすべての脆弱性の包括的なリストを提供します。

ArtifactGuard Conformance : Pass

  • ポリシーで検出されたすべての CVE のリストを提供します。
  • 重大度別に分類された CVE の合計数を提供します。
  • 検出されたすべての脆弱性の包括的なリストを提供します。

IGNORE_SERVER_ERRORS(省略可)

  • 省略可能なブール値のフラグ。true の場合、サーバーエラーが発生してもパイプラインは続行されます。デフォルトは false です。

評価を開始する

ビルドプロセス中に、Docker ベースの戦略により、事前定義されたポリシーに対してイメージが評価されます。脆弱性スキャンのロジックは、us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest イメージに含まれています。

CI/CD パイプラインで脆弱性スキャンを開始するには、次のコマンドを実行します。

docker run --rm \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v ${{ steps.auth.outputs.credentials_file_path }}:/gcp-creds.json \
    -e GOOGLE_APPLICATION_CREDENTIALS=/gcp-creds.json \
    -e GCP_PROJECT_ID="${GCP_PROJECT_ID}" \
    -e ORGANIZATION_ID="${ORGANIZATION_ID}" \
    -e IMAGE_NAME="${IMAGE_NAME}" \
    -e IMAGE_TAG="${IMAGE_TAG}" \
    -e CONNECTOR_ID="${CONNECTOR_ID}" \
    -e RUN_ID="${{ github.run_number }}" \
    "${SCANNER_IMAGE}" \
    || exit_code=$?

CI/CD 統合は、Docker コンテナの終了コードを Jenkins または GitHub Actions ランタイムに伝播します。これにより、パイプラインの合格または不合格の状態が決定されます。

パフォーマンスと制限事項

  • アーティファクトの評価: イメージは、 次の条件を満たす場合にのみ評価されます:
    • Package Uniform Resource Locator(pURL)オブジェクトのサイズは 100 MB を超えることはできません。
    • イメージには 500 個以下の pURL が含まれている必要があります。
  • アーティファクト保護サービス内のすべてのメソッドで、コンシューマー プロジェクトごとに 1 分あたり最大 1,200 件の API リクエスト(20 QPS)。
  • SLO: アーティファクトの評価には約 2 分かかります。

トラブルシューティング

このセクションでは、一般的なエラーとその解決方法について説明します。

CreateConnector の失敗

フィールド 必須/省略可 制約
name 必須 [形式]: 正規表現 [a-zA-Z0-9\\-\\s_]+$
と一致する必要があります。 次のいずれか 1 つ以上に一致します。
  • 文字(大文字または小文字)
  • 数値
  • ハイフン -
  • 空白文字(\s)
  • アンダースコア _


最大長: 64 文字。
pipeline_type 必須 次のいずれかの列挙値にする必要があります。
  • JENKINS_PIPELINE
  • GITHUB_ACTIONS
  • GOOGLE_CLOUD_BUILD
description 省略可 256 文字以内にしてください。
display_name 省略可 256 文字以内にしてください。

その他のエラー

次の表に、一般的なエラーとその解決方法を示します。

エラー メッセージ 原因 アクション/解決策
リソースに対する権限 artifactscanguard.connectors.create が拒否されました ユーザーまたはサービス アカウントに、リソース(プロジェクト、フォルダ、組織)に対する artifactscanguard.connectors.create IAM 権限がありません。 呼び出し元に、 artifactscanguard.connectors.create 権限を含む IAM ロールを付与します。
status.ErrFailedPrecondition コンソールにサービスが有効と表示されていても、オンボーディングが進行中の可能性があります。 Google Cloud サポートチームに問題を報告してください。
status.ErrInvalidArgument フィールドの検証エラー CreateConnector リクエストが指定された 制約を満たしていることを確認します。