דוגמה ל-Cloud Service Mesh: ‏ mTLS

ב-Cloud Service Mesh מגרסה 1.5 ואילך, פרוטוקול Mutual TLS (mTLS)‎ מופעל אוטומטית כברירת מחדל. עם mTLS אוטומטי, שרת proxy של קובץ עזר חיצוני בצד הלקוח מזהה באופן אוטומטי אם לשרת יש קובץ עזר חיצוני. ה-sidecar בצד הלקוח שולח mTLS לעומסי עבודה עם sidecars, ושולח טקסט פשוט לעומסי עבודה בלי sidecars. עם זאת, חשוב לזכור שהשירותים מקבלים גם תנועה בטקסט פשוט וגם תנועה ב-mTLS. כשאתם מזריקים קובצי עזר חיצוניים לפודים, מומלץ גם להגדיר את השירותים כך שיקבלו רק תנועת mTLS.

באמצעות Cloud Service Mesh, אתם יכולים לאכוף mTLS מחוץ לקוד האפליקציה, על ידי החלה של קובץ YAML יחיד. ‫Cloud Service Mesh מאפשר לכם להחיל מדיניות אימות על כל רשת השירות, על מרחב שמות או על עומס עבודה ספציפי.

mTLS הדדי

עלויות

במסמך הזה משתמשים ברכיבים הבאים של Google Cloud, והשימוש בהם כרוך בתשלום:

בעזרת מחשבון העלויות אתם יכולים להעריך את העלויות בהתאם לשימוש הצפוי.

משתמשים חדשים של Google Cloud ? יכול להיות שאתם זכאים לתקופת ניסיון בחינם.

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

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

פריסת שער כניסה

  1. מגדירים את ההקשר הנוכחי של kubectl לאשכול:

    gcloud container clusters get-credentials CLUSTER_NAME  \
    --project=PROJECT_ID \
    --zone=CLUSTER_LOCATION 
    
  2. יוצרים מרחב שמות לשער הכניסה:

    kubectl create namespace asm-ingress
    
  3. מפעילים את מרחב השמות להזרקה. השלבים תלויים בהטמעה של מישור הבקרה.

    מנוהל (TD)

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

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

    מנוהל (Istiod)

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

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

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

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

      kubectl -n istio-system get controlplanerevision
      

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

      NAME                AGE
      asm-managed-rapid   6d7h
      

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

    2. מחילים את תווית הגרסה על מרחב השמות:

      kubectl label namespace asm-ingress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      
  4. פורסים את שער ה-API לדוגמה במאגר anthos-service-mesh-samples:

    kubectl apply -n asm-ingress \
    -f docs/shared/asm-ingress-gateway
    

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

    serviceaccount/asm-ingressgateway configured
    service/asm-ingressgateway configured
    deployment.apps/asm-ingressgateway configured
    gateway.networking.istio.io/asm-ingressgateway configured
    

פריסת האפליקציה לדוגמה Online Boutique

  1. אם עדיין לא עשיתם זאת, הגדירו את ההקשר הנוכחי של kubectl לאשכול:

      gcloud container clusters get-credentials CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  2. יוצרים את מרחב השמות של האפליקציה לדוגמה:

      kubectl create namespace onlineboutique
    
  3. מוסיפים תווית למרחב השמות onlineboutique כדי להוסיף באופן אוטומטי שרתי proxy של Envoy. פועלים לפי השלבים להפעלת החדרת sidecar אוטומטית.

  4. פורסים את האפליקציה לדוגמה, את VirtualService בשביל ה-frontend ואת חשבונות השירות בשביל עומסי העבודה. במדריך הזה תפרסו את Online Boutique, אפליקציית הדגמה של מיקרו-שירותים (microservices).

      kubectl apply \
      -n onlineboutique \
      -f docs/shared/online-boutique/virtual-service.yaml
      kubectl apply \
      -n onlineboutique \
      -f docs/shared/online-boutique/service-accounts
    

הצגת השירותים שלך

  1. מציגים את הפודים במרחב השמות onlineboutique:

    kubectl get pods -n onlineboutique
    

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

    NAME                                     READY   STATUS    RESTARTS   AGE
    adservice-85598d856b-m84m6               2/2     Running   0          2m7s
    cartservice-c77f6b866-m67vd              2/2     Running   0          2m8s
    checkoutservice-654c47f4b6-hqtqr         2/2     Running   0          2m10s
    currencyservice-59bc889674-jhk8z         2/2     Running   0          2m8s
    emailservice-5b9fff7cb8-8nqwz            2/2     Running   0          2m10s
    frontend-77b88cc7cb-mr4rp                2/2     Running   0          2m9s
    loadgenerator-6958f5bc8b-55q7w           2/2     Running   0          2m8s
    paymentservice-68dd9755bb-2jmb7          2/2     Running   0          2m9s
    productcatalogservice-84f95c95ff-c5kl6   2/2     Running   0          114s
    recommendationservice-64dc9dfbc8-xfs2t   2/2     Running   0          2m9s
    redis-cart-5b569cd47-cc2qd               2/2     Running   0          2m7s
    shippingservice-5488d5b6cb-lfhtt         2/2     Running   0          2m7s
    

    כל ה-pods של האפליקציה אמורים לפעול, עם 2/2 בעמודה READY. זה מצביע על כך שהוזרק בהצלחה קובץ עזר חיצוני של Envoy לפודים. אם לא מופיע 2/2 אחרי כמה דקות, כדאי לעיין במדריך לפתרון בעיות.

  2. משיגים את כתובת ה-IP החיצונית ומגדירים אותה למשתנה:

    kubectl get services -n asm-ingress
    export FRONTEND_IP=$(kubectl --namespace asm-ingress \
    get service --output jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}' \
    )
    

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

    NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                      AGE
    asm-ingressgateway   LoadBalancer   10.19.247.233   35.239.7.64   80:31380/TCP,443:31390/TCP,31400:31400/TCP   27m
    
    
  3. נכנסים לכתובת EXTERNAL-IP בדפדפן האינטרנט. החנות Online Boutique אמורה להופיע בדפדפן.

    ממשק משתמש של בוטיק אונליין

יצירת pod של TestCurl

יוצרים pod של TestCurl כדי לשלוח תנועה בטקסט פשוט לצורך בדיקה.

  apiVersion: v1
  kind: Pod
  metadata:
    name: testcurl
    namespace: default
    annotations:
      sidecar.istio.io/inject: "false"
  spec:
    containers:
    - name: curl
      image: curlimages/curl
      command: ["sleep", "600"]

גישה ל-Online Boutique

  1. מגדירים את ההקשר הנוכחי של kubectl לאשכול שבו פרסתם את Online Boutique:

    gcloud container clusters get-credentials CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  2. מציגים את רשימת השירותים במרחב השמות frontend:

    kubectl get services -n frontend
    

    שימו לב ש-frontend-external הוא LoadBalancer, ויש לו כתובת IP חיצונית. האפליקציה לדוגמה כוללת שירות שהוא מאזן עומסים, כדי שאפשר יהיה לפרוס אותה ב-GKE בלי Cloud Service Mesh.

  3. נכנסים לאפליקציה בדפדפן באמצעות כתובת ה-IP החיצונית של השירות frontend-external:

    http://FRONTEND_EXTERNAL_IP/
    
  4. ב-Cloud Service Mesh אפשר לפרוס שער כניסה. אפשר גם לגשת ל-Online Boutique באמצעות כתובת ה-IP החיצונית של שער הכניסה. משיגים את כתובת ה-IP החיצונית של השער. מחליפים את ה-placeholders במידע הבא:

    • ‫GATEWAY_SERVICE_NAME : השם של שירות שער הכניסה. אם פרסתם את שער הדוגמה בלי שינויים, או אם פרסתם את שער ברירת המחדל של Ingress, השם הוא istio-ingressgateway.
    • ‫GATEWAY_NAMESPACE: מרחב השמות שבו פרסתם את שער הכניסה. אם פרסתם את שער הכניסה שמוגדר כברירת מחדל, מרחב השמות הוא istio-system.
    kubectl get service GATEWAY_NAME -n GATEWAY_NAMESPACE
    
  5. פותחים כרטיסייה אחרת בדפדפן ונכנסים לאפליקציה באמצעות כתובת ה-IP החיצונית של שער הכניסה:

    http://INGRESS_GATEWAY_EXTERNAL_IP/
    
  6. מריצים את הפקודה הבאה כדי curl את השירות frontend באמצעות HTTP פשוט מ-Pod אחר. מכיוון שהשירותים נמצאים במרחבי שמות שונים, צריך להפעיל את הפקודה curl על שם ה-DNS של השירות frontend.

    kubectl debug --image istio/base --target istio-proxy -it \
      $(kubectl get pod -l app=productcatalogservice -n product-catalog -o jsonpath={.items..metadata.name}) \
      -n product-catalog -- \
      curl http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

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

הפעלת Mutual TLS לכל מרחב שמות

כדי לאכוף את mTLS, צריך להחיל מדיניות PeerAuthentication באמצעות kubectl.

  1. שומרים את מדיניות האימות הבאה בתור mtls-namespace.yaml.

    cat <<EOF > mtls-namespace.yaml
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "namespace-policy"
    spec:
      mtls:
        mode: STRICT
    EOF
    

    השורה mode: STRICT בקובץ ה-YAML מגדירה את השירותים כך שיקבלו רק mTLS. כברירת מחדל, הערך של mode הוא PERMISSIVE, שמגדיר את השירותים כך שיקבלו גם טקסט פשוט וגם mTLS.

  2. מחילים את מדיניות האימות כדי להגדיר את כל השירותים של Online Boutique כך שיקבלו רק mTLS:

    for ns in ad cart checkout currency email frontend loadgenerator \
         payment product-catalog recommendation shipping; do
    kubectl apply -n $ns -f mtls-namespace.yaml
    done
    

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

    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created

  3. עוברים לכרטיסייה בדפדפן שדרכה ניגשים ל-Online Boutique באמצעות כתובת ה-IP החיצונית של השירות frontend-external:

    http://FRONTEND_EXTERNAL_IP/
    
  4. יש לרענן את הדף. בדפדפן מופיעה השגיאה הבאה:

    אי אפשר לגשת לאתר

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

  5. עוברים לכרטיסייה בדפדפן שדרכה ניגשים ל-Online Boutique באמצעות כתובת ה-IP החיצונית של istio-ingressgateway, ומרעננים את הדף שמוצג בהצלחה. כשניגשים ל-Online Boutique באמצעות שער הכניסה, הבקשה עוברת בנתיב הבא:

    mTLS הדדי

    תהליך אימות mTLS:

    1. הדפדפן שולח בקשת HTTP בטקסט פשוט לשרת.
    2. מאגר ה-proxy של שער הכניסה מיירט את הבקשה.
    3. שרת ה-proxy של שער הכניסה מבצע לחיצת יד בפרוטוקול TLS עם שרת ה-proxy בצד השרת (שירות החזית בדוגמה הזו). הלחיצת היד הזו כוללת החלפת אישורים. האישורים האלה נטענים מראש לקונטיינרים של שרתי ה-proxy על ידי Cloud Service Mesh.
    4. שרת ה-proxy של שער הכניסה מבצע בדיקת שמות מאובטחת באישור של השרת, כדי לוודא שזהות מורשית מפעילה את השרת.
    5. שער הכניסה ושרתי ה-proxy של השרת יוצרים חיבור TLS הדדי (mTLS), ושרת ה-proxy של השרת מעביר את הבקשה לקונטיינר של אפליקציית השרת (השירות בחזית).
  6. מריצים את הפקודה הבאה כדי curl את השירות frontend באמצעות HTTP פשוט מ-Pod אחר.

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    הבקשה נכשלת כי אנחנו שולחים תנועה בטקסט פשוט מעומס עבודה ללא sidecar שבו חלה מדיניות peerAuthentication מסוג STRICT.

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

  1. כדי להציג רשימה של כל כללי המדיניות של PeerAuthentication ב-Service mesh:

    kubectl get peerauthentication --all-namespaces
    

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

    NAMESPACE         NAME               MODE     AGE
    ad                namespace-policy   STRICT   17m
    cart              namespace-policy   STRICT   17m
    checkout          namespace-policy   STRICT   17m
    currency          namespace-policy   STRICT   17m
    email             namespace-policy   STRICT   17m
    frontend          namespace-policy   STRICT   17m
    loadgenerator     namespace-policy   STRICT   17m
    payment           namespace-policy   STRICT   17m
    product-catalog   namespace-policy   STRICT   17m
    recommendation    namespace-policy   STRICT   17m
    shipping          namespace-policy   STRICT   17m
    
  2. מוחקים את מדיניות האימות מכל מרחבי השמות של Online Boutique:

    for ns in ad cart checkout currency email frontend loadgenerator payment \
      product-catalog recommendation shipping; do
        kubectl delete peerauthentication -n $ns namespace-policy
    done;
    

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

    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    
  3. נכנסים ל-Online Boutique באמצעות כתובת ה-IP החיצונית של השירות frontend-external ומרעננים את הדף. הדף מוצג כמו שצריך.

  4. מריצים את הפקודה הבאה כדי curl את השירות frontend באמצעות HTTP פשוט מ-Pod אחר.

    kubectl debug --image istio/base --target istio-proxy -it \
      $(kubectl get pod -l app=productcatalogservice -n product-catalog -o jsonpath={.items..metadata.name}) \
      -n product-catalog -- \
      curl http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

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

אם תרעננו את הדף במסוף Google Cloud שבו מוצגת הרשימה Workloads, תראו שהסטטוס של mTLS הוא Permissive.

הפעלת פרוטוקול TLS הדדי (mTLS) לכל עומס עבודה

כדי להגדיר מדיניות PeerAuthentication לעומס עבודה ספציפי, צריך להגדיר את הקטע selector ולציין את התוויות שתואמות לעומס העבודה הרצוי. עם זאת, Cloud Service Mesh לא יכול לצבור כללי מדיניות ברמת עומס העבודה לתנועת mTLS יוצאת לשירות. כדי לנהל את ההתנהגות הזו, צריך להגדיר כלל יעד.

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

    cat <<EOF | kubectl apply -n frontend -f -
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "frontend"
      namespace: "frontend"
    spec:
      selector:
        matchLabels:
          app: frontend
      mtls:
        mode: STRICT
    EOF
    

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

    peerauthentication.security.istio.io/frontend created
  2. מגדירים כלל תואם ליעד.

    cat <<EOF | kubectl apply -n frontend -f -
    apiVersion: "networking.istio.io/v1alpha3"
    kind: "DestinationRule"
    metadata:
      name: "frontend"
    spec:
      host: "frontend.demo.svc.cluster.local"
      trafficPolicy:
        tls:
          mode: ISTIO_MUTUAL
    EOF
    

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

    destinationrule.networking.istio.io/frontend created
  3. נכנסים ל-Online Boutique באמצעות כתובת ה-IP החיצונית של השירות frontend-external ומרעננים את הדף. הדף לא מוצג כי frontend service מוגדר ל-mTLS של STRICT, וקובץ העזר החיצוני חוסם את הבקשה.

  4. מריצים את הפקודה הבאה כדי curl את השירות frontend באמצעות HTTP פשוט מ-Pod אחר.

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    הבקשה נכשלת כי אנחנו שולחים תנועה בטקסט פשוט מעומס עבודה ללא sidecar שבו חלה מדיניות peerAuthentication מסוג STRICT.

  5. מוחקים את מדיניות האימות:

    kubectl delete peerauthentication -n frontend frontend
    

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

    peerauthentication.security.istio.io "frontend" deleted
    
  6. מוחקים את כלל היעד:

    kubectl delete destinationrule -n frontend frontend
    

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

    destinationrule.networking.istio.io "frontend" deleted
    

אכיפת mTLS בכל רשת השירותים

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

  1. אכיפת mTLS בכל הרשת:

    kubectl apply -f - <<EOF
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "mesh-wide"
      namespace: "istio-system"
    spec:
      mtls:
        mode: STRICT
    EOF
    

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

    peerauthentication.security.istio.io/mesh-wide created

  2. נכנסים ל-Online Boutique באמצעות כתובת ה-IP החיצונית של השירות frontend-external ומרעננים את הדף. הדף לא מוצג.

  3. מריצים את הפקודה הבאה כדי curl את השירות frontend באמצעות HTTP פשוט מ-Pod אחר.

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    הבקשה נכשלת כי אנחנו שולחים תנועה בטקסט פשוט מעומס עבודה ללא sidecar שבו חלה מדיניות peerAuthentication מסוג STRICT.

  4. מוחקים את המדיניות mesh-wide:

    kubectl delete peerauthentication -n istio-system mesh-wide
    

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

    peerauthentication.security.istio.io "mesh-wide" deleted
    

הסרת המשאבים

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

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

    gcloud container clusters delete  CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  • אם רוצים להשאיר את האשכול ולהסיר את הדוגמה של Online Boutique:

    1. מוחקים את מרחבי השמות של האפליקציה:
      kubectl delete -f online-boutique/kubernetes-manifests/namespaces
    

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

    namespace "ad" deleted
    namespace "cart" deleted
    namespace "checkout" deleted
    namespace "currency" deleted
    namespace "email" deleted
    namespace "frontend" deleted
    namespace "loadgenerator" deleted
    namespace "payment" deleted
    namespace "product-catalog" deleted
    namespace "recommendation" deleted
    namespace "shipping" deleted
    
    1. מוחקים את רשומות השירות:
      kubectl delete -f online-boutique/istio-manifests/allow-egress-googleapis.yaml
    

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

    serviceentry.networking.istio.io "allow-egress-googleapis" deleted
    serviceentry.networking.istio.io "allow-egress-google-metadata" deleted
    

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