CI/CD 集成功能可将您的 CI/CD 流水线连接到制品安全防护,以便在构建阶段检测容器漏洞。
该扫描器支持 GitHub Actions、Cloud Build 和 Jenkins。当集成到流水线中时,它会在将容器映像推送到注册表之前,根据您的制品防护政策评估这些映像。
扫描仪功能
CI/CD 集成以二进制文件的形式在 CI/CD 工作流中运行,以执行以下任务:
- 构建时政策评估:根据您配置的制品安全防护政策扫描新构建的容器映像。
- 构建阻塞:向 CI/CD 运行程序(GitHub Actions、Cloud Build 或 Jenkins)返回退出代码,以阻止违反安全政策的构建。
- 扫描报告:将漏洞扫描结果发送到 Security Command Center,并以 JSON 或 SARIF 格式输出本地报告。
目标角色和职责
CI/CD 集成专为以下团队设计:
- DevOps 或平台工程团队:创建流水线连接器,并将扫描器执行步骤添加到 CI/CD 工作流配置中。
- 安全管理员:定义扫描工具强制执行的构建时漏洞政策。
- 应用开发者:查看 CI/CD build 输出中的扫描执行日志,以识别并修正违规问题。
关键术语和概念
- 常见漏洞和披露 (CVE):一种已公开披露的计算机安全漏洞,具有唯一的标识符。
- 软件物料清单 (SBOM):一种机器可读的软件组件和依赖项清单,用于识别 CVE 和其他安全风险。
- 制品:在制品安全防护的上下文中,指由 CI/CD 流水线构建的容器映像。
- 连接器:一种将 CI/CD 流水线与制品安全防护相关联的资源。在扫描期间从 CI 流水线传递时,连接器 ID 会确定哪些政策评估映像。
- CI 政策:一种漏洞政策,用于定义在 CI/CD 构建期间强制执行的特定 CVE、严重程度和软件包规则。
高级别工作流程
如需配置和运行 CI/CD 漏洞扫描,请完成以下工作流程:
- 为目标 CI/CD 平台创建 CI 连接器。
- 创建制品安全防护政策,并在其流水线范围内添加连接器 ID。
- 配置 CI/CD 流水线以向Google Cloud进行身份验证、构建容器映像并运行扫描器容器。
流水线运行时,扫描器会根据您的政策评估容器映像。如果映像违反了政策,扫描器会返回失败退出代码,导致 build 失败。然后,开发者可以检查 build 日志、更新存在安全漏洞的依赖项,并重新运行 build。
准备工作
如需使用 CI/CD 集成,您必须启用制品安全防护。如需查看相关说明,请参阅制品安全防护文档中的准备工作。
创建连接器
控制台
如需在 Google Cloud 控制台中创建连接器,请按以下步骤操作:
- 在 Google Cloud 控制台中,依次前往安全性 > 设置。
- 在制品安全防护卡片上,点击管理设置。
- 点击创建连接器,然后输入连接器的以下详细信息:
- 连接器 ID:为连接器添加 ID。
- 说明:输入连接器的说明。
- CI/CD 平台:从列表中选择相应的 CI/CD 平台。仅在通过所选 CI/CD 平台构建的流水线中使用此连接器。
- 点击创建。
系统会显示一条通知,确认已成功创建连接器。连接器表格列出了可用的连接器。
gcloud CLI
如需使用 gcloud CLI 创建连接器,请运行以下命令:
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:连接器的标识符(例如
my-connector)。 - LOCATION:存储数据的 Security Command Center 位置。仅支持
global。 - ORGANIZATION_ID:组织 ID。
- PROJECT_NUMBER:数字项目编号。
- PIPELINE_TYPE:CI/CD 流水线的类型。必须是以下其中一项:
GOOGLE_CLOUD_BUILDGITHUB_ACTIONSJENKINS_PIPELINE
- DESCRIPTION:连接器的文本说明。
- DISPLAY_NAME:连接器的简单易记的显示名称。
更新连接器
控制台
如需在 Google Cloud 控制台中将政策与连接器相关联,请按以下步骤操作:
- 在 Google Cloud 控制台中,依次前往安全性 > 设置。
- 在制品安全防护卡片上,点击管理设置。
- 在连接器表格中,点击连接器旁边的 ,然后选择添加政策。
- 继续执行相关步骤,以创建或更新制品安全防护政策。如需了解详情,请参阅创建政策。
gcloud CLI
如需使用 gcloud CLI 查看现有连接器并更新其政策关联,请使用以下命令:
列出连接器
gcloud alpha scc artifact-guard connectors list PARENT
替换以下内容:
- PARENT:组织或项目。父资源的接受格式包括:
organizations/ORGANIZATION_ID/locations/LOCATIONprojects/PROJECT_NUMBER/locations/LOCATION
描述连接器
gcloud alpha scc artifact-guard connectors describe CONNECTOR_ID \ --location=LOCATION \ (--organization=ORGANIZATION_ID | --project=PROJECT_NUMBER)
更新连接器政策关联
如需将连接器与政策相关联,请在政策定义文件的流水线范围内添加连接器 ID,然后使用 gcloud alpha scc artifact-guard policies update 命令更新政策。如需了解详情,请参阅创建政策。
删除连接器
控制台
如需在 Google Cloud 控制台中删除连接器,请按以下步骤操作:
- 在 Google Cloud 控制台中,依次前往安全性 > 设置。
- 在制品安全防护卡片上,点击管理设置。
- 在连接器表格中,点击连接器旁边的 ,然后选择删除连接器。
- 按照确认提示删除连接器。点击取消即可中止。
gcloud CLI
如需使用 gcloud CLI 删除连接器,请运行以下命令:
gcloud alpha scc artifact-guard connectors delete CONNECTOR_ID \ --location=LOCATION \ (--organization=ORGANIZATION_ID | --project=PROJECT_NUMBER)
运行评估
GitHub Actions 和 Jenkins 流水线支持漏洞扫描。如需执行评估,您必须执行以下操作:
Secret 配置
在 Google Cloud 外部运行的 CI/CD 流水线可以使用服务账号密钥或工作负载身份联合进行身份验证。如需详细了解如何创建 Secret,请参阅以下内容:
服务账号密钥
工作负载身份联合(适用于 GitHub Actions)
您必须使用以下方法之一将密文添加到 CI/CD 环境:
服务账号密钥方法
一个 Secret:
GCP_CREDENTIALS:您下载的服务账号 JSON 密钥文件的内容。
工作负载身份联合方法
两个密钥:
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@" 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)
此模板适用于使用工作负载身份联合的 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(密令)
- 添加服务账号密钥 (
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(必需)
- Google Cloud 组织 ID。
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 一致性:通过
- 详细说明政策名称。
- 仅提供按严重程度汇总的 CVE 数量。
ArtifactGuard 一致性:失败
- 详细说明了失败原因。
- 仅列出导致失败的特定 CVE。
- 提供按严重程度分类的 CVE 汇总数量。
详细程度:高(详细)
提供发现的所有漏洞的完整列表。
ArtifactGuard 一致性:通过
- 提供政策检测到的所有 CVE 的列表。
- 提供按严重程度细分的 CVE 摘要计数。
- 提供所有检测到的漏洞的完整列表。
ArtifactGuard 一致性:失败
- 包含失败原因。
- 列出导致失败的 CVE。
- 提供政策检测到的所有 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 运行时,然后由后者确定流水线的通过或失败状态。
问题排查
本部分概述了常见错误以及如何解决这些错误。
CreateConnector 失败
| 字段 | 必需/可选 | 限制条件 |
|---|---|---|
name |
必填 | 格式:必须与正则表达式 [a-zA-Z0-9\\-\\s_]+$匹配 匹配以下一项或多项:
长度上限:64 个字符。 |
pipeline_type |
必填 | 必须是以下枚举值之一:
|
description |
可选 | 不得超过 256 个字符。 |
display_name |
可选 | 不得超过 256 个字符。 |
其他错误
下表列出了一些常见错误以及如何解决这些错误。
| 错误消息 | 原因 | 措施/解决方案 |
|---|---|---|
对资源的权限 artifactscanguard.connectors.create 被拒绝 |
用户或服务账号缺少对资源(项目、文件夹或组织)的 artifactscanguard.connectors.create IAM 权限。 |
向调用者授予包含 artifactscanguard.connectors.create 权限的 IAM 角色。 |
status.ErrFailedPrecondition |
即使 Google Cloud 控制台显示该服务已启用,初始配置也可能仍在进行中。 | 向支持团队报告问题。 |
status.ErrInvalidArgument |
字段验证失败 | 确保 CreateConnector 请求满足指定的限制条件。 |