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

ב-Cloud Service Mesh בגרסה 1.5 ואילך, פרוטוקול TLS הדדי אוטומטי (auto mTLS) מופעל כברירת מחדל. עם mTLS אוטומטי, קובץ עזר חיצוני בצד הלקוח מזהה באופן אוטומטי אם לשרת יש קובץ עזר חיצוני. ה-sidecar של הלקוח שולח mTLS לעומסי עבודה עם sidecar ושולח טקסט לא מוצפן לעומסי עבודה בלי sidecar. עם זאת, שירותים מקבלים תעבורה בטקסט פשוט וגם תעבורת mTLS. כשמזריקים פרוקסי מסוג sidecar ל-Pods, מומלץ גם להגדיר את השירותים כך שיקבלו רק תנועה מסוג 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
    

    אם אתם משתמשים קיימים במישור הבקרה המנוהל של 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. פורסים את שער לדוגמה במאגר 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 לקצה הקדמי ואת חשבונות השירות לעומסי העבודה. במדריך הזה תפרסו את Online Boutique, אפליקציית הדגמה של מיקרו-שירות.

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

צפייה בשירותים

  1. מציגים את ה-pods במרחב השמות 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
    

    כל הפודים של האפליקציה צריכים לפעול, ובעמודה READY צריך להופיע 2/2. המשמעות היא שה-pods קיבלו בהצלחה הזרקה של Envoy sidecar proxy. אם הסמל 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 אמורה להופיע בדפדפן.

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

יצירת פוד TestCurl

יוצרים פוד 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"]

גישה לבוטיק אונליין

  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 אפשר לפרוס שער כניסה. אפשר גם לגשת לחנות המקוונת באמצעות כתובת ה-IP החיצונית של שער הכניסה. מקבלים את כתובת ה-IP החיצונית של השער. מחליפים את ה-placeholders בפרטים הבאים:

    • GATEWAY_SERVICE_NAME : השם של שירות שער הכניסה. אם פרסתם את שער הדוגמה ללא שינוי, או אם פרסתם את שער הכניסה שמוגדר כברירת מחדל, השם הוא 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 וגם תעבורת טקסט רגיל מתקבלות.

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

כדי לאכוף 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. עוברים לכרטיסייה בדפדפן שדרכה ניגשים לחנות הווירטואלית באמצעות כתובת ה-IP החיצונית של שירות frontend-external:

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

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

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

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

    mTLS הדדי

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

    1. הדפדפן שולח בקשת HTTP בטקסט לא מוצפן לשרת.
    2. הבקשה עוברת דרך קונטיינר הפרוקסי של שער הכניסה.
    3. שרת ה-proxy של שער הכניסה מבצע לחיצת יד בפרוטוקול TLS עם שרת ה-proxy בצד השרת (שירות הקצה הקדמי בדוגמה הזו). הלחיצת יד הזו כוללת החלפה של אישורים. האישורים האלה נטענים מראש למאגרי ה-proxy על ידי Cloud Service Mesh.
    4. שרת ה-proxy של שער הכניסה מבצע בדיקה מאובטחת של השמות באישור של השרת, כדי לוודא שזהות מורשית מפעילה את השרת.
    5. שערי הכניסה ושרתי ה-proxy יוצרים חיבור Mutual 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, שבו מוחלת מדיניות STRICT peerAuthentication.

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

  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 הדדי לכל עומס עבודה

כדי להגדיר מדיניות של 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 מוגדר ל-STRICT mTLS, וקובץ העזר החיצוני חוסם את הבקשה.

  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, שבו מוחלת מדיניות STRICT peerAuthentication.

  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, שבו מוחלת מדיניות STRICT peerAuthentication.

  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
    

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