הגדרת אימות משתמשים ב-Cloud Service Mesh

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

אימות משתמשים ב-Cloud Service Mesh הוא פתרון משולב לאימות משתמשי קצה מבוסס-דפדפן ולבקרת גישה לעומסי העבודה שפרסתם. הוא מאפשר לכם לבצע אינטגרציה עם ספקי זהויות (IDP) קיימים לצורך אימות משתמשים, ומשתמש בממשקי API ובמדיניות הרשאות של Istio לניהול גישה. זוהי חלופה ידידותית למשתמש לאימות של JSON Web Token ‏ (JWT) ב-Istio.

תרחיש שימוש אופייני הוא כשארגון משתמש ב-Cloud Service Mesh כדי לארח אפליקציית אינטרנט שהעובדים יכולים לגשת אליה דרך דפדפן אינטרנט. בנוסף, הארגון צריך להשתמש בספק הזהויות הקיים שלו כדי לנהל את זהויות המשתמשים. אימות משתמשים ב-Cloud Service Mesh מאפשר למשתמשים לבצע אימות בקלות באמצעות תהליך כניסה והסכמה מבוסס-אינטרנט של OpenID Connect ‏ (OIDC). כשהמשתמש מבצע אימות, Cloud Service Mesh אוכף את מדיניות ההרשאות של Istio, ואם ההרשאה מצליחה, הוא מעביר את הזהות לעומסי העבודה בפורמט מאובטח של פרטי כניסה.

איך זה עובד

אימות משתמשים ב-Cloud Service Mesh כולל רכיב חדש, authservice. הרכיב הזה משולב עם שער הכניסה מבוסס Envoy כשירות הרשאה חיצוני שמיירט את כל הבקשות הנכנסות לאימות. ‫authservice מטמיע את צד הלקוח של פרוטוקול OIDC ומאפשר למשתמשים לגשת לאפליקציות דרך דפדפן, שבו המשתמשים משלימים תהליך אימות והסכמה אינטראקטיבי כדי ליצור סשן קצר. ‫authservice מטמיע פרוטוקולים סטנדרטיים בתעשייה כדי להשתלב עם כל ספק זהויות שיכול לשמש כשרת הרשאות OIDC. אחרי שהמשתמש עובר אימות, פרטי העיקרון מוצפנים ב-RCToken בפורמט JWT, נחתמים על ידי authservice ומועברים לשכבת ההרשאה של Istio ב-ingress. המודל הזה מספק בקרת גישה היקפית לתנועה ברשת. אם המשתמש מורשה לגשת למשאב, ה-RCToken מועבר גם למיקרו-שירותים כדי לקבל מידע על הגורם המורשה ולאכוף בקרת גישה מדויקת.

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

אימות משתמשי קצה

אדמינים יכולים להתקין את authservice כתוסף על התקנה של Cloud Service Mesh. אחרי ההתקנה, authservice קורא את ההגדרות של נקודת הקצה של OIDC ואת ההגדרות המשויכות האחרות שמוגדרות במשאב המותאם אישית UserAuth. האדמין יכול להשתמש בממשקי API של Cloud Service Mesh‏ ExternalAuthorization כדי להגדיר את auth_server כמסנן בתעבורת הכניסה.

התקנה של שירות אימות משתמשים

בשלבים הבאים מוסבר איך להגדיר את authservice.

דרישות מוקדמות

פועלים לפי השלבים במאמר התקנת כלים תלויי-הקשר ואימות האשכול כדי:
  • אם אתם משתמשים ב-Cloud Service Mesh מנוהל באשכול פרטי, אתם צריכים לוודא שהאשכול יכול לשלוח תנועת יציאה לספק הזהויות.

בנוסף, צריך לוודא שאתם עומדים בדרישות המוקדמות באמצעות השלבים הבאים.

התאמה אישית של שכבת-העל לאימות משתמשים בהתקנה

כדי להתקין את שירות אימות המשתמשים, צריך להתאים אישית את ההתקנה של Cloud Service Mesh כדי להוסיף ספק הרשאות חיצוני ברמת הרשת. השלבים הנדרשים תלויים בשאלה אם אתם משתמשים ב-Cloud Service Mesh מנוהל או ב-Cloud Service Mesh בתוך האשכול.

מנוהל

  1. מעדכנים את ConfigMap כך שיכלול את MeshConfig של אימות המשתמש. בפקודה הבאה, צריך להשתמש באותו REVISION_LABEL שבו השתמשתם כשהקציתם משאבים ל-Cloud Service Mesh מנוהל (למשל, asm-managed, asm-managed-rapid או asm-managed-stable):

    kubectl edit configmap istio-REVISION_LABEL -n istio-system
    
  2. מוסיפים את הטקסט הבא בשדה mesh ב-MeshConfig:

    mesh: |-
    ...
      extensionProviders:
      - name: "asm-userauth-grpc"
        envoyExtAuthzGrpc:
          service: "authservice.asm-user-auth.svc.cluster.local"
          port: "10003"
    
  3. יוצרים מרחב שמות asm-user-auth.

    kubectl create namespace asm-user-auth
    
  4. מפעילים את מרחב השמות להחדרה. השלבים תלויים בהטמעה של מישור הבקרה.

    מנוהל (TD)

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

    kubectl label namespace asm-user-auth \
        istio.io/rev- istio-injection=enabled --overwrite
    

    מנוהל (Istiod)

    מומלץ: מריצים את הפקודה הבאה כדי להחיל את תווית ברירת המחדל של הזרקה על מרחב השמות:

    ```sh
    kubectl label namespace asm-user-auth \
        istio.io/rev- istio-injection=enabled --overwrite
    ```
    

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

    1. מריצים את הפקודה הבאה כדי לאתר את ערוצי ההפצה הזמינים:
    kubectl -n istio-system get controlplanerevision
    

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

    NAME                AGE
    asm-managed-rapid   6d7h
    

    בפלט, הערך בעמודה NAME הוא תווית הגרסה שתואמת לערוץ ההפצה שזמין לגרסה של Cloud Service Mesh.

    1. החלת תווית הגרסה על מרחב השמות:

      kubectl label namespace asm-user-auth \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      
  5. מתקינים את שער Istio במרחב השמות asm-user-auth.

    kubectl apply -n asm-user-auth -f DIR_PATH/samples/gateways/istio-ingressgateway
    

באשכול

  1. אפשר לקבל את הדוגמה של שכבת-על לאימות משתמשים ולעדכן אותה אם יש התאמות אישיות ברשת שלכם. מומלץ לשמור את קובץ השכבות האלה בבקרה של המקור.

    curl https://raw.githubusercontent.com/GoogleCloudPlatform/asm-user-auth/v1.2.5/overlay/user-auth-overlay.yaml > user-auth-overlay.yaml
    
  2. כדי להתקין את Cloud Service Mesh עם שכבת-על של אימות משתמשים, צריך לפעול לפי ההוראות במאמר התקנת Cloud Service Mesh עם שכבת-על ולהשתמש בסקריפט שסופק על ידי Google. לדוגמה:

    ./asmcli install \
      --project_id PROJECT_ID \
      --cluster_name CLUSTER_NAME \
      --cluster_location CLUSTER_LOCATION \
      --fleet_id FLEET_PROJECT_ID \
      --output_dir DIR_PATH \
      --enable_all \
      --custom_overlay user-auth-overlay.yaml
    

    חבילות האימות של המשתמש יוצרות kpt כדי להפנות לספק ההרשאות החיצוני שצוין על ידי AuthorizationPolicy.pkg/ext-authz.yaml

  3. יוצרים מרחב שמות asm-user-auth.

    kubectl create namespace asm-user-auth
    
  4. מפעילים את מרחב השמות להחדרה.

    מומלץ: מריצים את הפקודה הבאה כדי להחיל את תווית ברירת המחדל של הזרקה על מרחב השמות:

      kubectl label namespace asm-user-auth \
          istio.io/rev- istio-injection=enabled --overwrite
    

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

    1. משתמשים בפקודה הבאה כדי לאתר את תווית הגרסה ב-istiod:

      kubectl get deploy -n istio-system -l app=istiod -o \
        jsonpath={.items[*].metadata.labels.'istio\.io\/rev'}'{"\n"}'
      
    2. מחילים את תווית הגרסה על מרחב השמות. בפקודה הבאה, REVISION_LABEL הוא הערך של התווית istiod revision שרשמתם בשלב הקודם.

      kubectl label namespace asm-user-auth \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      
  5. מתקינים את שער Istio במרחב השמות asm-user-auth.

    kubectl apply -n asm-user-auth -f DIR_PATH/samples/gateways/istio-ingressgateway
    

הכנת הגדרות לקוח OIDC

כדי להגדיר את לקוח ה-OIDC: במדריך הזה נעשה שימוש ב-Google כספק זהויות, אבל אפשר להשתמש בכל ספק זהויות שתומך באימות OIDC.

  1. במסוף Google Cloud , עוברים אל API & Services > Credentials.

    כניסה לדף Credentials

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

    • מגדירים את Application type (סוג האפליקציה) ל-Web application (אפליקציית אינטרנט).
    • מגדירים את Authorized redirect URI (כתובת ה-URI המורשית להפניה אוטומטית) לערך https://REDIRECT_HOST/REDIRECT_PATH. לדוגמה, בשביל localhost אפשר להגדיר את הערך https://localhost:8443/_gcp_asm_authenticate.

    לאחר מכן, לוחצים על שמירה.

  3. בנוסף, שומרים את הגדרות הלקוח של OIDC לשימוש מאוחר יותר.

    export OIDC_CLIENT_ID=CLIENT_ID
    export OIDC_CLIENT_SECRET=CLIENT_SECRET
    export OIDC_ISSUER_URI=ISSUER_URI
    export OIDC_REDIRECT_HOST=REDIRECT_HOST
    export OIDC_REDIRECT_PATH=REDIRECT_PATH
    

קבלת חבילות kpt

כדי להתקין את ההגדרה המומלצת authservice ממאגר המידע הציבורי: הפקודות האלה מאחזרות את מאגר authservice האחרון ומתחילות אותו כ-Pod במרחב השמות asm-user-auth. היא גם מגדירה את ה-ingress ליירוט של כל הבקשות.

מורידים את חבילת kpt:

kpt pkg get https://github.com/GoogleCloudPlatform/asm-user-auth.git/@v1.2.5 .
cd asm-user-auth/

הגדרת כתובת ה-URL להפניה אוטומטית והסוד לשער הכניסה

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

  1. יצירת אישור בחתימה עצמית:

    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
     -days 365 -nodes -subj '/CN=localhost'
    
  2. יוצרים סוד בשביל שער הכניסה (ingress) כדי לארח תנועת נתונים מסוג HTTPS:

    kubectl create -n asm-user-auth secret tls userauth-tls-cert --key=key.pem \
    --cert=cert.pem
    

החלת מפתחות ההצפנה והחתימה

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

קבוצת המפתחות השנייה היא זוג מפתחות ציבוריים/פרטיים. המפתח הזה משמש לחתימה על פרטי המשתמש המאומת בפורמט JWT בתור RCToken. המפתח הציבורי מהזוג הזה מתפרסם בנקודת קצה מוגדרת מראש, שקובצי ה-sidecar יכולים להשתמש בה כדי לאמת את ה-JWT.

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

  1. מכינים את מפתח ההצפנה של הסשן בפורמט הבא או משתמשים בדוגמה מ-pkg, שאפשר לראות אותה באמצעות cat ./samples/cookie_encryption_key.json.

    {
      "keys":[
         {
            "kty":"oct",
            "kid":"key-0",
            "K":"YOUR_KEY",
            "useAfter": 1612813735
         }
      ]
    }
    

    אפשר ליצור מפתח AES לבדיקה באמצעות הפקודה הבאה:

    openssl enc -aes-256-cbc -k mycustomkey -P -md sha1 | grep key
    
  2. מכינים את מפתח החתימה של RCToken בפורמט הבא או משתמשים בדוגמה מתוך חבילת ה-pkg, שאפשר לראות אותה באמצעות cat ./samples/rctoken_signing_key.json.

    {
      "keys":[
         {
            "kty":"RSA",
            "kid":"rsa-signing-key",
            "K":"YOUR_KEY",  # k contains a Base64 encoded PEM format RSA signing key.
            "useAfter": 1612813735  # unix timestamp
         }
      ]
    }
    

    אפשר ליצור מפתח פרטי של RSA ל-512 ביט לבדיקה באמצעות הפקודה הבאה:

    openssl genpkey -algorithm RSA -out rsa_private.pem -pkeyopt rsa_keygen_bits:512
    
  3. יוצרים את הסוד של Kubernetes, שauthservice יותקן במערכת קבצים משלו.

    kubectl create secret generic secret-key  \
        --from-file="session_cookie.key"="./samples/cookie_encryption_key.json" \
        --from-file="rctoken.key"="./samples/rctoken_signing_key.json"  \
        --namespace=asm-user-auth
    

פריסת שירות אימות המשתמשים

הפקודות הבאות יוצרות את שירות אימות המשתמש ואת הפריסה במרחב השמות asm-user-auth.

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

kpt fn eval pkg --image gcr.io/kpt-fn/apply-setters:v0.2 --truncate-output=false -- \
  client-id="$(echo -n ${OIDC_CLIENT_ID} | base64 -w0)" \
  client-secret="$(echo -n ${OIDC_CLIENT_SECRET} | base64 -w0)" \
  issuer-uri="${OIDC_ISSUER_URI}" \
  redirect-host="${OIDC_REDIRECT_HOST}" \
  redirect-path="${OIDC_REDIRECT_PATH}"

החלת החבילה kpt:

# Remove the potential alpha version CRD if exists.
kubectl delete crd userauthconfigs.security.anthos.io
kubectl apply -f ./pkg/asm_user_auth_config_v1beta1.yaml
kubectl apply -f ./pkg

ה-authservice צורך את ה-CRD UserAuthConfig כדי לספק אימות למשתמשי קצה. אפשר להגדיר את UserAuthConfig בזמן הריצה, ולעדכן אותו כדי לשנות את ההתנהגות של authservice ולהגדיר אותו עם נקודות קצה לכל שרת הרשאות OIDC.

אפשר לראות את הקובץ באמצעות cat pkg/user_auth_config.yaml, והוא מכיל את השדות הבאים:

apiVersion: security.anthos.io/v1beta1
kind: UserAuthConfig
metadata:
  name: user-auth-config
  namespace: asm-user-auth
spec:
  authentication:
    oidc:
      certificateAuthorityData: ""  # kpt-set: ${ca-cert}
      issuerURI: "<your issuer uri>"  # kpt-set: ${issuer-uri}
      proxy: ""  # kpt-set: ${proxy}
      oauthCredentialsSecret:
        name: "oauth-secret"  # kpt-set: ${secret-name}
        namespace: "asm-user-auth"  # kpt-set: ${secret-namespace}
      redirectURIHost: ""  # kpt-set: ${redirect-host}
      redirectURIPath: "/_gcp_asm_authenticate"  # kpt-set: ${redirect-path}
      scopes: ""  # kpt-set: ${scopes}
      groupsClaim: ""  # kpt-set: ${groups}
  outputJWTAudience: "test_audience"  # kpt-set: ${jwt-audience}

במאמר פרטים על הגדרת אימות משתמשים מופיעים תיאורים מפורטים של השדות user_auth_config.yaml.

הגדרת מזהי בקשות API

מזהי בקשות API הם קבוצה של ביטויי Common Expression Language (CEL) שמשמשים לאימות משתמשים ב-Cloud Service Mesh כדי לזהות בקשות API. הגדרת המזהים האלה מאפשרת לאימות המשתמשים ב-Cloud Service Mesh לטפל באופן סמנטי בכשלים באימות בקשות API.

באופן ספציפי, כשמספקים ביטויי CEL שמזהים את בקשות ה-API, מאפשרים לאימות המשתמשים ב-Cloud Service Mesh לדחות בקשות API לא מאומתות עם קוד סטטוס 401 (לא מורשה). זה חשוב במיוחד ללקוחות כמו ממשקי שורת פקודה (CLI) וחשבונות שירות, שמצפים לקוד הסטטוס הזה עבור בקשות API לא מאומתות.

בלי מזהים של בקשות API, אימות המשתמשים ב-Cloud Service Mesh יפנה מחדש את כל הבקשות שלא אומתו לספק OpenID Connect ‏ (OIDC) שהוגדר, ויחזיר קוד סטטוס 302-Found. בדרך כלל, התנהגות ההפניה האוטומטית הזו לא רצויה לבקשות API, שצפוי שיוחזר להן קוד שגיאה 401 (לא מורשה) אם הן נכשלות.

כדי להגדיר את המזהים של בקשת ה-API, מוסיפים קטע apiRequestIdentifier אל UserAuthConfig CR. כך אפשר להוסיף כמה ביטויי CEL. אם מספקים כמה ביטויי CEL, בקשה נחשבת לבקשת API אם היא תואמת לפחות לאחד מהם.

בדוגמה הבאה מוצגות דרכים להגדיר בקשה כבקשת API: כל בקשה שמכילה את הכותרת example-api-header, כל בקשה שמופנית למארח api.example.com או כל בקשה שמטרגטת נתיב מתחת ל-/v1/api/.

apiVersion: security.anthos.io/v1beta1
kind: UserAuthConfig
metadata:
  name: user-auth-config
  namespace: asm-user-auth
spec:
  authentication:
    oidc:
      certificateAuthorityData: ""  # kpt-set: ${ca-cert}
      issuerURI: "<your issuer uri>"  # kpt-set: ${issuer-uri}
      proxy: ""  # kpt-set: ${proxy}
      oauthCredentialsSecret:
        name: "oauth-secret"  # kpt-set: ${secret-name}
        namespace: "asm-user-auth"  # kpt-set: ${secret-namespace}
      redirectURIHost: ""  # kpt-set: ${redirect-host}
      redirectURIPath: "/_gcp_asm_authenticate"  # kpt-set: ${redirect-path}
      scopes: ""  # kpt-set: ${scopes}
      groupsClaim: ""  # kpt-set: ${groups}
  outputJWTAudience: "test_audience"  # kpt-set: ${jwt-audience}
  apiRequestIdentifier: 
    name: "api-request-identifiers"
    expressions: 
    - "'example-api-header' in http_request.headers"
    - "http_request.host == 'api.example.com'"
    - "matches(http_request.path, '/v1/api/(.*)')"
    # other expressions go here

אפשר ליצור ביטויי CEL לאימות משתמשים ב-Cloud Service Mesh מהשדות הבאים:

  1. http_request.headers: מפה של כותרות בקשות ה-HTTP, שבה המפתחות הם מחרוזות באותיות קטנות והערכים הם וקטורים של מחרוזות.

    משתמשים בשדה הזה בביטוי CEL כדי להגדיר בקשות עם כותרות ספציפיות כבקשות API.

  2. http_request.host: המארח של כתובת ה-URL של ה-HTTP המבוקשת (למשל, api.example.com).

    משתמשים בשדה הזה בביטוי CEL כדי להגדיר בקשות למארחים ספציפיים כבקשות API.

  3. http_request.path: נתיב כתובת ה-URL מסוג HTTP שהתבקשה (למשל /example-api-path). הנתיב תמיד מתחיל ב-'/' ולא מכיל את רכיב השאילתה של כתובת ה-URL של הבקשה.

    משתמשים בשדה הזה בביטוי CEL כדי להגדיר בקשות עם נתיבים ספציפיים כבקשות API.

בקטע הבא מוצגות דוגמאות לביטויי CEL שבודקים חלקים מהשדות האלה ומתאימים אותם כדי להודיע לאימות המשתמשים ב-Cloud Service Mesh איך לקבוע אילו בקשות הן בקשות API.

ביטויים לדוגמה

התאמה לכל הבקשות שמכילות את הכותרת X-API-Key:

"'x-api-key' in http_request.headers"

התאמה של כל הבקשות עם הכותרת X-API-Key שהערך שלה מוגדר לערך מסוים:

"'x-api-key' in http_request.headers && http_request.headers['x-api-key'].values == ['example-value ']"

התאמת כל הבקשות למארח api.example.com:

"http_request.host == api.example.com"

התאמה של כל הבקשות לנתיב /api/query:

"http_request.path == /api/query"

התאמה של כל הבקשות לכל נתיב בקטע /api/:

"matches(http_request.path, '/api/(.*)')"

ביצוע משימות אחרי ההתקנה

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

הפעלת אימות משתמשים באפליקציות

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

אימות משתמשים ב-Cloud Service Mesh מתבצע באמצעות מדיניות הרשאות מוקלדת CUSTOM כדי להפעיל את תהליך OIDC.

אחרי ההתקנה של שער Istio, צריך להגדיר אותו כך שישרת תנועת HTTPS באמצעות אישור ה-TLS‏ userauth-tls-cert שיצרתם למעלה. בהמשך מוצגת ההגדרה של pkg/gateway.yaml שהתקנתם זה עתה.

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: userauth
  namespace: asm-user-auth
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - '*'
    port:
      name: https
      number: 443
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: userauth-tls-cert
---
# This ensures the OIDC endpoint has at least some route defined.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: userauth-oidc
  namespace: asm-user-auth
spec:
  gateways:
  - userauth
  hosts:
  - '*'
  http:
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: "your-oidc-redirect-path"
    name: user-auth-route
    route:
    - destination:
        host: authservice
        port:
          number: 10004
  1. הגדרת מרחב שמות של תווית default כדי להפעיל istio-proxy הוספה אוטומטית לפריסות.

    kubectl label namespace default istio.io/rev=REVISION --overwrite
    
  2. פורסים את httpbin למרחב השמות default.

    kubectl apply -f https://raw.githubusercontent.com/istio/istio/master/samples/httpbin/httpbin.yaml -n default
    
  3. מעדכנים את httpbin כדי להשתמש בשער הזה להצגת תנועת HTTPS, ומשתמשים בהעברת יציאות כדי לגשת לאפליקציה באופן מקומי:

    kubectl apply -f./samples/httpbin-route.yaml -n default
    kubectl port-forward service/istio-ingressgateway 8443:443 -n asm-user-auth
    
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: httpbin
      namespace: default
    spec:
      gateways:
      - asm-user-auth/userauth
      hosts:
      - '*'
      http:
      - match:
        - uri:
            prefix: /ip
        - uri:
            prefix: /headers
        name: httpbin-routes
        route:
        - destination:
            host: httpbin.default.svc.cluster.local
            port:
              number: 8000
    

    השער של הכניסה ליציאה 8443 יועבר אל localhost כדי לאפשר גישה לאפליקציה באופן מקומי.

  4. פורסים את samples/rctoken-authz.yaml כדי להפעיל את RequestAuthentication ו-AuthorizationPolicy כדי לאמת את ה-RCToken עבור הבקשות.

    kubectl apply -f ./samples/rctoken-authz.yaml -n asm-user-auth
    

    דוגמה samples/rctoken-authz.yaml:

    apiVersion: security.istio.io/v1beta1
    kind: RequestAuthentication
    metadata:
      name: require-rc-token
    spec:
      selector:
        matchLabels:
          istio: ingressgateway
      jwtRules:
      - issuer: "authservice.asm-user-auth.svc.cluster.local"
        audiences:
        - "test_audience"
        jwksUri: "http://authservice.asm-user-auth.svc.cluster.local:10004/_gcp_user_auth/jwks"
        fromHeaders:
        - name: X-ASM-RCTOKEN
        forwardOriginalToken: true
    ---
    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: require-rc-token
    spec:
      selector:
        matchLabels:
          istio: ingressgateway
      action: ALLOW
      rules:
      - when:
        - key: request.auth.claims[iss]
          values:
          - authservice.asm-user-auth.svc.cluster.local
        - key: request.auth.claims[aud]
          values:
          - test_audience
    

אימות משתמשים

הערך httpbin משמש לשתי דרכים, הערך /ip נגיש לציבור והערך /headers מחייב את משתמש הקצה להתחבר דרך ספק הזהויות שהוגדר.

  1. מוודאים שאפשר לגשת לכתובת /ip ישירות דרך https://localhost:8443/ip.

  2. כדי לוודא שדף הכניסה של OIDC מוצג, נכנסים לכתובת https://localhost:8443/headers.

  3. אחרי שמתחברים, לוחצים על הבא ומוודאים שהמערכת מפנה אתכם לדף /headers.

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

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

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

    kubectl apply -f ./samples/httpbin-authz.yaml -n asm-user-auth
    
  2. הקובץ httpbin-authz.yaml מגדיר את שער הכניסה (ingress) לאימות של טוקן ה-RC שהונפק על ידי authservice, ומאשר גישה רק אם ה-JWT מכיל את השדות הרצויים, כמו קהלים ומנפיקים.

    דוגמה למדיניות הרשאות:

    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: require-rc-token
    spec:
      selector:
        matchLabels:
          istio: ingressgateway
      action: ALLOW
      rules:
      - to:
        - operation:
            paths: ["/ip"]
      - to:
        when:
        - key: request.auth.claims[iss]
          values:
          - authservice.asm-user-auth.svc.cluster.local
        - key: request.auth.claims[aud]
          values:
          - test_audience
        - key: request.auth.claims[sub]
          values:
          - allowed_user_sub_1  # Change this with the "sub" claim in the RC token. Wildcard '*' will match everything.
    

הגדרת הגדרות ספציפיות לסביבה

בשלבים הקודמים נעשה שימוש ב-localhost ובאישור HTTPS בחתימה עצמית כדי להגדיר את הכל במהירות. לשימוש אמיתי בסביבת ייצור, צריך להשתמש בדומיין משלכם, כמו example.com.

בנוסף, מוודאים שלקובץ certificateAuthorityData יש את התוכן של אישור הבסיס הרצוי. לדוגמה, אם ספק ה-IDP מהימן עם אישורי הבסיס של המערכת, אפשר להשאיר את השדה הזה ריק. אם יש שרת proxy של HTTPS שמסיים את חיבור ה-HTTPS, צריך להגדיר אותו לאישור הבסיס של ה-proxy.

ניהול מפתחות וביצוע רוטציה שלהם

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

שני המפתחות הם בפורמט JSON. בשדה useAfter מציינים את חותמת הזמן שממנה המפתח ייחשב כמפתח בשימוש. במהלך רוטציית מפתחות, צריך לכלול ב-JSON גם את המפתחות הישנים וגם את המפתחות החדשים. לדוגמה, בדוגמה הבאה, המאפיין new-key ישמש רק אחרי חותמת הזמן 1712813735.

{
   "keys":[
      {
         "kty":"RSA",
         "kid":"old-key",
         "K":"...", # k contains a Base64 encoded PEM format RSA signing key.
         "useAfter": 1612813735, # unix timestamp
      }
      {
      "kty":"RSA",
         "kid":"new-key",
         "K":"...", # k contains a Base64 encoded PEM format RSA signing key.
         "useAfter": 1712813735, # unix timestamp
      }
   ]
}

‫Cloud Service Mesh משתמש במפתח הסימטרי להצפנת נתוני סשן שמאוחסנים בקובצי Cookie בדפדפן. כדי לוודא שהסשנים הקיימים תקפים, authservice מנסה לפענח עם כל המפתחות בקבוצת המפתחות. במהלך הסיבוב, authservice ישתמש במפתח החדש להצפנת סשנים חדשים, וימשיך לנסות לפענח באמצעות המפתחות הישנים.

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

לסיכום, כשמבצעים רוטציית מפתחות, מומלץ:

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

Multi Cluster Deployment

אימות משתמשים ב-Cloud Service Mesh תומך בפריסה של כמה אשכולות. צריך להטמיע אימות משתמשים בכל אשכול, כמו שמתואר למעלה. צריך לשכפל בכל האשכולות את הגדרות אימות המשתמש, כמו משאב מותאם אישית של UserAuth, סוד הלקוח ב-OIDC, מפתחות הצפנה וכו'.

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

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: authservice-fail-over
  namespace: asm-user-auth
spec:
  host: authservice.asm-user-auth.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      localityLbSetting:
        enabled: true
        failover:
        - from:  us-east
          to: us-west
        - from: us-west
          to: us-east

בדומה להגדרות אחרות, צריך להגדיר את זה בכל אשכול.

מיפוי טענות בהתאמה אישית

כדי להגדיר מיפוי של הצהרות מותאמות אישית, מגדירים את spec.authentication.oidc.attributeMapping כדי להגדיר מיפויים מאסימון המזהה של ספק הזהויות המקורי. המפתח יהיה שם הטענה ב-RCToken, והערך יהיה ביטוי CEL שמפרט איך לנתח את הטענה מ-IDToken. כדי להפנות אל IDToken, צריך להשתמש ב-assertion.

דוגמה:

spec:
  authentication:
    oidc:
      attributeMapping:
        aud_copy: assertion.aud
        decision: 'assertion.sub.startsWith("123") ? "success" : "fail"'

ב-RCToken, שדה מוטמע attributes מכיל את השדות שהוגדרו:

"attributes": {
    "aud_copy": "foo.googleusercontent.com",
    "decision": "success"
}

אם ניתוח הערך מ-IDToken באמצעות ביטוי CEL ייכשל, הטענה תתעלם מהערך בלי לגרום לכשל בתהליך האימות.

שדרוגים של אימות משתמשים

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

    kpt pkg get https://github.com/GoogleCloudPlatform/asm-user-auth.git/@v1.2.5 .
    cd asm-user-auth/
    
  2. שומרים את הגדרות הלקוח של OIDC:

    export OIDC_CLIENT_ID=CLIENT_ID
    export OIDC_CLIENT_SECRET=CLIENT_SECRET
    export OIDC_ISSUER_URI=ISSUER_URI
    export OIDC_REDIRECT_HOST=REDIRECT_HOST
    export OIDC_REDIRECT_PATH=REDIRECT_PATH
    
  3. פורסים את שירות אימות המשתמש כדי לשדרג לגרסה חדשה.

פרטים על הגדרת אימות משתמשים

בטבלה הבאה מתואר כל שדה ב-CRD:

שם השדה תיאור
authentication.oidc בקטע הזה מופיעה ההגדרה של נקודת הקצה של OIDC והפרמטרים שמשמשים בתהליך OIDC.
authentication.oidc.certificateAuthorityData זהו אישור הבסיס של ה-SSL של הדומיין של שרת ההרשאות של OIDC או של שרת ה-proxy של HTTPS, אם יש כזה.
authentication.oidc.oauthCredentialsSecret הפניות לסוד מסוג Kubernetes Opaque שמכיל client_id ו-client_secret של OAuth2 OIDC במטען הייעודי (payload) בפורמט JSON.
authentication.oidc.issuerURI מזהה ה-URI לשימוש כמנפיק ב-RCToken של הפלט.
authentication.oidc.proxy שרת proxy ל-IdP שתומך ב-OIDC, אם רלוונטי. בפורמט http://user:password@10.10.10.10:8888.
authentication.oidc.redirectURIHost המארח שישמש ל-URI של סיום OAuth. אם לא תזינו ערך, המערכת תשתמש במארח מכתובת היעד, ותבנה את כתובת ה-URI להפניה אוטומטית באופן דינמי.
אפשר להשתמש בערך הזה כשרוצים סשן SSO לאימות משתמשים בדומיין ברמה גבוהה יותר. לדוגמה, כדי להפעיל כניסה יחידה בין profile.example.com/ ‎ לבין admin.example.com/, אפשר להגדיר את הערך הזה כ-example.com. כך תופעל סשן אימות משתמש ב-example.com, שישותף בין כל תתי-הדומיין. הערה: אם כמה דומיינים מוגשים מאותה רשת, למשל example1.com ו-example2.com, אי אפשר להשתמש בתכונה הזו, ועדיף להשאיר את השדה הזה ריק.
authentication.oidc.redirectURIPath נתיב נקודת הקצה שבו authservice יסיים את תהליך OAuth. צריך לרשום את נתיב ה-URI הזה בתוספת המארח כ-URI מורשה להפניה אוטומטית בשרת ההרשאות של authentication.oidc.clientID.
בנוסף, ה-URI הזה צריך להיות מוגש מאותו Service mesh ומאותו שער כניסה שבהם מופעל authservice.
authentication.oidc.scopes היקף ההרשאות של OAuth שצריך לבקש בבקשת האימות. רשימה של מזהים מופרדים בפסיקים שמשמשים לציון הרשאות הגישה המבוקשות בנוסף להיקף openid, למשל: ‫"groups,allatclaim".
authentication.oidc.groupsClaim אם idtoken מכיל טענה לגבי קבוצות, משתמשים בשדה הזה כדי לציין את השם שלה. אם מציינים את השדה הזה, השירות יעביר את הנתונים בטענה הזו אל הטענה groups ב-RCToken של הפלט. התביעה הזו צריכה להכיל רשימה מופרדת בפסיקים של מחרוזות, למשל: ‪['group1', 'group2'].
authentication.oidc.attributeMapping מכיל מיפוי אחד או יותר של טענות מ-idtoken ואחריו ביטויי CEL. כל הטענות צריכות להיות מופנות אל assertion.X, הטענה assertion מופנית אל טוקן ה-ID המקורי, לדוגמה aud_copy: assertion.aud.
authentication.outputJWTAudience הקהל שאמור להשתמש ב-RCToken שנוצר על ידי authservice. ה-sidecars יכולים לאמת את ה-RCToken הנכנס מול הערך הזה של קהל היעד.

פתרון בעיות

  1. הגישה של ספק הזהויות לרשת.

    יומן אפשרי: error: TLS handshake failed..

    כדי לאמת, מריצים את הפקודה curl מהקונטיינר האפמרי שמצורף ל-istio-proxy כדי לקרוא ל-URI של מנפיק IDP. לדוגמה, ראו איסוף יומנים של Cloud Service Mesh.

    אם אתם לא מצליחים להתחבר, כדאי לבדוק את כללי חומת האש או הגדרות רשת אחרות של האשכול.

  2. אישור CA בסיס.

    יומן אפשרי: error: The server's TLS certificate did not match expectations. או error: TLS handshake failed..

    מוודאים שבתיבת הטקסט certificateAuthorityData מופיע אישור ה-CA הבסיסי הנכון. אם אין שרת proxy של HTTPS שמסיים את תנועת ה-HTTPS, צריך להזין כאן את אישור ה-CA הבסיסי של ספק הזהויות. אם יש כזה, הוא צריך להכיל את כתובת ה-proxy.

  3. הגדרת נתיב ההפניה האוטומטית.

    תצפית אפשרית: מתקבל דף שגיאה 404 במהלך תהליך אימות OIDC.

    האימות של המשתמש מחזיר את הכותרת Set-Cookie בלי להשתמש במאפיין path. כברירת מחדל, הדפדפן משתמש בספרייה של כתובת האתר של הבקשה כנתיב של קובץ ה-Cookie (ההיקף של קובץ ה-Cookie שקשור לנתיב). לכן מומלץ לא לכלול את התו '/' בנתיב ההפניה האוטומטית, אלא אם זו הכוונה.

  4. קובץ העזר לא יכול לאחזר את jwksUri.

    בתרחישים מסוימים, הגבלה של sidecar עלולה לגרום לכשל באחזור של jwksUri. אם מרחב השמות לא מופיע כשמשתמשים בתו כללי (לדוגמה, ./* או istio-system/*), הפעולה לא תצליח. צריך להוסיף באופן ידני את מרחב השמות שלהם ב-sidecar של היציאה.

שאלות נפוצות

  1. איך משדרגים את Cloud Service Mesh עם אימות משתמשים מופעל?

    פועלים לפי תהליך השדרוג של Cloud Service Mesh ומציינים את קובץ השכבה העליונה על ידי הוספת --custom_overlay user-auth-overlay.yaml בשורת הפקודה אל asmcli install.

  2. כמה משאבים צריך להקצות ל-authservice? וכמה בקשות לשנייה הוא יכול לטפל?

    כברירת מחדל, authservice מוגדר עם 2.0 vCPU ו-256Mi זיכרון. בהגדרה כזו, authservice יכול לטפל ב-500 בקשות לשנייה. כדי לטפל בכמות גדולה יותר של בקשות, צריך להקצות יותר CPU, באופן שפרופורציונלי בערך ליכולת הטיפול בבקשות. אפשר גם להגדיר כמה רפליקות של authservice כדי להגדיל את יכולת ההרחבה האופקית.