אימות באמצעות זהות של סוכן ב-GKE

סוכני Google Kubernetes Engine ‏ (GKE) שיש להם זהות סוכן יכולים להשתמש בה כדי לבצע אימות לממשקי Google Cloud API ולכלים ולשירותים חיצוניים. הסוכנים יכולים להשתמש בזהות שלהם או לפעול בשם משתמשי הקצה. במאמר הזה נסביר למפתחי אפליקציות של סוכנים איך להגדיר את האפליקציות שלהם כדי לבצע אימות למשאבים שונים. כדאי שתקראו את המאמר בנושא בקשת זהות של סוכן עבור סוכן GKE.

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

מגבלות

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

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

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

  • מפעילים את ממשק ה-API של Google Kubernetes Engine.
  • הפעלת Google Kubernetes Engine API
  • כדי להשתמש ב-CLI של Google Cloud למשימה הזו, צריך להתקין ואז לאתחל את ה-CLI של gcloud. אם התקנתם בעבר את ה-CLI של gcloud, מריצים את הפקודה gcloud components update כדי לקבל את הגרסה העדכנית. יכול להיות שגרסאות קודמות של ה-CLI של gcloud לא יתמכו בהרצת הפקודות שמופיעות במסמך הזה.

התפקידים הנדרשים

כדי לקבל את ההרשאות שנדרשות להגדרת סוכנים שנפרסו באשכולות GKE, צריך לבקש מהאדמין להקצות לכם את תפקיד ה-IAM‏ Kubernetes Engine Developer (roles/container.developer) בפרויקט. כדי לקרוא הסבר על מתן תפקידים, ראו איך מנהלים את הגישה ברמת הפרויקט, התיקייה והארגון.

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

אימות ל- Google Cloud APIs

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

שימוש בספריות לקוח ב-Cloud

אם אתם משתמשים בגרסה של ספריות הלקוח ב-Cloud שכוללת את גרסה 2.61.0 ואילך של ספריית google-auth, אז Application Default Credentials ‏ (ADC) מקבל באופן אוטומטי אסימון גישה לזהות של הסוכן. לא צריך לבצע שינויים נוספים בקוד. אם מפעילים הזרקת אישורים ל-Pods על ידי הגדרת ההערה iam.gke.io/inject-podcertificates: "true", אסימון הגישה משויך לאישור X.509 כברירת מחדל, אלא אם משביתים את האסימונים המשויכים.

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

  • מפעילים את הזרקת האישורים ב-Pod ומגדירים את משתנה הסביבה GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN ל-false.
  • אל תפעילו הוספה של אישורים ב-Pod.

שימוש בקריאות ישירות לנקודות קצה של Google Cloud API

אם אתם לא משתמשים בספריות הלקוח ב-Cloud כדי ליצור אינטראקציה עם שירות, אתם יכולים להשתמש בזהות של הסוכן כדי לבצע אימות ל-API Google Cloud באופן הבא:

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

    • טוקני גישה מאוגדים: משתמשים בספריית Python‏ google-auth, שמגלה את אישור X.509 של ה-Pod ומקבלת אוטומטית טוקני גישה מאוגדים כברירת מחדל. בשפות תכנות אחרות, משתמשים בטוקנים לא קשורים.

    • Unbound access tokens: אם ל-Pod אין את חבילת האישורים של זהות הסוכן, צריך להשתמש בספריית האימות של Google בשפת התכנות שלכם. ספריית האימות מקבלת באופן אוטומטי טוקני גישה לא קשורים ומרעננת טוקנים שתוקפם עומד לפוג. באפליקציות Python ב-Pods שיש להן את חבילת פרטי הכניסה, מגדירים את משתנה הסביבה GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN במפרט ה-Pod לערך false, כמו בדוגמה הבאה:

      # Multiple lines are omitted here.
      spec:
        containers:
        - name: example-agent
          image: example-image
          env:
          - name: GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN
            value: "false"
      # Multiple lines are omitted here.
      

      משתנה הסביבה הזה מונע מהספרייה לקבל אסימוני גישה ואסימוני זהות שקשורים ל-Bound.

  2. עבור טוקני גישה קשורים, שלח את הבקשה לנקודת הקצה של mTLS ב-API וכלול את שרשרת האישורים של זהות הסוכן X.509 בתעבורת ה-HTTP. אם אתם משתמשים בספריית האימות של Google עבור Python, הספרייה מטפלת בשבילכם בהגדרת התעבורה ב-HTTP.

בדוגמה הבאה אפשר לראות איך משתמשים בספריית האימות של Google ל-Python כדי לקבל אסימון גישה מאוגד ולשלוח בקשה לנקודת הקצה של Cloud Storage mTLS:

import google.auth
from google.auth.transport.requests import AuthorizedSession


def call_storage_api_mtls(bucket_name: str) -> None:
    # Discover the Pod's X.509 certificate chain by using the auth library
    credentials, project = google.auth.default(
        scopes=["https://www.googleapis.com/auth/cloud-platform"]
    )
    # Configure the mTLS session by using the Pod's certificate chain
    session = AuthorizedSession(credentials)
    session.configure_mtls_channel()
    # Call the Google Cloud mTLS endpoint
    mtls_url = f"https://storage.mtls.googleapis.com/storage/v1/b/{bucket_name}/o"
    response = session.get(mtls_url)
    response.raise_for_status()
    print(response.json())

אימות מול כלים ושירותים חיצוניים

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

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

אימות לסוכנים אחרים

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

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

קבלת טוקן של מזהה

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

  • טוקנים של מזהה מאוגדים: משתמשים באנוטציה ה-iam.gke.io/inject-podcertificates: "true" כדי להפעיל הוספה של אישור ל-Pod. ספריית האימות של Python שולחת באופן אוטומטי בקשה לטוקן של מזהה שקשור לאישור משרת המטא-נתונים של GKE. משתמשים בטוקנים של מזהה מאוגדים כשמבצעים אימות בין סוכנים שפועלים ב- Google Cloud באמצעות mTLS.
  • טוקנים של מזהה לא קשורים:

    • מפעילים את הזרקת האישורים ל-Pod ומבצעים אחת מהפעולות הבאות:
      • בקוד האפליקציה, בפונקציה id_token.fetch_id_token, מגדירים את הארגומנט bind_id_token לערך False. הארגומנט הזה גורם לספריית האימות לבקש טוקנים של מזהה לא מאוגד. בקשות לטוקן גישה לא מושפעות.
      • במפרט ה-Pod, מגדירים את משתנה הסביבה GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN לערך false. משתנה הסביבה הזה מונע מהספרייה לבקש טוקני גישה וטוקנים של מזהה שקשורים ל-bound.
    • לא מפעילים הוספת אישורים ל-Pod. ספריית האימות מקבלת טוקן של מזהה לא מאוגד, כי אין חבילת אישורים ב-Pod.

    משתמשים בטוקנים של מזהה לא קשורים כשמבצעים אימות ל-API Google Cloud , לשירותים חיצוניים או לסוכנים אחרים באמצעות חיבור שאינו mTLS.

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

  • שליחת בקשה לטוקן של מזהה מאוגד:

    import google.auth.transport.requests
    from google.oauth2 import id_token
    
    # Application Default Credentials automatically requests a certificate-bound
    # ID token.
    def get_bound_id_token(target_audience: str) -> str:
        auth_req = google.auth.transport.requests.Request()
        return id_token.fetch_id_token(auth_req, audience=target_audience)
    

    אסימון הזהות המקשר כולל את טביעת האצבע לאישור SHA-256 של רשת אישורי ה-X.509 של ה-Pod בפרמטר cnf.x5t#S256.

  • שליחת בקשה לאסימון מזהה לא מאוגד:

    import google.auth.transport.requests
    from google.oauth2 import id_token
    
    def get_unbound_id_token(target_audience: str) -> str:
        auth_req = google.auth.transport.requests.Request()
        return id_token.fetch_id_token(
            auth_req,
            audience=target_audience,
            # Always get an unbound ID token, even if the Pod has a credential
            # bundle.
            bind_id_token=False,
        )
    

שימוש בטוקן של מזהה בבקשה לסוכן אחר

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

  • כדי להשתמש בטוקנים של מזהה קשורים, צריך ליצור חיבור mTLS עם הסוכן המקבל ולאמת את החיבור באמצעות שני סוגי האישורים הבאים מתיקיית /var/run/secrets/workload-spiffe-credentials/ ב-Pod:
    • חבילת פרטי הכניסה של זהות הסוכן שנמצאת בקובץ x509.credential-bundle.private-key.pem, שמכילה את שרשרת האישורים של עלה העץ עבור ה-Pod.
    • חבילת האישורים של האשכול שנמצאת בקובץ TRUST_DOMAIN.spiffe-trust-bundle.pem. הקובץ הזה מכיל את אישור ה-CA הבסיסי של הסוכן המקבל, והוא משמש לאימות שרשרת האישורים של הסוכן המקבל במהלך לחיצת היד של mTLS. הנציגים שמבצעים את השיחה ומקבלים אותה צריכים להיות באותו מאגר זהויות של נציגים.
  • במקרה של טוקנים של מזהה לא קשורים, צריך ליצור חיבור לא mTLS עם הסוכן המקבל.

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

  • טוקן של מזהה מאוגד: כוללים את טוקן הזהות המאוגד בכותרת Authorization: Bearer של הבקשה ששולחים לנקודת הקצה של mTLS של הסוכן המקבל. מאמתים את חיבור ה-TLS באמצעות אישור X.509 והמפתח הפרטי של ה-Pod:

    import ssl
    import urllib3
    
    BUNDLE_PATH = "/var/run/secrets/workload-spiffe-credentials/x509.credential-bundle.private-key.pem"
    TRUST_BUNDLE_PATH = "/var/run/secrets/workload-spiffe-credentials/TRUST_DOMAIN.spiffe-trust-bundle.pem"
    
    def call_peer_agent_bound_mtls(target_mtls_url: str, target_audience: str) -> None:
    
        # Configure the mTLS context by using the certificate chain and trust
        # bundle from the Pod.
        ctx = ssl.create_default_context(cafile=TRUST_BUNDLE_PATH)
        ctx.load_cert_chain(BUNDLE_PATH)
        http = urllib3.PoolManager(ssl_context=ctx, assert_hostname=False)
    
        # Call a peer agent's mTLS endpoint by using the bound ID token and Pod
        # certificate chain.
        bound_id_token = get_bound_id_token(target_audience)
        response = http.request(
            "POST",
            target_mtls_url,
            headers={"Authorization": f"Bearer {bound_id_token}"},
            json={"task": "analyze_data"},
            timeout=10,
        )
        print(response.json())
    

    מחליפים את TRUST_DOMAIN במתחם האמון (trust domain) של מאגר הזהויות של הסוכן.

  • טוקן של מזהה לא קשור: כוללים את הטוקן בכותרת Authorization: Bearer של הבקשה לסוכן העמית:

    import requests
    
    def call_peer_agent_unbound(target_url: str, target_audience: str) -> None:
        unbound_id_token = get_unbound_id_token(target_audience)
        # Send the request by using a standard TLS connection or plain HTTP.
        response = requests.post(
            target_url,
            headers={"Authorization": f"Bearer {unbound_id_token}"},
            json={"task": "analyze_data"},
            timeout=10,
        )
        response.raise_for_status()
        print(response.json())
    

אימות הבקשה בסוכן המקבל

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

  1. מחולצים את טוקן הזהות מהכותרת Authorization: Bearer של הבקשה.
  2. מוודאים שההצהרה iss (מנפיק) בטוקן של מזהה היא מאגר הזהויות של הסוכן עבור הסוכן שקורא ל-API. המנפיק הוא אחד מהבאים, בהתאם לשאלה אם הסוכן שמבצע את הקריאה נמצא בפרויקט ששייך לארגון:
    • פרויקט שנמצא בארגון: ‫https://sts.googleapis.com/v1/organizations/ORGANIZATION_ID/locations/global/workloadIdentityPools/agents.global.org-ORGANIZATION_ID.system.id.goog, כאשר ORGANIZATION_ID הוא מזהה הארגון שמכיל את הפרויקט של הסוכן שקורא ל-API.
    • ‫Project that isn't in an organization:‏ https://sts.googleapis.com/v1/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/agents.global.proj-PROJECT_NUMBER.system.id.goog, כאשר PROJECT_NUMBER הוא מספר הפרויקט של אשכול GKE של הסוכן המתקשר.
  3. מאתרים את ה-URI של JSON Web Key Set‏ (JWKS) עבור המנפיק ומאחסנים במטמון את מפתחות ה-JSON Web Keys‏ (JWKs) הציבוריים. נקודת הקצה של JWKS היא בפורמט ISSUER_URL/openid/jwks, כאשר ISSUER_URL היא כתובת ה-URL של המנפיק.
  4. כדי לאמת את חתימת הטוקן של מזהה, משתמשים במידע הבא מכותרת ה-JOSE של הטוקן של מזהה:
    • מפתח ה-JWK הציבורי שתואם לפרמטר הכותרת kid.
    • האלגוריתם הקריפטוגרפי שתואם לפרמטר הכותרת alg, כמו RS256.
  5. מוודאים שטביעת האצבע לאישור SHA-256 שנמצאת בפרמטר cnf.x5t#S256 תואמת לטביעת האצבע לאישור X.509 שסוכן הקריאה השתמש בו כדי לאמת את חיבור ה-mTLS.
  6. בודקים את הטענות הבאות בטוקן ה-ID:
    • זמן התפוגה בהצהרת exp הוא עתידי.
    • קהל היעד בטענה aud הוא הסוכן המקבל.
  7. מאשרים את הבקשה על סמך מזהה SPIFFE שנמצא בהצהרה sub (subject) של האסימון.

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