פיתוח מאובטח & פריסה באמצעות Cloud Build, Artifact Registry ו-GKE

1. מבוא

שירות Container Analysis מספק סריקות לאיתור נקודות חולשה ואחסון מטא-נתונים של קונטיינרים. שירות הסריקה מבצע סריקות לאיתור נקודות חולשה בתמונות ב-Artifact Registry וב-Container Registry, ואז מאחסן את המטא-נתונים שמתקבלים ומאפשר גישה אליהם דרך API. אחסון מטא-נתונים מאפשר לכם לאחסן מידע ממקורות שונים, כולל בדיקת נקודות חולשה, שירותי Google Cloud וספקים של צד שלישי.

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

  • כשמפעילים את הסריקה האוטומטית, הסריקה מופעלת אוטומטית בכל פעם שמעלים תמונה חדשה ל-Artifact Registry או ל-Container Registry. המידע על נקודות החולשה מתעדכן כל הזמן כשמתגלות נקודות חולשה חדשות.
  • כשמפעילים את הסריקה לפי דרישה, צריך להריץ פקודה כדי לסרוק תמונה מקומית או תמונה ב-Artifact Registry או ב-Container Registry. סריקה על פי דרישה מאפשרת לכם גמישות לגבי מועד הסריקה של הקונטיינרים. לדוגמה, אתם יכולים לסרוק תמונה שנבנתה באופן מקומי ולתקן פגיעויות לפני שאתם מאחסנים אותה במאגר. תוצאות הסריקה זמינות למשך עד 48 שעות אחרי שהסריקה מסתיימת, והמידע על נקודות החולשה לא מתעדכן אחרי הסריקה.

עם Container Analysis משולב בצינור העיבוד של CI/CD, תוכלו לקבל החלטות על סמך המטא-נתונים האלה. לדוגמה, אפשר להשתמש ב-Binary Authorization כדי ליצור מדיניות פריסה שמאפשרת פריסות רק לתמונות תואמות ממאגרי תמונות מהימנים.

מה תלמדו

  • איך מפעילים סריקה אוטומטית
  • איך מבצעים סריקה על פי דרישה
  • איך משלבים סריקה בצינור build
  • איך חותמים על תמונות שאושרו
  • איך משתמשים בבקרי הרשאות כניסה של GKE כדי לחסום תמונות
  • איך מגדירים את GKE כך שיוכלו להשתמש רק בתמונות חתומות שאושרו

2. הגדרה ודרישות

הגדרת סביבה בקצב עצמי

  1. נכנסים ל-מסוף Google Cloud ויוצרים פרויקט חדש או משתמשים בפרויקט קיים. אם עדיין אין לכם חשבון Gmail או Google Workspace, אתם צריכים ליצור חשבון.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • שם הפרויקט הוא השם המוצג של הפרויקט הזה למשתתפים. זו מחרוזת תווים שלא נמצאת בשימוש ב-Google APIs. אפשר לעדכן את המיקום הזה בכל שלב.
  • מזהה הפרויקט הוא ייחודי לכל הפרויקטים ב-Google Cloud, והוא קבוע (אי אפשר לשנות אותו אחרי שהוא מוגדר). מסוף Cloud יוצר באופן אוטומטי מחרוזת ייחודית, ובדרך כלל לא צריך לדעת מה היא. ברוב ה-Codelabs, תצטרכו להפנות למזהה הפרויקט (בדרך כלל הוא מסומן כ-PROJECT_ID). אם אתם לא אוהבים את המזהה שנוצר, אתם יכולים ליצור מזהה אקראי אחר. אפשר גם לנסות שם משתמש משלכם ולבדוק אם הוא זמין. אי אפשר לשנות את ההגדרה הזו אחרי השלב הזה, והיא תישאר כזו למשך הפרויקט.
  • לידיעתכם, יש ערך שלישי, מספר פרויקט, שחלק מממשקי ה-API משתמשים בו. במאמרי העזרה מפורט מידע נוסף על שלושת הערכים האלה.
  1. בשלב הבא, תצטרכו להפעיל את החיוב במסוף Cloud כדי להשתמש במשאבי Cloud או בממשקי API של Cloud. העלות של התרגול הזה לא אמורה להיות גבוהה, ואולי אפילו לא תצטרכו לשלם בכלל. כדי להשבית את המשאבים ולא לחייב אתכם מעבר למדריך הזה, אתם יכולים למחוק את המשאבים שיצרתם או למחוק את כל הפרויקט. משתמשים חדשים ב-Google Cloud זכאים לתוכנית תקופת ניסיון בחינם בשווי 300$.

הפעלת Cloud Shell Editor

ה-Lab הזה תוכנן ונבדק לשימוש עם Google Cloud Shell Editor. כדי לגשת לכלי העריכה,

  1. נכנסים לפרויקט Google בכתובת https://console.cloud.google.com.
  2. בפינה השמאלית העליונה, לוחצים על סמל העורך של Cloud Shell.

8560cc8d45e8c112.png

  1. ייפתח חלונית חדשה בחלק התחתון של החלון.

הגדרת הסביבה

ב-Cloud Shell, מגדירים את מזהה הפרויקט ואת מספר הפרויקט. שומרים אותם כמשתנים PROJECT_ID ו-PROJECT_ID.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
    --format='value(projectNumber)')

הפעלת שירותים

מפעילים את כל השירותים הנדרשים:

gcloud services enable \
  cloudkms.googleapis.com \
  cloudbuild.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  artifactregistry.googleapis.com \
  containerscanning.googleapis.com \
  ondemandscanning.googleapis.com \
  binaryauthorization.googleapis.com 

יצירת מאגר של Artifact Registry

בשיעור ה-Lab הזה תשתמשו ב-Artifact Registry כדי לאחסן ולסרוק את התמונות שלכם. יוצרים את המאגר באמצעות הפקודה הבאה.

gcloud artifacts repositories create artifact-scanning-repo \
  --repository-format=docker \
  --location=us-central1 \
  --description="Docker repository"

מגדירים את Docker כך שישתמש בפרטי הכניסה שלכם ב-gcloud כשניגשים אל Artifact Registry.

gcloud auth configure-docker us-central1-docker.pkg.dev

3. סריקה אוטומטית

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

יצירה של ספריית עבודה ומעבר אליה

mkdir vuln-scan && cd vuln-scan

הגדרת תמונה לדוגמה

יוצרים קובץ בשם Dockerfile עם התוכן הבא.

cat > ./Dockerfile << EOF
FROM gcr.io/google-appengine/debian9@sha256:ebffcf0df9aa33f342c4e1d4c8428b784fc571cdf6fbab0b31330347ca8af97a

# System
RUN apt update && apt install python3-pip -y

# App
WORKDIR /app
COPY . ./

RUN pip3 install Flask==1.1.4
RUN pip3 install gunicorn==20.1.0

CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

EOF

יוצרים קובץ בשם main.py עם התוכן הבא:

cat > ./main.py << EOF
import os
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    name = os.environ.get("NAME", "Worlds")
    return "Hello {}!".format(name)

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
EOF

יצירה של קובץ האימג' ודחיפה שלו ל-AR

משתמשים ב-Cloud Build כדי ליצור את הקונטיינר ולהעביר אותו אוטומטית בדחיפה ל-Artifact Registry. שימו לב לתג bad בתמונה. כך תוכלו לזהות אותו בשלבים הבאים.

gcloud builds submit . -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:bad

בדיקת פרטי התמונה

אחרי שתהליך ה-build מסתיים, בודקים את התוצאות של קובץ האימג' והפגיעות בלוח הבקרה של Artifact Registry.

  1. פותחים את Artifact Registry ב-Cloud Console.
  2. כדי לראות את התוכן, לוחצים על artifact-scanning-repo.
  3. לחיצה על פרטי התמונה
  4. לחיצה על התקציר האחרון של התמונה
  5. בסיום הסריקה, לוחצים על הכרטיסייה 'פגיעויות' של התמונה.

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

361be7b3bf293fca.png

האפשרות להפעיל סריקה אוטומטית מופעלת כברירת מחדל. כדאי לעיין בהגדרות של Artifact Registry כדי לראות איך אפשר להפעיל או להשבית את הסריקה האוטומטית.

4. On-Demand Scanning

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

יצירת תמונה

בשלב הזה תשתמשו ב-Docker מקומי כדי ליצור את קובץ האימג' במטמון המקומי.

docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image .

סריקת התמונה

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

gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --format="value(response.scan)" > scan_id.txt

בדיקת קובץ הפלט

כדאי לעיין בתוצאות של השלב הקודם, שנשמרו בקובץ scan_id.txt. שימו לב למיקום הדוח של תוצאות הסריקה בשרת המטא-נתונים.

cat scan_id.txt

בדיקת תוצאות סריקה מפורטות

כדי לראות את התוצאות בפועל של הסריקה, משתמשים בפקודה list-vulnerabilities במיקום הדוח שצוין בקובץ הפלט.

gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt) 

הפלט מכיל כמות משמעותית של נתונים על כל נקודות החולשה בתמונה.

סימון בעיות קריטיות

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

export SEVERITY=CRITICAL

gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt) --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq ${SEVERITY}; then echo "Failed vulnerability check for ${SEVERITY} level"; else echo "No ${SEVERITY} Vulnerabilities found"; fi

הפלט מהפקודה הזו יהיה

Failed vulnerability check for CRITICAL level

5. סריקת פייפליין Build

בקטע הזה תיצרו פייפליין אוטומטי של build שיבנה את קובץ אימג' של קונטיינר, יסרוק אותו ואז יעריך את התוצאות. אם לא נמצאו פגיעויות קריטיות, התמונה תועבר למאגר. אם יימצאו פגיעויות קריטיות, ה-build ייכשל והתהליך ייפסק.

מתן גישה לחשבון השירות ב-Cloud Build

ל-Cloud Build צריכות להיות הרשאות גישה ל-API של סריקה לפי דרישה. נותנים גישה באמצעות הפקודות הבאות.

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/iam.serviceAccountUser"
        
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/ondemandscanning.admin"

יצירת צינור עיבוד נתונים של Cloud Build

הפקודה הבאה תיצור קובץ cloudbuild.yaml בספרייה שלכם, שישמש לתהליך האוטומטי. בדוגמה הזו, השלבים מוגבלים לתהליך build של הקונטיינר. עם זאת, בפועל, בנוסף לשלבי הקונטיינר, צריך לכלול הוראות ובדיקות ספציפיות לאפליקציה.

יוצרים את הקובץ באמצעות הפקודה הבאה.

cat > ./cloudbuild.yaml << EOF
steps:

# build
- id: "build"
  name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', '.']
  waitFor: ['-']

#Run a vulnerability scan at _SECURITY level
- id: scan
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
    (gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --location us \
    --format="value(response.scan)") > /workspace/scan_id.txt

#Analyze the result of the scan
- id: severity check
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
      gcloud artifacts docker images list-vulnerabilities \$(cat /workspace/scan_id.txt) \
      --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq CRITICAL; \
      then echo "Failed vulnerability check for CRITICAL level" && exit 1; else echo "No CRITICAL vulnerability found, congrats !" && exit 0; fi

#Retag
- id: "retag"
  name: 'gcr.io/cloud-builders/docker'
  args: ['tag',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#pushing to artifact registry
- id: "push"
  name: 'gcr.io/cloud-builders/docker'
  args: ['push',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']

images:
  - us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image
EOF

הפעלת צינור ה-CI

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

gcloud builds submit

הבדיקה נכשלה

הגרסה ששלחת עכשיו תיכשל כי התמונה מכילה פגיעויות קריטיות.

בדיקת הכשל ב-build בדף Cloud Build History

תיקון הפגיעות

מעדכנים את קובץ Dockerfile כדי להשתמש בתמונת בסיס שלא מכילה פגיעויות קריטיות.

מחליפים את קובץ ה-Dockerfile כדי להשתמש באימג' של Debian 10 באמצעות הפקודה הבאה

cat > ./Dockerfile << EOF
from python:3.8-slim  

# App
WORKDIR /app
COPY . ./

RUN pip3 install Flask==2.1.0
RUN pip3 install gunicorn==20.1.0

CMD exec gunicorn --bind :\$PORT --workers 1 --threads 8 main:app

EOF

מריצים את תהליך ה-CI עם התמונה הטובה

שולחים את ה-build לעיבוד כדי לוודא שה-build יצליח אם לא יימצאו פגיעויות ברמת חומרה CRITICAL.

gcloud builds submit

בדיקת הצלחת הבנייה

ה-build ששלחתם עכשיו יצליח כי התמונה המעודכנת לא מכילה פגיעויות קריטיות.

בדיקת ההצלחה של הבנייה בדף ההיסטוריה של Cloud Build

בדיקת תוצאות הסריקה

בדיקת התמונה הטובה ב-Artifact Registry

  1. פותחים את Artifact Registry ב-Cloud Console.
  2. כדי לראות את התוכן, לוחצים על artifact-scanning-repo.
  3. לחיצה על פרטי התמונה
  4. לחיצה על התקציר האחרון של התמונה
  5. לוחצים על הכרטיסייה 'פגיעויות' של התמונה.

6. חתימה על תמונות

יצירת הערה של מאשר

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

יצירת הערה

cat > ./vulnz_note.json << EOM
{
  "attestation": {
    "hint": {
      "human_readable_name": "Container Vulnerabilities attestation authority"
    }
  }
}
EOM

שמירת ההערה

NOTE_ID=vulnz_note

curl -vvv -X POST \
    -H "Content-Type: application/json"  \
    -H "Authorization: Bearer $(gcloud auth print-access-token)"  \
    --data-binary @./vulnz_note.json  \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/?noteId=${NOTE_ID}"

אימות ההערה

curl -vvv  \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/${NOTE_ID}"

יצירת גורם מאמת (attestor)

המאמתים משמשים לביצוע תהליך החתימה בפועל של התמונה, והם יצרפו מופע של ההערה לתמונה לצורך אימות מאוחר יותר. יוצרים את המאשר לשימוש מאוחר יותר.

יצירת מאמת

ATTESTOR_ID=vulnz-attestor

gcloud container binauthz attestors create $ATTESTOR_ID \
    --attestation-authority-note=$NOTE_ID \
    --attestation-authority-note-project=${PROJECT_ID}

אימות המאשר

gcloud container binauthz attestors list

שימו לב שהשורה האחרונה מציינת NUM_PUBLIC_KEYS: 0 שתספקו מפתחות בשלב מאוחר יותר

חשוב גם לדעת ש-Cloud Build יוצר באופן אוטומטי את built-by-cloud-build המאשר בפרויקט כשמריצים build שיוצר תמונות. לכן, הפקודה שלמעלה מחזירה שני מאמתים, vulnz-attestor ו-built-by-cloud-build. אחרי שגרסאות ה-build של קובצי האימג' נוצרות בהצלחה, מערכת Cloud Build חותמת עליהם באופן אוטומטי ויוצרת להם אישורים.

הוספת תפקיד IAM

לחשבון השירות של Binary Authorization צריכות להיות הרשאות לצפייה בהערות האישור. הענקת הגישה באמצעות קריאה ל-API

PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}"  --format="value(projectNumber)")

BINAUTHZ_SA_EMAIL="service-${PROJECT_NUMBER}@gcp-sa-binaryauthorization.iam.gserviceaccount.com"


cat > ./iam_request.json << EOM
{
  'resource': 'projects/${PROJECT_ID}/notes/${NOTE_ID}',
  'policy': {
    'bindings': [
      {
        'role': 'roles/containeranalysis.notes.occurrences.viewer',
        'members': [
          'serviceAccount:${BINAUTHZ_SA_EMAIL}'
        ]
      }
    ]
  }
}
EOM

שימוש בקובץ ליצירת מדיניות IAM

curl -X POST  \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    --data-binary @./iam_request.json \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/${NOTE_ID}:setIamPolicy"

הוספת מפתח KMS

למאמת נדרשים מפתחות קריפטוגרפיים כדי לצרף את ההערה ולספק חתימות שניתנות לאימות. בשלב הזה תיצרו מפתחות ותאחסנו אותם ב-KMS כדי ש-Cloud Build יוכל לגשת אליהם בהמשך.

קודם מוסיפים כמה משתני סביבה כדי לתאר את המפתח החדש

KEY_LOCATION=global
KEYRING=binauthz-keys
KEY_NAME=codelab-key
KEY_VERSION=1

יצירת אוסף מפתחות להחזקת קבוצה של מפתחות

gcloud kms keyrings create "${KEYRING}" --location="${KEY_LOCATION}"

יצירת זוג מפתחות אסימטריים חדש לחתימה עבור גורם מאמת (attestor)

gcloud kms keys create "${KEY_NAME}" \
    --keyring="${KEYRING}" --location="${KEY_LOCATION}" \
    --purpose asymmetric-signing   \
    --default-algorithm="ec-sign-p256-sha256"

המפתח אמור להופיע בדף KMS במסוף Google Cloud.

עכשיו משייכים את המפתח למאמת באמצעות הפקודה gcloud binauthz:

gcloud beta container binauthz attestors public-keys add  \
    --attestor="${ATTESTOR_ID}"  \
    --keyversion-project="${PROJECT_ID}"  \
    --keyversion-location="${KEY_LOCATION}" \
    --keyversion-keyring="${KEYRING}" \
    --keyversion-key="${KEY_NAME}" \
    --keyversion="${KEY_VERSION}"

אם מדפיסים שוב את רשימת הרשויות, אמור להופיע בה מפתח רשום:

gcloud container binauthz attestors list

יצירת אישור חתום

בשלב הזה, התכונות שמאפשרות לכם לחתום על תמונות מוגדרות. משתמשים ב-Attestor שיצרתם קודם כדי לחתום על קובץ האימג' של הקונטיינר שעבדתם איתו

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image

DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:latest \
    --format='get(image_summary.digest)')

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

gcloud beta container binauthz attestations sign-and-create  \
    --artifact-url="${CONTAINER_PATH}@${DIGEST}" \
    --attestor="${ATTESTOR_ID}" \
    --attestor-project="${PROJECT_ID}" \
    --keyversion-project="${PROJECT_ID}" \
    --keyversion-location="${KEY_LOCATION}" \
    --keyversion-keyring="${KEYRING}" \
    --keyversion-key="${KEY_NAME}" \
    --keyversion="${KEY_VERSION}"

במונחים של Container Analysis, הפעולה הזו תיצור אירוע חדש ותצרף אותו להערה של גורם מאמת (attestor). כדי לוודא שהכול פעל כמו שצריך, אפשר להציג את האישורים

gcloud container binauthz attestations list \
   --attestor=$ATTESTOR_ID --attestor-project=${PROJECT_ID}

7. חתימה באמצעות Cloud Build

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

בקטע הזה נגדיר את Cloud Build כדי לאמת תמונות באופן אוטומטי

תפקידים

מוסיפים את התפקיד Binary Authorization Attestor Viewer לחשבון השירות של Cloud Build:

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/binaryauthorization.attestorsViewer

הוספת התפקיד Cloud KMS CryptoKey Signer/Verifier לחשבון השירות של Cloud Build (חתימה מבוססת-KMS):

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/cloudkms.signerVerifier

מוסיפים את התפקיד צירוף מופעי Container Analysis להערות לחשבון השירות של Cloud Build:

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/containeranalysis.notes.attacher

הכנת שלב Custom Build ב-Cloud Build

כדי לפשט את תהליך האימות, נשתמש בשלב Custom Build ב-Cloud Build. ‫Google מספקת את שלב ה-Build המותאם אישית הזה, שמכיל פונקציות עזר לייעול התהליך. לפני שמשתמשים בקוד של שלב בנייה בהתאמה אישית, צריך לבנות אותו בתוך קונטיינר ולהעביר אותו בדחיפה ל-Cloud Build. כדי לעשות זאת, מריצים את הפקודות הבאות:

git clone https://github.com/GoogleCloudPlatform/cloud-builders-community.git
cd cloud-builders-community/binauthz-attestation
gcloud builds submit . --config cloudbuild.yaml
cd ../..
rm -rf cloud-builders-community

הוספת שלב חתימה לקובץ cloudbuild.yaml

בשלב הזה תוסיפו את שלב האימות לצינור Cloud Build שיצרתם קודם.

  1. בודקים את השלב החדש שרוצים להוסיף.

לצפייה בלבד. אין להעתיק

#Sign the image only if the previous severity check passes
- id: 'create-attestation'
  name: 'gcr.io/${PROJECT_ID}/binauthz-attestation:latest'
  args:
    - '--artifact-url'
    - 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image'
    - '--attestor'
    - 'projects/${PROJECT_ID}/attestors/$ATTESTOR_ID'
    - '--keyversion'
    - 'projects/${PROJECT_ID}/locations/$KEY_LOCATION/keyRings/$KEYRING/cryptoKeys/$KEY_NAME/cryptoKeyVersions/$KEY_VERSION'
  1. מחליפים את הקובץ cloudbuild.yaml בצינור העדכני המלא.
cat > ./cloudbuild.yaml << EOF
steps:

# build
- id: "build"
  name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', '.']
  waitFor: ['-']

#Run a vulnerability scan at _SECURITY level
- id: scan
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
    (gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --location us \
    --format="value(response.scan)") > /workspace/scan_id.txt

#Analyze the result of the scan
- id: severity check
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
      gcloud artifacts docker images list-vulnerabilities \$(cat /workspace/scan_id.txt) \
      --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq CRITICAL; \
      then echo "Failed vulnerability check for CRITICAL level" && exit 1; else echo "No CRITICAL vulnerability found, congrats !" && exit 0; fi

#Retag
- id: "retag"
  name: 'gcr.io/cloud-builders/docker'
  args: ['tag',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#pushing to artifact registry
- id: "push"
  name: 'gcr.io/cloud-builders/docker'
  args: ['push',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#Sign the image only if the previous severity check passes
- id: 'create-attestation'
  name: 'gcr.io/${PROJECT_ID}/binauthz-attestation:latest'
  args:
    - '--artifact-url'
    - 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good'
    - '--attestor'
    - 'projects/${PROJECT_ID}/attestors/$ATTESTOR_ID'
    - '--keyversion'
    - 'projects/${PROJECT_ID}/locations/$KEY_LOCATION/keyRings/$KEYRING/cryptoKeys/$KEY_NAME/cryptoKeyVersions/$KEY_VERSION'



images:
  - us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good
EOF

הרצת ה-Build

gcloud builds submit

בדיקת ה-build בהיסטוריה של Cloud Build

פותחים את Cloud Console ועוברים אל הדף Cloud Build History. בודקים את הגרסה האחרונה ואת ההפעלה המוצלחת של שלבי ה-build.

8. מדיניות בקרה על אישור בקשות

Binary Authorization היא תכונה ב-GKE וב-Cloud Run שמאפשרת לאמת כללים לפני שמאפשרים להריץ קובץ אימג' של קונטיינר. האימות מופעל בכל בקשה להפעלת תמונה, בין אם היא מגיעה מצינור CI/CD מהימן או ממשתמש שמנסה לפרוס תמונה באופן ידני. היכולת הזו מאפשרת לכם לאבטח את סביבות זמן הריצה בצורה יעילה יותר מאשר בדיקות של צינור עיבוד נתונים של CI/CD בלבד.

כדי להבין את היכולת הזו, תשנו את מדיניות ברירת המחדל של GKE כדי לאכוף כלל הרשאה מחמיר.

יצירת אשכול GKE

יוצרים את אשכול GKE:

gcloud beta container clusters create binauthz \
    --zone us-central1-a  \
    --binauthz-evaluation-mode=PROJECT_SINGLETON_POLICY_ENFORCE

מאפשרים ל-Cloud Build לפרוס את האשכול הזה:

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/container.developer"

מדיניות הרשאות

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

  1. בדיקת מדיניות קיימת
gcloud container binauthz policy export
  1. שימו לב שמדיניות האכיפה מוגדרת לערך ALWAYS_ALLOW

evaluationMode: ALWAYS_ALLOW

  1. פריסת דוגמה כדי לוודא שאפשר לפרוס כל דבר
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. אימות הפריסה
kubectl get pods

יוצג הפלט הבא

161db370d99ffb13.png

  1. מחיקת פריסה
kubectl delete pod hello-server

מדיניות דחייה של כל הבקשות

עכשיו מעדכנים את המדיניות כך שלא תהיה אפשרות להציג תמונות.

  1. ייצוא המדיניות הנוכחית לקובץ שאפשר לערוך
gcloud container binauthz policy export  > policy.yaml
  1. שנה את המדיניות

בכלי לעריכת טקסט, משנים את הערך של evaluationMode מ-ALWAYS_ALLOW ל-ALWAYS_DENY.

edit policy.yaml

קובץ ה-YAML של המדיניות צריך להיראות כך:

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_DENY
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. פותחים את הטרמינל, מחילים את המדיניות החדשה וממתינים כמה שניות עד שהשינוי יתעדכן.
gcloud container binauthz policy import policy.yaml
  1. ניסיון לפרוס עומס עבודה לדוגמה
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. הפריסה נכשלת עם ההודעה הבאה
Error from server (VIOLATES_POLICY): admission webhook "imagepolicywebhook.image-policy.k8s.io" denied the request: Image gcr.io/google-samples/hello-app:1.0 denied by Binary Authorization default admission rule. Denied by always_deny admission rule

חזרה למדיניות שמאפשרת הכול

לפני שעוברים לקטע הבא, חשוב להחזיר את השינויים במדיניות

  1. שנה את המדיניות

בכלי לעריכת טקסט, משנים את הערך של evaluationMode מ-ALWAYS_DENY ל-ALWAYS_ALLOW.

edit policy.yaml

קובץ ה-YAML של המדיניות צריך להיראות כך:

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_ALLOW
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. החלת המדיניות שהוחזרה
gcloud container binauthz policy import policy.yaml

9. חסימת נקודות חולשה ב-GKE

בקטע הזה תיישמו את מה שלמדתם עד עכשיו ותיצרו פייפליין CI/CD עם Cloud Build שסורק את התמונות, בודק אם יש נקודות חולשה לפני שחותם על התמונה ומנסה לפרוס אותה. ‫GKE ישתמש ב-Binary Authorization כדי לוודא שיש לאימג' חתימה מבדיקת נקודות חולשה לפני שיאפשר להפעיל את האימג'.

d5c41bb89e22fd61.png

עדכון המדיניות של GKE לדרישת אישור

כדי לדרוש שהתמונות ייחתמו על ידי גורם מאמת (attestor), מוסיפים clusterAdmissionRules למדיניות GKE BinAuth

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

COMPUTE_ZONE=us-central1-a

cat > binauth_policy.yaml << EOM
defaultAdmissionRule:
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
  evaluationMode: ALWAYS_DENY
globalPolicyEvaluationMode: ENABLE
clusterAdmissionRules:
  ${COMPUTE_ZONE}.binauthz:
    evaluationMode: REQUIRE_ATTESTATION
    enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
    requireAttestationsBy:
    - projects/${PROJECT_ID}/attestors/vulnz-attestor
EOM

החלת המדיניות

gcloud beta container binauthz policy import binauth_policy.yaml

ניסיון לפרוס את התמונה הלא חתומה

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

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

קבלת תקציר התמונה של התמונה הגרועה

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image


DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:bad \
    --format='get(image_summary.digest)')

שימוש ב-digest בהגדרות של Kubernetes

cat > deploy.yaml << EOM
apiVersion: v1
kind: Service
metadata:
  name: deb-httpd
spec:
  selector:
    app: deb-httpd
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deb-httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deb-httpd
  template:
    metadata:
      labels:
        app: deb-httpd
    spec:
      containers:
      - name: deb-httpd
        image: ${CONTAINER_PATH}@${DIGEST}
        ports:
        - containerPort: 8080
        env:
          - name: PORT
            value: "8080"

EOM

ניסיון לפרוס את האפליקציה ב-GKE

kubectl apply -f deploy.yaml

בודקים את עומס העבודה במסוף ורושמים את השגיאה שמציינת שהפריסה נדחתה:

No attestations found that were valid and signed by a key trusted by the attestor

פריסת תמונה חתומה

קבלת תקציר התמונה של התמונה הגרועה

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image


DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:good \
    --format='get(image_summary.digest)')

שימוש ב-digest בהגדרות של Kubernetes

cat > deploy.yaml << EOM
apiVersion: v1
kind: Service
metadata:
  name: deb-httpd
spec:
  selector:
    app: deb-httpd
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deb-httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deb-httpd
  template:
    metadata:
      labels:
        app: deb-httpd
    spec:
      containers:
      - name: deb-httpd
        image: ${CONTAINER_PATH}@${DIGEST}
        ports:
        - containerPort: 8080
        env:
          - name: PORT
            value: "8080"

EOM

פריסת האפליקציה ב-GKE

kubectl apply -f deploy.yaml

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

10. מעולה!

כל הכבוד, סיימתם את ה-Codelab!

הנושאים שדיברנו עליהם:

  • איך מפעילים סריקה אוטומטית
  • איך מבצעים סריקה על פי דרישה
  • איך משלבים סריקה בצינור build
  • איך חותמים על תמונות שאושרו
  • איך משתמשים בבקרי הרשאות כניסה של GKE כדי לחסום תמונות
  • איך מגדירים את GKE כך שיוכלו להשתמש רק בתמונות חתומות שאושרו

השלב הבא:

הסרת המשאבים

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

מחיקת הפרויקט

הדרך הקלה ביותר לבטל את החיוב היא למחוק את הפרויקט שיצרתם בשביל המדריך.