פתרון בעיות באימות ב-GKE

בעיות באימות ב-Google Kubernetes Engine ‏ (GKE) יכולות למנוע מבעלי הרשאות, כמו משתמשים ועומסי עבודה, לגשת לשרת ה-API של Kubernetes, או להפריע לגישה בין משאבי GKE לבין שירותים אחרים של Cloud de Confiance by S3NS .

במאמר הזה מוסבר איך לאבחן בעיות באימות, לתקן שגיאות ב-Kubernetes RBAC וב-IAM, ולפתור בעיות ב-איחוד זהויות של עומסי עבודה ל-GKE.

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

RBAC ו-IAM

חשבונות IAM מאומתים לא מצליחים לבצע פעולות בתוך האשכול

הבעיה הבאה מתרחשת כשמנסים לבצע פעולה באשכול, אבל GKE לא מצליח למצוא מדיניות RBAC שמאשרת את הפעולה. ‫GKE מנסה למצוא מדיניות הרשאות ב-IAM שמעניקה את אותה הרשאה. אם הפעולה נכשלת, מוצגת הודעת שגיאה דומה לזו:

Error from server (Forbidden): roles.rbac.authorization.k8s.io is forbidden:
User "example-account@example-project.s3ns.iam.gserviceaccount.com" cannot list resource "roles" in
API group "rbac.authorization.k8s.io" in the namespace "kube-system": requires
one of ["container.roles.list"] permission(s).

כדי לפתור את הבעיה, צריך להשתמש במדיניות RBAC כדי להעניק את ההרשאות לפעולה שניסיתם לבצע. לדוגמה, כדי לפתור את הבעיה בדוגמה הקודמת, צריך להעניק תפקיד עם ההרשאה list לאובייקטים roles במרחב השמות kube-system. הוראות מפורטות זמינות במאמר בנושא הרשאת פעולות באשכולות באמצעות בקרת גישה מבוססת-תפקידים.

איחוד זהויות של עומסי עבודה ל-GKE

ל-Pod אין אפשרות לאמת את Cloud de Confiance by S3NS

אם לא ניתן לאמת את האפליקציה ב- Cloud de Confiance by S3NS, צריך לוודא שההגדרות הבאות מוגדרות בצורה נכונה:

  1. צריך לוודא שהפעלתם את IAM Service Account Credentials API בפרויקט שמכיל את אשכול GKE.

    הפעלת IAM Credentials API

  2. מוודאים שאיחוד הזהויות של עומסי עבודה ל-GKE מופעל באשכול על ידי בדיקה אם מוגדר בו מאגר זהויות של עומסי עבודה:

    gcloud container clusters describe CLUSTER_NAME \
        --format="value(workloadIdentityConfig.workloadPool)"
    

    מחליפים את CLUSTER_NAME בשם של אשכול GKE.

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

  3. מוודאים ששרת המטא-נתונים של GKE מוגדר במאגר הצמתים שבו האפליקציה פועלת:

    gcloud container node-pools describe NODEPOOL_NAME \
        --cluster=CLUSTER_NAME \
        --format="value(config.workloadMetadataConfig.mode)"
    

    מחליפים את מה שכתוב בשדות הבאים:

    • NODEPOOL_NAME בשם של מאגר הצמתים.
    • CLUSTER_NAME בשם של אשכול GKE.
  4. אם יש לכם מדיניות רשת של אשכול, אתם צריכים לאפשר יציאה אל 169.254.169.252/32 ביציאה 988. באשכולות שמופעל בהם GKE Dataplane V2, צריך לאפשר תעבורת נתונים יוצאת (egress) אל 169.254.169.254/32 ביציאה 80.

    kubectl describe networkpolicy NETWORK_POLICY_NAME
    

    מחליפים את NETWORK_POLICY_NAME בשם של מדיניות הרשת ב-GKE.

  5. אם ברשת ה-VPC שלכם מיושמים כללי יציאה שמוגדרים כברירת מחדל כ'דחייה' או מכשירי ניתוב בהתאמה אישית, צריך לוודא שתעבורת HTTPS יוצאת מותרת לשירות האסימונים הציבורי של Google (sts.googleapis.com ביציאה 443).

    שרת המטא-נתונים של GKE פועל במרחב השמות של רשת המארח, ונדרשת לו גישה ישירה לנקודות הקצה הציבוריות של Security Token Service כדי לבצע תיווך בהחלפת אסימונים מאוחדת. אם כללי חומת האש ליציאה חוסמים את הנתיב הזה, בקשות האסימון נכשלות עם שגיאות כמו 504 Gateway Timeout או context deadline exceeded.

אם בהגדרות שלכם יש קישור בין חשבונות שירות של Kubernetes לבין חשבונות שירות של IAM, צריך לוודא את הדברים הבאים:

  1. מוודאים שחשבון השירות של Kubernetes מתויג בצורה נכונה:

    kubectl describe serviceaccount \
        --namespace NAMESPACE KSA_NAME
    

    מחליפים את מה שכתוב בשדות הבאים:

    • NAMESPACE עם מרחב השמות של אשכול GKE.
    • KSA בשם של חשבון השירות שלכם ב-Kubernetes.

    הפלט הצפוי מכיל הערה שדומה לזו:

    iam.gke.io/gcp-service-account: GSA_NAME@PROJECT_ID.s3ns.iam.gserviceaccount.com
    
  2. בודקים שחשבון השירות ב-IAM מוגדר בצורה נכונה:

    gcloud iam service-accounts get-iam-policy \
        GSA_NAME@GSA_PROJECT.s3ns.iam.gserviceaccount.com
    

    הפלט הצפוי מכיל קישור שדומה לזה:

    - members:
      - serviceAccount:PROJECT_ID.s3ns.svc.id.goog[NAMESPACE/KSA_NAME]
      role: roles/iam.workloadIdentityUser
    

שגיאה: פורמט לא תקין של מספר חשבון

השגיאה הבאה מתרחשת כשמנסים לבצע פעולה שנדרשת בה כתובת האימייל בחשבון שירות ב-IAM, כמו יצירה ידנית של כתובת URL חתומה ב-Cloud Storage:

ERROR: Error: Invalid form of account ID test_account.s3ns.svc.id.goog. Should be [Gaia ID |Email |Unique ID |] of the account
# Multiple lines are omitted here
command terminated with exit code 137

השגיאה הזו מתרחשת כשמשתמשים בהערה כדי לקשר חשבונות שירות של Kubernetes לחשבונות שירות של IAM במקום להשתמש במזהה של חשבון משתמש ב-IAM כדי להגדיר איחוד שירותי אימות הזהות של עומסי עבודה ב-GKE.

כברירת מחדל, שרת המטא-נתונים של GKE מחזיר את הערך SERVICEACCOUNT_NAME.s3ns.svc.id.goog כמזהה של חשבון השירות עבור חשבונות שירות מקושרים. המזהה הזה לא משתמש בתחביר של מזהה חשבון משתמש ב-IAM.

כדי לפתור את השגיאה, מוסיפים את ההערה iam.gke.io/return-principal-id-as-email="true" ל-Kubernetes ServiceAccount של ה-Pod:

kubectl annotate serviceaccount KSA_NAME \
    --namespace=NAMESPACE \
    iam.gke.io/return-principal-id-as-email="true"

מחליפים את מה שכתוב בשדות הבאים:

  • KSA_NAME: השם של חשבון השירות ב-Kubernetes.
  • NAMESPACE: מרחב השמות של ServiceAccount.

הגישה לחשבון שירות ב-IAM נדחתה

יכול להיות שיהיו בעיות בגישה של פודים למשאב עם איחוד שירותי אימות הזהות של עומסי עבודה ב-GKE מיד אחרי הוספת קישורי תפקידים ב-IAM. סביר יותר שגישת משתמשים תיכשל בצינורות עיבוד נתונים לפריסה או בהגדרות דקלרטיביות של Cloud de Confiance , שבהן נוצרים יחד משאבים כמו מדיניות הרשאות IAM, קישורי תפקידים ו-Pods של Kubernetes. הודעת השגיאה הבאה מופיעה ביומני ה-Pod:

HTTP/403: generic::permission_denied: loading: GenerateAccessToken("SERVICE_ACCOUNT_NAME@PROJECT_ID.s3ns.iam.gserviceaccount.com", ""): googleapi: Error 403: Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist).

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

כדי לפתור את השגיאה הזו, אפשר להוסיף השהיה לפני שה-Pods מנסים לגשת למשאבי Cloud de Confiance אחרי שהם נוצרים.

בעיות בפענוח DNS

בקטע הזה מוסבר איך לזהות ולפתור שגיאות בחיבורים מ-Pods ל-APIs שנגרמות מבעיות בפענוח DNS. Cloud de Confiance אם השלבים שבקטע הזה לא פותרים את שגיאות החיבור, אפשר לעיין בקטע שגיאות של פסק זמן בהפעלה של Pod.

חלק Cloud de Confiance by S3NS מספריות הלקוח מוגדרות להתחבר לשרתי המטא-נתונים של GKE ו-Compute Engine על ידי פתרון שם ה-DNS metadata.google.internal. בספריות האלה, פתרון תקין של DNS בתוך האשכול הוא תלות קריטית כדי שעומסי העבודה יוכלו לבצע אימות לשירותיםCloud de Confiance .

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

לדוגמה, הודעת השגיאה הבאה עשויה להצביע על בעיה בפתרון DNS:

ComputeEngineCredentials cannot find the metadata server. This is likely because code is not running on Google Compute Engine

אם נתקלתם בבעיות בפענוח DNS של metadata.google.internal, אפשר להגדיר לספריות לקוח מסוימות של Cloud de Confiance by S3NS לדלג על פענוח DNS. לשם כך, צריך להגדיר את משתנה הסביבה GCE_METADATA_HOST ל-169.254.169.254:

apiVersion: v1
kind: Pod
metadata:
  name: example-pod
  namespace: default
spec:
  containers:
  - image: debian
    name: main
    command: ["sleep", "infinity"]
    env:
    - name: GCE_METADATA_HOST
      value: "169.254.169.254"

זו כתובת ה-IP שמוגדרת בהארדקוד, שבה שירות המטא-נתונים תמיד זמין בפלטפורמות מחשוב Cloud de Confiance .

יש תמיכה בספריות הבאות: Cloud de Confiance

  • Python
  • Java
  • Node.js
  • Golang

    כברירת מחדל, ספריית הלקוח של Go מתחברת באמצעות כתובת ה-IP.

שגיאות שקשורות לזמן קצוב לתפוגה בהפעלת ה-Pod

לשרת המטא-נתונים של GKE דרושות כמה שניות לפני שהוא יכול להתחיל לקבל בקשות ב-Pod חדש. ניסיונות אימות באמצעות איחוד זהויות של עומסי עבודה ל-GKE במהלך כמה השניות הראשונות של חיי ה-Pod עלולים להיכשל באפליקציות ובספריות לקוח שהוגדרו עם זמן קצוב לתפוגה קצר. Cloud de Confiance by S3NS

אם נתקלתם בשגיאות שקשורות לזמן קצוב לתפוגה, נסו את הפעולות הבאות:

  • מעדכנים את Cloud de Confiance ספריות הלקוח שבהן נעשה שימוש בעומסי העבודה.
  • משנים את קוד האפליקציה כך שימתין כמה שניות וינסה שוב.
  • פורסים initContainer שממתין עד ששרת המטא-נתונים של GKE מוכן לפני שמריצים את הקונטיינר הראשי של ה-Pod.

    לדוגמה, המניפסט הבא הוא של Pod עם initContainer:

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-with-initcontainer
    spec:
      serviceAccountName: KSA_NAME
      initContainers:
      - image:  gcr.io/google.com/cloudsdktool/cloud-sdk:alpine
        name: workload-identity-initcontainer
        command:
        - '/bin/bash'
        - '-c'
        - |
          curl -sS -H 'Metadata-Flavor: Google' 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token' --retry 30 --retry-connrefused --retry-max-time 60 --connect-timeout 3 --fail --retry-all-errors > /dev/null && exit 0 || echo 'Retry limit exceeded. Failed to wait for metadata server to be available. Check if the gke-metadata-server Pod in the kube-system namespace is healthy.' >&2; exit 1
      containers:
      - image: gcr.io/your-project/your-image
        name: your-main-application-container
    

איחוד זהויות של עומסי עבודה ל-GKE נכשל בגלל חוסר זמינות של מישור הבקרה

שרת המטא-נתונים לא יכול להחזיר את איחוד הזהויות של עומסי העבודה ל-GKE אם מישור הבקרה של האשכול לא זמין. קריאות לשרת המטא-נתונים מחזירות את קוד הסטטוס 500.

רשומה ביומן יכולה להיראות כך ב-Logs Explorer:

dial tcp 35.232.136.58:443: connect: connection refused

ההתנהגות הזו גורמת לכך שאי אפשר להשתמש באיחוד שירותי אימות הזהות של עומסי עבודה ב-GKE.

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

אימות באמצעות איחוד זהויות של עומסי עבודה ל-GKE נכשל באשכולות שמשתמשים ב-Cloud Service Mesh או ב-OSS Istio

יכול להיות שתראו שגיאות דומות לאלה כשמפעילים את האפליקציה באשכולות באמצעות Cloud Service Mesh או Istio sidecars ומנסים לתקשר עם נקודת קצה:

Connection refused (169.254.169.254:80)
Connection timeout

השגיאות האלה יכולות להתרחש כשהאפליקציה מנסה ליצור חיבור לרשת לפני שמאגר התגים istio-proxy מוכן. כברירת מחדל, Istio ו-Cloud Service Mesh מאפשרים לעומסי עבודה לשלוח בקשות ברגע שהם מתחילים לפעול, בלי קשר לשאלה אם עומס העבודה של ה-proxy של רשת השירות שמיירט ומפנה תנועה פועל. ב-Pods שמשתמשים באיחוד זהויות של עומסי עבודה ל-GKE, יכול להיות שהבקשות הראשוניות האלה שמתרחשות לפני שהפרוקסי מתחיל לפעול לא יגיעו לשרת המטא-נתונים של GKE. כתוצאה מכך, האימות לממשקי ה-API של Cloud de Confiance נכשל. אם לא תגדירו את האפליקציות כך שינסו לשלוח את הבקשות מחדש, יכול להיות שעומסי העבודה ייכשלו.

כדי לוודא שהבעיה הזו היא הגורם לשגיאות, צריך לעיין ביומנים ולבדוק אם מאגר התגים istio-proxy הופעל בהצלחה:

  1. נכנסים לדף Logs Explorer במסוף Cloud de Confiance .

    כניסה לדף Logs Explorer

  2. בחלונית השאילתה, מזינים את השאילתה הבאה:

    (resource.type="k8s_container"
    resource.labels.pod_name="POD_NAME"
    textPayload:"Envoy proxy is ready" OR textPayload:"ERROR_MESSAGE")
    OR
    (resource.type="k8s_pod"
    logName:"events"
    jsonPayload.involvedObject.name="POD_NAME")
    

    מחליפים את מה שכתוב בשדות הבאים:

    • POD_NAME: השם של ה-Pod עם עומס העבודה המושפע.
    • ERROR_MESSAGE: השגיאה שהאפליקציה קיבלה (connection timeout או connection refused).
  3. לוחצים על Run query.

  4. בודקים את הפלט כדי לראות מתי קונטיינר istio-proxy היה מוכן.

    בדוגמה הבאה, האפליקציה ניסתה לבצע קריאת gRPC. עם זאת, בגלל שמאגר התגים istio-proxy עדיין היה בתהליך אתחול, האפליקציה קיבלה שגיאה Connection refused. חותמת הזמן לצד ההודעה Envoy proxy is ready מציינת מתי מאגר istio-proxy היה מוכן לבקשות חיבור:

    2024-11-11T18:37:03Z started container istio-init
    2024-11-11T18:37:12Z started container gcs-fetch
    2024-11-11T18:37:42Z Initializing environment
    2024-11-11T18:37:55Z Started container istio-proxy
    2024-11-11T18:38:06Z StatusCode="Unavailable", Detail="Error starting gRPC call. HttpRequestException: Connection refused (169.254.169.254:80)
    2024-11-11T18:38:13Z Envoy proxy is ready
    

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

  • ‫Cloud Service Mesh ו-Istio בקוד פתוח:

    • כדי למנוע ממאגרי אפליקציות לשלוח בקשות עד שכוח העבודה של ה-proxy מוכן, מוסיפים את ההערה הבאה לשדה metadata.annotations במפרט ה-Pod:

      proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }'
      
    • כדי להגדיר את Istio או את Cloud Service Mesh כך שכתובת ה-IP של שרת המטא-נתונים של GKE לא תופנה מחדש, מוסיפים את ההערה הבאה לשדה metadata.annotations במפרט של ה-Pod:169.254.169.254/32

      traffic.sidecar.istio.io/excludeOutboundIPRanges: 169.254.169.254/32
      
  • ב-Istio בקוד פתוח בלבד, אפשר לצמצם את הבעיה הזו לכל ה-Pods באשכול על ידי הגדרת אחת מאפשרויות התצורה הגלובליות הבאות:

    • כדי לא לכלול את כתובת ה-IP של שרת המטא-נתונים של GKE בהפניה אוטומטית, צריך לעדכן את אפשרות ההגדרה הגלובלית global.proxy.excludeIPRanges כדי להוסיף את טווח כתובות ה-IP של 169.254.169.254/32.

    • כדי למנוע מאפליקציות לשלוח בקשות עד שהפרוקסי יתחיל לפעול, מוסיפים את אפשרות ההגדרה הגלובלית global.proxy.holdApplicationUntilProxyStarts עם ערך של true להגדרה של Istio. האפשרות הזו לא חלה על initContainers. אם יש לכם initContainers שצריך לאמת, עדיף להשתמש באפשרות global.proxy.excludeIPRanges.

gke-metadata-server לחצן Pod קורס

ה-Pod של DaemonSet במערכת gke-metadata-server מאפשר איחוד זהויות של עומסי עבודה ל-GKE בצמתים. ה-Pod משתמש במשאבי זיכרון באופן יחסי למספר חשבונות השירות של Kubernetes באשכול.

הבעיה הבאה מתרחשת כששימוש המשאבים של gke-metadata-server Pod חורג מהמגבלות שלו. ה-kubelet מוציא את ה-Pod עם שגיאה של חוסר זיכרון. הבעיה הזו יכולה להתרחש אם באשכול יש יותר מ-3,000 חשבונות שירות של Kubernetes.

כדי לזהות את הבעיה:

  1. כדי למצוא את ה-Pods שקורסים במרחב השמות kube-system:gke-metadata-server

    kubectl get pods -n=kube-system | grep CrashLoopBackOff
    

    הפלט אמור להיראות כך:

    NAMESPACE     NAME                        READY     STATUS             RESTARTS   AGE
    kube-system   gke-metadata-server-8sm2l   0/1       CrashLoopBackOff   194        16h
    kube-system   gke-metadata-server-hfs6l   0/1       CrashLoopBackOff   1369       111d
    kube-system   gke-metadata-server-hvtzn   0/1       CrashLoopBackOff   669        111d
    kube-system   gke-metadata-server-swhbb   0/1       CrashLoopBackOff   30         136m
    kube-system   gke-metadata-server-x4bl4   0/1       CrashLoopBackOff   7          15m
    
  2. כדי לוודא שהסיבה לקריסת ה-Pod היא הוצאה מזיכרון (OOM), צריך לתאר את ה-Pod שקרס:

    kubectl describe pod POD_NAME --namespace=kube-system | grep OOMKilled
    

    מחליפים את POD_NAME בשם של ה-Pod שרוצים לבדוק.

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

הפעלת איחוד זהויות של עומסי עבודה ל-GKE נכשלת עם הודעת השגיאה DeployPatch failed

‫GKE משתמש ב Cloud de Confianceסוכן השירות של Kubernetes Engine שמנוהל על ידי Google כדי להקל על איחוד זהויות של עומסי עבודה ב-GKE באשכולות שלכם. Cloud de Confiance מעניק לסוכן השירות הזה באופן אוטומטי את התפקיד 'סוכן השירות של Kubernetes Engine' (roles/container.serviceAgent) בפרויקט שלכם כשאתם מפעילים את Kubernetes Engine API.

אם מנסים להפעיל איחוד שירותי אימות הזהות של עומסי עבודה ב-GKE באשכולות בפרויקט שבו לסוכן השירות אין את התפקיד Kubernetes Engine Service Agent, הפעולה נכשלת ומוצגת הודעת שגיאה שדומה להודעה הבאה:

Error waiting for updating GKE cluster workload identity config: DeployPatch failed

כדי לפתור את הבעיה, נסו את הפתרונות הבאים:

  1. בודקים אם סוכן השירות קיים בפרויקט ומוגדר בצורה נכונה:

    gcloud projects get-iam-policy PROJECT_ID \
        --flatten=bindings \
        --filter=bindings.role=roles/container.serviceAgent \
        --format="value[delimiter='\\n'](bindings.members)"
    

    מחליפים את PROJECT_ID במזהה הפרויקט ב- Cloud de Confiance.

    אם סוכן השירות מוגדר בצורה נכונה, הפלט מציג את הזהות המלאה של סוכן השירות:

    serviceAccount:service-PROJECT_NUMBER@container-engine-robot.s3ns-system.iam.gserviceaccount.com
    

    אם סוכן השירות לא מופיע בפלט, צריך להעניק לו את התפקיד Kubernetes Engine Service Agent. כדי להעניק את התפקיד הזה, צריך לבצע את השלבים הבאים.

  2. מציאת מספר הפרויקט ב- Cloud de Confiance :

    gcloud projects describe PROJECT_ID \
        --format="value(projectNumber)"
    

    הפלט אמור להיראות כך:

    123456789012
    
  3. נותנים לסוכן השירות את התפקיד:

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member=serviceAccount:service-PROJECT_NUMBER@container-engine-robot.s3ns-system.iam.gserviceaccount.com \
        --role=roles/container.serviceAgent \
        --condition=None
    

    מחליפים את PROJECT_NUMBER במספר הפרויקט ב- Cloud de Confiance.

  4. מנסים שוב להפעיל את איחוד הזהויות של עומסי עבודה ל-GKE.

המאמרים הבאים