הגדרת שילוב של CI/CD

לקוחות Security Command Center מתקשים לקבל תצוגה מרכזית ומקיפה של תוצאות הסריקה מסביבות Continuous Integration/Continuous Delivery ‏ (CI/CD). היעדר תצוגה מאוחדת מקשה על ניהול נקודות חולשה. השילוב של CI/CD הוא רכיב של הגנה על ארטיפקטים (גרסת טרום-השקה (Preview)) שמאפשר לכם לחבר כמה פייפליינים של CI/CD כדי לזהות נקודות חולשה לאורך מחזור החיים של פיתוח התוכנה.

השילוב של CI/CD תומך ב-GitHub Actions, ב-Cloud Build וב-Jenkins. אחרי החיבור, אפשר להשתמש בכל אחת מהפלטפורמות האלה של CI/CD כדי להגדיר מדיניות הגנה על ארטיפקטים, וכך לקבל תובנות ולשלוט באופן פרואקטיבי במצב האבטחה.

סקירה כללית

שילוב של CI/CD מציע את היתרונות הבאים:

  • אכיפת מדיניות מקצה לקצה: מסייעת להגנה על ארטיפקטים להחיל מדיניות אבטחה עקבית מקוד לענן.
  • זיהוי מקיף של איומים: סריקה לאיתור נקודות חולשה, סודות חשופים, רישיונות בעייתיים וחבילות זדוניות.
  • תאימות רחבה ל-CI/CD: פועל עם Jenkins ו-GitHub Actions.
  • אופטימיזציה ל-CI/CD: קובץ בינארי קל משקל מבטיח פעולה יעילה בסביבות מקומיות.
  • Flexible output: מספק תוצאות בפורמטים JSON ו-SARIF שהם פורמטים סטנדרטיים בתעשייה.
  • תובנות אבטחה מרכזיות: מספקות תצוגות ברורות של התאמה למדיניות ישירות במרכז הבקרה לאבטחה.

הסורק הזה מאכלס את Security Command Center באופן מקורי, ומספק תצוגה מאוחדת של ממצאי אבטחה שמתמקדת בנכסים, החל מיצירת הארטיפקט.

השילוב עם CI/CD עוזר לאכוף את מדיניות ההגנה על ארטיפקטים בכל מחזור החיים של התוכנה. כך מוודאים שהארטיפקטים מאומתים בהתאם לתקנים, החל משלב הבנייה ועד לפריסה.

קהל

שילוב של CI/CD יכול לעזור לבעלי העניין במשימות הבאות:

  • משתמשים עיקריים: צוותי DevOps וצוותי הנדסת פלטפורמות
    • ניהול ושילוב: אחראי על ניהול כלי הסריקה ושילובו ישירות בצינורות עיבוד נתונים של CI/CD.
    • הגדרת כללי מדיניות: הגדרת שלבי הסריקה.
    • מעקב אחרי תאימות: עוקבים אחרי התאימות של הגרסה ועובדים עם אדמינים של אבטחה כדי לשפר את המדיניות.
  • משתמשים משניים: אדמינים של אבטחה
    • יוצרי מדיניות: מגדירים ואוכפים מדיניות אבטחה על סמך חשיבות עסקית.
    • אוטומציה של אכיפה: אוטומציה של אבטחה ב-CI/CD כדי לצמצם את הרעש שנובע מחולשות ולהגדיר קריטריונים לשליטה בגישה.
    • פיקוח: שיתוף פעולה עם DevOps והצעת מרכז שליטה לניהול כדי לעקוב אחרי פגיעויות שמוגנות על ידי שער.
  • משתמשים משניים: מפתחי אפליקציות
    • בדיקה ותיקון: אפשר לבדוק את תוצאות הערכת המדיניות, לבדוק את תוצאות הבנייה ולתקן בעיות אבטחה שסומנו.
    • הערכה: מתחילים בהערכת המדיניות באופן עקיף דרך תהליך build של צינור ה-CI.
    • שמירה על תאימות: שמירה על תאימות לדרישות האבטחה בלי לשבש את תהליכי העבודה של הפיתוח.

מושגים והגדרות חשובים

  • נקודות חולשה וחשיפה נפוצות (CVE): נקודת חולשה באבטחת מחשב שנחשפה לציבור וקיבלה מזהה ייחודי. המזהים האלה עוזרים לעקוב אחרי נקודות חולשה לצורך תיקון.
  • רשימת חומרים (BOM) של תוכנה (SBOM): מלאי של רכיבי תוכנה ויחסי תלות שמתאים לקריאה למחשבים. ה-SBOM כולל מידע על הגרסה, המקור ופרטים רלוונטיים אחרים של כל רכיב. אפשר להשתמש ב-SBOM כדי לזהות CVE וסיכוני אבטחה אחרים.
  • ארטיפקט: פלט של פיתוח תוכנה שעבר אימות וקיבל גרסה, כמו נתונים או פריט שנוצרו במהלך תהליך build.
  • תג Connector: תג לתמונות. כשמעבירים את המחבר מצינור ה-CI לשירות הגנה על ארטיפקטים, הוא קובע אילו מדיניות להפעיל מול התמונה שנבנית.
  • מדיניות CI: מדיניות בנושא פגיעויות שמגדירה כללים או קריטריונים לשליטה בפגיעויות ובחבילות שמותרות בסביבה שלכם.

תהליך עבודה כללי

  1. יצירת מחברים של CI.
  2. יוצרים כללי מדיניות להגנה על ארטיפקטים באמצעות המחברים שהוגדרו בשלב הקודם כדי להגדיר את היקף המדיניות.
  3. הפעלת הערכות של ארטיפקטים.

במהלך ההערכה, נוצרת תמונה והיא נבדקת בהשוואה למדיניות מוגדרת מראש. אם המדיניות נכשלת, ה-build נכשל. מהנדסי DevOps או מהנדסי אפליקציות צריכים לבדוק את פרטי הכשל כדי למצוא את נקודת החולשה הספציפית, לעדכן את התלות בהתאם לפרטי ה-CVE ולהפעיל מחדש את צינור הנתונים.

לפני שמתחילים

כדי להשתמש בשילוב CI/CD, צריך להפעיל את הגנה על ארטיפקטים. הוראות מפורטות מופיעות בקטע לפני שמתחילים במאמרי העזרה בנושא הגנה על ארטיפקטים.

אחרי זה תוכלו ליצור מחברים במסוףGoogle Cloud או באמצעות Google Cloud CLI.

יצירת מחבר במסוף Google Cloud

כדי ליצור מחבר:

  1. במסוף Google Cloud , עוברים אל Security > Settings (אבטחה > הגדרות).

  2. בכרטיס Artifact guard (הגנה על ארטיפקטים), לוחצים על Manage settings (ניהול הגדרות).

  3. לוחצים על Create connector (יצירת מחבר) ומזינים את הפרטים הבאים של המחבר:

    • מזהה המחבר: הוספת מזהה למחבר.
    • תיאור: מזינים תיאור של המחבר.
    • פלטפורמת CI/CD: בוחרים את פלטפורמת ה-CI/CD המתאימה מהרשימה. אפשר להשתמש במחבר הזה רק בצינורות עיבוד נתונים שנבנו באמצעות פלטפורמת ה-CI/CD שסופקה.
  4. לוחצים על יצירה.

הודעה מאשרת שהמחבר נוצר בהצלחה. המחברים הזמינים מפורטים בטבלה Connectors.

כדי להסיר מחבר, לוחצים על ליד המחבר, בוחרים באפשרות מחיקת המחבר ופועלים לפי ההנחיות. לוחצים על ביטול כדי להפסיק את הפעולה.

כדי לקשר מדיניות למחבר, לוחצים על לצד המחבר ובוחרים באפשרות הוספת מדיניות. ממשיכים בשלבים ליצירת מדיניות של הגנה על ארטיפקטים. מידע נוסף זמין במאמר יצירת מדיניות.

יצירת מחבר באמצעות Google Cloud CLI

בקטע הזה מפורטות הפקודות של ה-CLI של gcloud שזמינות לסריקת ארטיפקטים של CI/CD, ומוסבר איך להשתמש בהן.

דרישות מוקדמות ל-Google Cloud CLI

  • מוודאים שגרסת ה-CLI של gcloud היא 559.0.0 ואילך.
  • מגדירים את הפרויקט כפרויקט ההגדרות.

כדי לעשות זאת, מריצים את הפקודות הבאות ב-CLI של gcloud:

   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: המזהה של מחבר שרוצים ליצור.
  • 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: המזהה של מחבר שרוצים לקבל תיאור שלו.

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: המזהה של מחבר שרוצים למחוק.

הרצת הערכה

בדיקת נקודות חולשה נתמכת ב-GitHub Actions ובצינורות Jenkins. כדי לבצע הערכה, צריך לבצע את הפעולות הבאות:

  1. יצירת סודות לאימות.
  2. יוצרים קובץ תבנית שילוב שספציפי לפייפליין.
  3. התחלת תהליך הבדיקה

הגדרת סודות

צינורות CI/CD שפועלים מחוץ ל- Google Cloud יכולים לבצע אימות באמצעות מפתחות של חשבון שירות או איחוד שירותי אימות הזהות של עומסי עבודה. הוראות מפורטות ליצירת סודות מופיעות במאמרים הבאים:

מפתחות לחשבונות שירות

איחוד שירותי אימות הזהות של עומסי עבודה (ל-GitHub Actions)

צריך להוסיף סודות לסביבת ה-CI/CD באחת מהשיטות הבאות:

שימוש במפתח של חשבון שירות

סוד אחד:

  • 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@developer.gserviceaccount.com"
    
    options:
          logging: CLOUD_LOGGING_ONLY
    

פעולות GitHub (סודי)

התבנית הזו מיועדת לפעולות GitHub שמשתמשות במפתח סודי.

  • מוסיפים את המפתח הסודי של חשבון השירות (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 ‏ (WIF)

התבנית הזו מיועדת לפעולות GitHub שמשתמשות באיחוד שירותי אימות הזהות של עומסי עבודה.

  • מוסיפים את ספק מאגרי זהויות של עומסי עבודה בסוד של GitHub‏ (GCP_WORKLOAD_IDENTITY_PROVIDER).
  • מוסיפים את חשבון השירות בסוד של 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 מזהה הפרויקט שמשמש לאימות ולתצורה.

AR_REPOSITORY (אופציונלי)

  • מציין את השם של מאגר Artifact Registry שבו התמונה תפורסם אם הבנייה תצליח.

ORGANIZATION_ID (חובה)

  • מזהה הארגון Google Cloud .

CONNECTOR_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.

התחלת הערכה

במהלך תהליך build, שיטה שמבוססת על 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) לא יכול לחרוג מ-100MB.
    • התמונה צריכה להכיל 500 כתובות pURL או פחות.
  • עד 1,200 בקשות API לדקה (20 QPS) לכל פרויקט צרכן לכל השיטות בשירות הגנה על ארטיפקטים.
  • SLO: הערכת הארטיפקט נמשכת כשתי דקות.

פתרון בעיות

בקטע הזה מפורטות שגיאות נפוצות ומוסבר איך לפתור אותן.

כשלים ב-CreateConnector

שדה חובה/אופציונלי מגבלות
name חובה פורמט: הערך חייב להתאים לביטוי הרגולרי [a-zA-Z0-9\\-\\s_]+$
הערך חייב להתאים לאחד או יותר מהערכים הבאים:
  • אותיות (רישיות או קטנות)
  • ספרות
  • מקף -
  • רווח לבן (‎\s)
  • קו תחתון _


אורך מקסימלי: 64 תווים.
pipeline_type חובה צריך להזין אחד מהערכים הבאים של enum:
  • JENKINS_PIPELINE
  • GITHUB_ACTIONS
  • GOOGLE_CLOUD_BUILD
description אופציונלי האורך המקסימלי הוא 256 תווים.
display_name אופציונלי האורך המקסימלי הוא 256 תווים.

שגיאות אחרות

בטבלה הבאה מפורטות כמה שגיאות נפוצות והצעות לפתרונות.

הודעת השגיאה הסיבה פעולה/פתרון
ההרשאה artifactscanguard.connectors.create נדחתה במשאב למשתמש או לחשבון השירות חסרה הרשאת IAM במשאב (פרויקט, תיקייה או ארגון) artifactscanguard.connectors.create . נותנים למשתמש שקורא ל-API תפקיד ב-IAM שכולל את ההרשאה artifactscanguard.connectors.create.
status.ErrFailedPrecondition יכול להיות שתהליך ההצטרפות עדיין מתבצע גם אם Google Cloud המסוף מציג את השירות כמופעל. דיווח על בעיה לצוות התמיכה.
status.ErrInvalidArgument כשלים באימות השדות מוודאים שהבקשה CreateConnector עומדת במגבלות שצוינו.