配置 CI/CD 集成

Security Command Center 客户可能会发现,很难从持续集成/持续交付 (CI/CD) 环境中获得集中、全面的扫描结果视图。缺少统一视图会使漏洞管理变得复杂。CI/CD 集成是 制品安全防护 (预览版)的一个组件,可让您连接多个 CI/CD 流水线,帮助您在整个软件开发 生命周期中检测漏洞。

CI/CD 集成支持 GitHub Actions、Cloud Build、 和 Jenkins。连接后,您可以使用任何这些 CI/CD 平台来配置制品安全防护政策,从而提供对安全状况的可见性和主动控制。

概览

CI/CD 集成提供以下功能:

  • 端到端政策执行:帮助制品安全防护 从代码到云端应用一致的安全政策。
  • 全面威胁检测:扫描漏洞、公开的 Secret、有问题的许可和恶意软件包。
  • 广泛的 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 控制台 中或使用 Google Cloud CLI 创建连接器。

在 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. 创建 Secret 以进行身份验证
  2. 创建特定于您的流水线的集成模板文件
  3. 启动评估

Secret 配置

在外部运行的 CI/CD 流水线可以使用 服务帐号密钥或工作负载身份联合进行身份验证。 Google Cloud 如需了解有关创建 Secret 的详细说明,请参阅以下内容:

服务账号密钥

工作负载身份联合 (适用于 GitHub Actions)

您必须使用以下方法之一将 Secret 添加到 CI/CD 环境:

服务账号密钥方法

一个 Secret:

  • GCP_CREDENTIALS:您下载的服务帐号 JSON 密钥文件的内容。

工作负载身份联合方法

两个 Secret:

  • GCP_WORKLOAD_IDENTITY_PROVIDER:工作负载身份提供方的完整资源名称。例如,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(Secret)

此模板适用于使用 Secret 密钥的 GitHub Actions。

  • 添加您的服务帐号 Secret 密钥 (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)

此模板适用于使用工作负载身份联合的 GitHub Actions。

  • 在 GitHub Secret 中添加您的工作负载身份提供方 (GCP_WORKLOAD_IDENTITY_PROVIDER)。
  • 在 GitHub Secret 中添加您的服务账号 (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(Secret)

  • 添加服务帐号 Secret 密钥 (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 标志,用于控制扫描输出中显示的详细程度。扫描器输出因详细程度和符合性结果(通过或失败)而异。
  • 详细程度标志可以设置为 LOWHIGH。如果未提供,则默认为 LOW

低详细程度(简洁)

ArtifactGuard 符合性:False

  • 详细说明失败的原因。
  • 仅列出导致失败的特定 CVE。
  • 提供按严重程度分类的 CVE 的汇总计数。

ArtifactGuard 符合性:通过

  • 详细说明政策名称。
  • 仅提供按严重程度分类的 CVE 的汇总计数。

高详细程度(详细)

提供找到的所有漏洞的完整列表。

ArtifactGuard 符合性:False

  • 包含失败原因。
  • 列出导致失败的 CVE。
  • 提供为政策检测到的所有 CVE 的列表。
  • 提供按严重程度分类的 CVE 的汇总计数。
  • 提供检测到的所有漏洞的完整列表。

ArtifactGuard 符合性:通过

  • 提供为政策检测到的所有 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 运行时,然后由后者确定流水线的通过或失败状态。

性能和限制

  • 制品评估:仅当映像满足以下条件时,才会对其进行评估:
    • 软件包统一资源定位符 (p网址) 对象大小不得超过 100 MB。
    • 映像必须包含 500 个或更少的 p网址。
  • 对于制品安全防护服务中的所有方法,每个消费者项目每分钟最多可以发出 1,200 个 API 请求(20 QPS)。
  • SLO:制品评估大约需要两分钟。

问题排查

本部分概述了常见错误以及如何解决这些错误。

CreateConnector 失败

字段 必需/可选 限制条件
name 必需 格式: 必须匹配正则表达式 [a-zA-Z0-9\\-\\s_]+$
匹配以下一项或多项:
  • 字母(大写或小写)
  • 数字
  • 连字符 -
  • 空格符 (\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 请求满足指定的 限制条件。