שימוש בשערי יציאה (egress) של Cloud Service Mesh באשכולות GKE: הדרכה

במדריך הזה נסביר איך להשתמש בשערי יציאה (egress) של Cloud Service Mesh ובאמצעי בקרה Google Cloud נוספים כדי לאבטח תעבורת נתונים יוצאת (egress) מעומסי עבודה שנפרסו באשכול Google Kubernetes Engine. המדריך הזה נועד לשמש כתוספת לשיטות המומלצות לשימוש בשערי יציאה (egress) של Cloud Service Mesh באשכולות GKE.

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

מטרות

  • מגדירים את התשתית להרצת Cloud Service Mesh:
  • מתקינים את Cloud Service Mesh.
  • מתקינים שרתי proxy של שערים לתעבורת נתונים יוצאת (egress) שפועלים במאגר צמתים ייעודי.
  • הגדרת כללי ניתוב מרובי דיירים לתעבורה חיצונית דרך שער היציאה:
    • אפליקציות במרחב השמות team-x יכולות להתחבר אל example.com
    • אפליקציות במרחב השמות team-y יכולות להתחבר אל httpbin.org
  • משתמשים במשאב Sidecar כדי להגביל את היקף ההגדרה של תעבורת נתונים יוצאת (egress) של פרוקסי ה-sidecar לכל מרחב שמות.
  • הגדרת מדיניות הרשאות כדי לאכוף כללי יציאה.
  • מגדירים את שער היציאה לשדרוג בקשות HTTP רגילות ל-TLS (התחלת TLS).
  • מגדירים את שער היציאה כך שיעביר תעבורת TLS.
  • הגדרת כללי מדיניות של רשת Kubernetes כאמצעי בקרה נוסף על תעבורת נתונים יוצאת (egress).
  • הגדרת גישה ישירה ל-Google APIs באמצעות גישה פרטית ל-Google והרשאות לניהול זהויות והרשאות גישה (IAM).

עלויות

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

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

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

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

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

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  2. Verify that billing is enabled for your Google Cloud project.

  3. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

  4. יוצרים ספריית עבודה לשימוש במהלך ההדרכה:

    mkdir -p ~/WORKING_DIRECTORY
    cd ~/WORKING_DIRECTORY
    
  5. יוצרים סקריפט מעטפת כדי לאתחל את הסביבה בשביל המדריך. מחליפים את המשתנים ועורכים אותם בהתאם לפרויקט ולהעדפות. אם תוקף הסשן שלכם במעטפת פג, מריצים את הסקריפט הזה עם הפקודה source כדי לאתחל מחדש את הסביבה:

    cat << 'EOF' > ./init-egress-tutorial.sh
    #! /usr/bin/env bash
    PROJECT_ID=YOUR_PROJECT_ID
    REGION=REGION
    ZONE=ZONE
    
    gcloud config set project ${PROJECT_ID}
    gcloud config set compute/region ${REGION}
    gcloud config set compute/zone ${ZONE}
    
    EOF
    
  6. הפעלה של compute.googleapis.com:

    gcloud services enable compute.googleapis.com --project=YOUR_PROJECT_ID
    
  7. הופכים את הסקריפט לסקריפט שניתן להפעלה ומריצים אותו באמצעות הפקודה source כדי לאתחל את הסביבה. אם מוצגת בקשה להפעיל את Y, בוחרים באפשרות Y:compute.googleapis.com

    chmod +x ./init-egress-tutorial.sh
    source ./init-egress-tutorial.sh
    
  8. הגדרת התשתית

    יצירת רשת VPC ורשת משנה

    1. יוצרים רשת VPC חדשה:

      gcloud compute networks create vpc-network \
          --subnet-mode custom
      
    2. יוצרים רשת משנה להרצת האשכול עם טווחי כתובות IP משניות שהוקצו מראש לקבוצות Pod ולשירותים. הגישה הפרטית ל-Google מופעלת כדי שאפליקציות עם כתובות IP פנימיות בלבד יוכלו להגיע לשירותים ולממשקי ה-API של Google:

      gcloud compute networks subnets create subnet-gke \
          --network vpc-network \
          --range 10.0.0.0/24 \
          --secondary-range pods=10.1.0.0/16,services=10.2.0.0/20 \
          --enable-private-ip-google-access
      

    הגדרת Cloud NAT

    ‫Cloud NAT מאפשר לעומסי עבודה ללא כתובות IP חיצוניות להתחבר ליעדים באינטרנט ולקבל תשובות נכנסות מהיעדים האלה.

    1. יוצרים Cloud Router:

      gcloud compute routers create nat-router \
          --network vpc-network
      
    2. מוסיפים הגדרת NAT לנתב:

      gcloud compute routers nats create nat-config \
          --router nat-router \
          --nat-all-subnet-ip-ranges \
          --auto-allocate-nat-external-ips
      

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

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

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

      gcloud iam service-accounts create sa-application-nodes \
          --description="SA for application nodes" \
          --display-name="sa-application-nodes"
      
    2. יוצרים חשבון שירות לשימוש על ידי הצמתים במאגר הצמתים של השער:

      gcloud iam service-accounts create sa-gateway-nodes \
          --description="SA for gateway nodes" \
          --display-name="sa-gateway-nodes"
      

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

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

        project_roles=(
            roles/logging.logWriter
            roles/monitoring.metricWriter
            roles/monitoring.viewer
            roles/storage.objectViewer
        )
        for role in "${project_roles[@]}"
        do
            gcloud projects add-iam-policy-binding ${PROJECT_ID} \
                --member="serviceAccount:sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
                --role="$role"
            gcloud projects add-iam-policy-binding ${PROJECT_ID} \
                --member="serviceAccount:sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
                --role="$role"
        done
    

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

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

    1. יוצרים כלל ברירת מחדל (עדיפות נמוכה) של חומת אש שחוסם את כל התעבורה היוצאת מרשת ה-VPC:

      gcloud compute firewall-rules create global-deny-egress-all \
          --action DENY \
          --direction EGRESS \
          --rules all \
          --destination-ranges 0.0.0.0/0 \
          --network vpc-network \
          --priority 65535 \
          --description "Default rule to deny all egress from the network."
      
    2. יוצרים כלל שמאפשר רק לצמתים עם חשבון השירות של שער הגישה להגיע לאינטרנט:

      gcloud compute firewall-rules create gateway-allow-egress-web \
          --action ALLOW \
          --direction EGRESS \
          --rules tcp:80,tcp:443 \
          --target-service-accounts sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
          --network vpc-network \
          --priority 1000 \
          --description "Allow the nodes running the egress gateways to connect to the web"
      
    3. מאפשרים לצמתים להגיע למישור הבקרה של Kubernetes:

      gcloud compute firewall-rules create allow-egress-to-api-server \
          --action ALLOW \
          --direction EGRESS \
          --rules tcp:443,tcp:10250 \
          --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
          --destination-ranges 10.5.0.0/28 \
          --network vpc-network \
          --priority 1000 \
          --description "Allow nodes to reach the Kubernetes API server."
      
    4. ‫Cloud Service Mesh משתמש ב-webhook כשמזריקים שרתי proxy מסוג sidecar לעומסי עבודה. מאפשרים לשרת GKE API לקרוא ל-webhooks שנחשפים על ידי מישור הבקרה של Service mesh שפועל בצמתים:

      gcloud compute firewall-rules create allow-ingress-api-server-to-webhook \
          --action ALLOW \
          --direction INGRESS \
          --rules tcp:15017 \
          --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
          --source-ranges 10.5.0.0/28 \
          --network vpc-network \
          --priority 1000 \
          --description "Allow the API server to call the webhooks exposed by istiod discovery"
      
    5. מאפשרים קישוריות יוצאת בין הצמתים לבין ה-Pods שפועלים באשכול. מערכת GKE יוצרת באופן אוטומטי כלל תואם של תעבורת נתונים נכנסת (ingress). לא נדרש כלל לקישוריות של שירותים, כי שרשרת הניתוב של iptables תמיד ממירה כתובות IP של שירותים לכתובות IP של Pod.

      gcloud compute firewall-rules create allow-egress-nodes-and-pods \
          --action ALLOW \
          --direction EGRESS \
          --rules all \
          --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
          --destination-ranges 10.0.0.0/24,10.1.0.0/16 \
          --network vpc-network \
          --priority 1000 \
          --description "Allow egress to other Nodes and Pods"
      
    6. מאפשרים גישה לקבוצות השמורות של כתובות ה-IP שמשמשות את הגישה הפרטית ל-Google להצגת ממשקי Google API,‏ Container Registry ושירותים אחרים:

      gcloud compute firewall-rules create allow-egress-gcp-apis \
          --action ALLOW \
          --direction EGRESS \
          --rules tcp \
          --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
          --destination-ranges 199.36.153.8/30 \
          --network vpc-network \
          --priority 1000 \
          --description "Allow access to the VIPs used by Google Cloud APIs (Private Google Access)"
      
    7. מתן הרשאה לשירות Google Cloud לבדוק את תקינות הפודים שפועלים באשכול. מידע נוסף זמין במאמר בנושא בדיקות תקינות.

      gcloud compute firewall-rules create allow-ingress-gcp-health-checker \
          --action ALLOW \
          --direction INGRESS \
          --rules tcp:80,tcp:443 \
          --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
          --source-ranges 35.191.0.0/16,130.211.0.0/22,209.85.152.0/22,209.85.204.0/22 \
          --network vpc-network \
          --priority 1000 \
          --description "Allow workloads to respond to Google Cloud health checks"
      

    הגדרת גישה פרטית ל- Google Cloud API

    הגישה הפרטית ל-Google מאפשרת למכונות וירטואליות ול-Pods שיש להם רק כתובות IP פנימיות לגשת לשירותים ול-Google APIs. למרות שממשקי ה-API והשירותים של Google מוגשים מכתובות IP חיצוניות, התעבורה מהצמתים אף פעם לא יוצאת מהרשת של Google כשמשתמשים בגישה פרטית ל-Google.

    מפעילים את Cloud DNS API:

    gcloud services enable dns.googleapis.com
    

    יוצרים אזור DNS פרטי, רשומות CNAME ו-A כדי שהצמתים ועומסי העבודה יוכלו להתחבר לשירותים ול-Google APIs באמצעות גישה פרטית ל-Google ושם המארח private.googleapis.com:

    gcloud dns managed-zones create private-google-apis \
        --description "Private DNS zone for Google APIs" \
        --dns-name googleapis.com \
        --visibility private \
        --networks vpc-network
    
    gcloud dns record-sets transaction start --zone private-google-apis
    
    gcloud dns record-sets transaction add private.googleapis.com. \
        --name "*.googleapis.com" \
        --ttl 300 \
        --type CNAME \
        --zone private-google-apis
    
    gcloud dns record-sets transaction add "199.36.153.8" \
    "199.36.153.9" "199.36.153.10" "199.36.153.11" \
        --name private.googleapis.com \
        --ttl 300 \
        --type A \
        --zone private-google-apis
    
    gcloud dns record-sets transaction execute --zone private-google-apis
    

    הגדרת גישה פרטית ל-Container Registry

    יוצרים שרת DNS פרטי, רשומת CNAME ורשומת A כדי שהצמתים יוכלו להתחבר ל-Container Registry באמצעות גישה פרטית ל-Google ושם המארח gcr.io:

    gcloud dns managed-zones create private-gcr-io \
        --description "private zone for Container Registry" \
        --dns-name gcr.io \
        --visibility private \
        --networks vpc-network
    
    gcloud dns record-sets transaction start --zone private-gcr-io
    
    gcloud dns record-sets transaction add gcr.io. \
        --name "*.gcr.io" \
        --ttl 300 \
        --type CNAME \
        --zone private-gcr-io
    
    gcloud dns record-sets transaction add "199.36.153.8" "199.36.153.9" "199.36.153.10" "199.36.153.11" \
        --name gcr.io \
        --ttl 300 \
        --type A \
        --zone private-gcr-io
    
    gcloud dns record-sets transaction execute --zone private-gcr-io
    

    יצירת אשכול פרטי של GKE

    1. כדי להוסיף את כתובת ה-IP החיצונית של Cloud Shell לרשימת הרשתות שמורשות לגשת לשרת ה-API של האשכול:

      SHELL_IP=$(dig TXT -4 +short @ns1.google.com o-o.myaddr.l.google.com)
      

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

      cat << 'EOF' >> ./init-egress-tutorial.sh
      SHELL_IP=$(dig TXT -4 +short @ns1.google.com o-o.myaddr.l.google.com)
      gcloud container clusters update cluster1 \
          --enable-master-authorized-networks \
          --master-authorized-networks ${SHELL_IP//\"}/32
      EOF
      
    2. מפעילים את Google Kubernetes Engine API:

      gcloud services enable container.googleapis.com
      
    3. יוצרים אשכול GKE פרטי:

      gcloud container clusters create cluster1 \
          --enable-ip-alias \
          --enable-private-nodes \
          --release-channel "regular" \
          --enable-master-authorized-networks \
          --master-authorized-networks ${SHELL_IP//\"}/32 \
          --master-ipv4-cidr 10.5.0.0/28 \
          --enable-dataplane-v2 \
          --service-account "sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
          --machine-type "e2-standard-4" \
          --network "vpc-network" \
          --subnetwork "subnet-gke" \
          --cluster-secondary-range-name "pods" \
          --services-secondary-range-name "services" \
          --workload-pool "${PROJECT_ID}.svc.id.goog" \
          --zone ${ZONE}
      

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

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

      ‫Google ממליצה להירשם לערוץ ההפצה 'רגיל' כדי לוודא שהצמתים מריצים גרסת Kubernetes שנתמכת על ידי Cloud Service Mesh.

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

      איחוד זהויות של עומסי עבודה ל-GKE מופעל באשכול. ‫Cloud Service Mesh דורש איחוד זהויות של עומסי עבודה ל-GKE, וזו הדרך המומלצת לגשת ל-Google APIs מעומסי עבודה של GKE.

    4. יוצרים מאגר צמתים בשם gateway. מאגר הצמתים הזה הוא המקום שבו שער היציאה (egress) נפרס. הdedicated=gateway:NoSchedule taint מתווסף לכל צומת במאגר הצמתים של השער.

      gcloud container node-pools create "gateway" \
          --cluster "cluster1" \
          --machine-type "e2-standard-4" \
          --node-taints dedicated=gateway:NoSchedule \
          --service-account "sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
          --num-nodes "1"
      

      taints and tolerations ב-Kubernetes עוזרים לוודא שרק קבוצות Pod של שער יציאה פועלות בצמתים במאגר הצמתים של שער היציאה.

    5. מורידים את פרטי הכניסה כדי להתחבר לאשכול באמצעות kubectl:

      gcloud container clusters get-credentials cluster1
      
    6. מוודאים שלצמתי השער יש את ה-taint הנכון:

      kubectl get nodes -l cloud.google.com/gke-nodepool=gateway -o yaml \
      -o=custom-columns='name:metadata.name,taints:spec.taints[?(@.key=="dedicated")]'
      

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

      name                                 taints
      gke-cluster1-gateway-9d65b410-cffs   map[effect:NoSchedule key:dedicated value:gateway]
      

    התקנה והגדרה של Cloud Service Mesh

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

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

    התקנה של שער לתעבורת נתונים יוצאת

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

      kubectl create namespace istio-egress
      
    2. כשפורסים את שער היציאה, ההגדרה מוזרקת באופן אוטומטי על סמך תווית שמחילים על הפריסה או על מרחב השמות. אם תג ברירת המחדל מוגדר, צריך להוסיף למרחב השמות את התווית default injection labels. אחרת, צריך להשתמש בrevision label למישור הבקרה שהתקנתם.

      במישורי בקרה בתוך האשכול, בדרך כלל לשירות istiod ולפריסה יש תווית של עדכון שדומה ל-istio.io/rev=, כאשר מציין את הגרסה של Cloud Service Mesh. המספר של הגרסה הופך לחלק מistiod שם השירות, לדוגמה: istiod-.istio-system

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

      kubectl get deploy -n istio-system -l app=istiod \
        -o=jsonpath='{.items[*].metadata.labels.istio\.io\/rev}''{"\n"}'
      
    3. אופציונלי: מתייגים את מרחב השמות כדי שהגדרת השער תוזרק אוטומטית. מספיק להוסיף תווית למרחב השמות או לפריסה. לצורך המדריך הזה, צריך להוסיף תווית לשניהם כדי להימנע מאזהרות מהכלי istioctl analyze.

      kubectl label namespace istio-egress istio.io/rev=REVISION
      
    4. יוצרים מניפסט של אופרטור לשער היציאה:

      cat << EOF > egressgateway-operator.yaml
      apiVersion: install.istio.io/v1alpha1
      kind: IstioOperator
      metadata:
        name: egressgateway-operator
        annotations:
          config.kubernetes.io/local-config: "true"
      spec:
        profile: empty
        revision: REVISION
        components:
          egressGateways:
          - name: istio-egressgateway
            namespace: istio-egress
            enabled: true
        values:
          gateways:
            istio-egressgateway:
              injectionTemplate: gateway
              tolerations:
                - key: "dedicated"
                  operator: "Equal"
                  value: "gateway"
              nodeSelector:
                cloud.google.com/gke-nodepool: "gateway"
      EOF
      
    5. מורידים את הכלי istioctl. צריך להשתמש בגרסה 1.16.2-asm.2 ואילך, גם אם אתם משתמשים ב-Cloud Service Mesh מגרסה 1.15 ומטה. הורדת הגרסה הנכונה של istioctl

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

      ISTIOCTL=$(find "$(pwd -P)" -name istioctl)
      echo "ISTIOCTL=\"${ISTIOCTL}\"" >> ./init-egress-tutorial.sh
      
    7. יוצרים את מניפסט ההתקנה של שער היציאה באמצעות מניפסט האופרטור istioctl:

      ${ISTIOCTL} manifest generate \
          --filename egressgateway-operator.yaml \
          --output egressgateway \
          --cluster-specific
      
    8. מתקינים את שער היציאה:

      kubectl apply --recursive --filename egressgateway/
      
    9. בודקים ששער היציאה פועל בצמתים ב-node pool‏ gateway:

      kubectl get pods -n istio-egress -o wide
      
    10. ל-pods של שער היציאה יש affinity לצמתים במאגר הצמתים gateway וטולרנטיות שמאפשרת להם לפעול בצמתים של השער עם ההגדרה tainted. בודקים את ההעדפה של הצמתים ואת הטולרנטיות של ה-Pods של שער היציאה:

      kubectl -n istio-egress get pod -l istio=egressgateway \
          -o=custom-columns='name:metadata.name,node-affinity:spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms,tolerations:spec.tolerations[?(@.key=="dedicated")]'
      

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

      name                                   node-affinity                                                                                   tolerations
      istio-egressgateway-754d9684d5-jjkdz   [map[matchExpressions:[map[key:cloud.google.com/gke-nodepool operator:In values:[gateway]]]]]   map[key:dedicated operator:Equal value:gateway]
      

    הפעלת רישום ביומן של גישת Envoy

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

    הכנת הרשת ואפליקציית בדיקה

    1. מוודאים שפרוטוקול TLS הדדי מחמיר מופעל. החלת מדיניות ברירת מחדל PeerAuthentication עבור הרשת במרחב השמות istio-system:

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

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

    2. יוצרים מרחבי שמות לשימוש בפריסת עומסי עבודה לבדיקה. בשלבים הבאים במדריך הזה מוסבר איך להגדיר כללי ניתוב שונים של תעבורת נתונים יוצאת (egress) לכל מרחב שמות.

      kubectl create namespace team-x
      kubectl create namespace team-y
      
    3. מתייגים את מרחבי השמות כדי שאפשר יהיה לבחור אותם באמצעות כללי מדיניות של רשת Kubernetes:

      kubectl label namespace team-x team=x
      kubectl label namespace team-y team=y
      
    4. כדי ש-Cloud Service Mesh יזריק באופן אוטומטי sidecars של proxy, צריך להגדיר את התווית של עדכון מישור הבקרה במרחבי השמות של עומס העבודה:

      kubectl label ns team-x istio.io/rev=REVISION
      kubectl label ns team-y istio.io/rev=REVISION
      
    5. יוצרים קובץ YAML לשימוש בפריסות בדיקה:

      cat << 'EOF' > ./test.yaml
      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: test
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: test
        labels:
          app: test
      spec:
        ports:
        - port: 80
          name: http
        selector:
          app: test
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: test
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: test
        template:
          metadata:
            labels:
              app: test
          spec:
            serviceAccountName: test
            containers:
            - name: test
              image: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
              command: ["/bin/sleep", "infinity"]
              imagePullPolicy: IfNotPresent
      EOF
      
    6. פורסים את אפליקציית הבדיקה במרחב השמות team-x:

      kubectl -n team-x create -f ./test.yaml
      
    7. מוודאים שאפליקציית הבדיקה נפרסה לצומת במאגר ברירת המחדל ושקונטיינר proxy sidecar מוזרק. חוזרים על הפקודה הבאה עד שהסטטוס של ה-Pod הוא Running:

      kubectl -n team-x get po -l app=test -o wide
      

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

      NAME                   READY   STATUS    RESTARTS   AGE   IP          NODE                                      NOMINATED NODE   READINESS GATES
      test-d5bdf6f4f-9nxfv   2/2     Running   0          19h   10.1.1.25   gke-cluster1-default-pool-f6c7a51f-wbzj
      

      ‫2 מתוך 2 מאגרים בסטטוס Running. קונטיינר אחד הוא אפליקציית הבדיקה והשני הוא proxy sidecar.

      ה-Pod פועל בצומת במאגר הצמתים שמוגדר כברירת מחדל.

    8. מוודאים שאי אפשר לשלוח בקשת HTTP ממסגרת הבדיקה לאתר חיצוני:

      kubectl -n team-x exec -it \
          $(kubectl -n team-x get pod -l app=test -o jsonpath={.items..metadata.name}) \
          -c test -- curl -v http://example.com
      

      נוצרת הודעת שגיאה משרת ה-Proxy של קובץ העזר החיצוני כי כלל חומת האש global-deny-egress-all דוחה את החיבור במעלה הזרם.

    שימוש במשאב Sidecar כדי להגביל את ההיקף של הגדרת שרת proxy מסוג Sidecar

    אתם יכולים להשתמש במשאב Sidecar כדי להגביל את היקף המאזין ליציאה שמוגדר ל-Sidecar proxies. כדי לצמצם את נפח ההגדרות ואת השימוש בזיכרון, מומלץ להחיל משאב Sidecar ברירת מחדל על כל מרחב שמות.

    שרת ה-proxy שמופעל ב-Cloud Service Mesh ב-sidecar הוא Envoy. בטרמינולוגיה של Envoy,‏ cluster היא קבוצה דומה מבחינה לוגית של נקודות קצה במעלה הזרם שמשמשות כיעדים לאיזון עומסים.

    1. מריצים את הפקודה istioctl proxy-config כדי לבדוק את האשכולות היוצאים שהוגדרו ב-Envoy קובץ עזר חיצוני עבור ה-Pod של הבדיקה:

      ${ISTIOCTL} pc c $(kubectl -n team-x get pod -l app=test \
          -o jsonpath={.items..metadata.name}).team-x --direction outbound
      

      ברשימה יש בערך 11 אשכולות של Envoy, כולל כמה לשער היציאה.

    2. הגבלת הגדרת ה-proxy לנתיבי יציאה שהוגדרו באופן מפורש באמצעות service entries במרחבי השמות של היציאה ושל team-x. החלת משאב Sidecar על מרחב השמות team-x:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: Sidecar
      metadata:
        name: default
        namespace: team-x
      spec:
        outboundTrafficPolicy:
          mode: REGISTRY_ONLY
        egress:
        - hosts:
          - 'istio-egress/*'
          - 'team-x/*'
      EOF
      

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

      ההגדרה egress.hosts מציינת ששרת ה-proxy מסוג קובץ עזר חיצוני בוחר רק מסלולים ממרחב השמות של תעבורת נתונים יוצאת שזמינים באמצעות המאפיין exportTo. החלק team-x/* כולל את כל המסלולים שהוגדרו באופן מקומי במרחב השמות team-x.

    3. אפשר לראות את האשכולות היוצאים שהוגדרו ב-Envoy קובץ עזר חיצוני ולהשוות אותם לרשימת האשכולות שהוגדרו לפני החלת המשאב Sidecar:

      ${ISTIOCTL} pc c $(kubectl -n team-x get pod -l app=test \
          -o jsonpath={.items..metadata.name}).team-x --direction outbound
      

      אפשר לראות אשכולות עבור שער היציאה ואשכול אחד עבור ה-pod של הבדיקה עצמו.

    הגדרת Cloud Service Mesh לניתוב תעבורה דרך שער היציאה

    1. מגדירים Gateway לתנועת HTTP ביציאה 80. ‫Gateway בוחר את שרת ה-proxy של שער היציאה שפרסתם במרחב השמות של היציאה. ההגדרה של Gateway חלה על מרחב השמות של תעבורת הנתונים היוצאת (egress) ומטפלת בתעבורה של כל מארח.

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: Gateway
      metadata:
        name: egress-gateway
        namespace: istio-egress
      spec:
        selector:
          istio: egressgateway
        servers:
        - port:
            number: 80
            name: https
            protocol: HTTPS
          hosts:
            - '*'
          tls:
            mode: ISTIO_MUTUAL
      EOF
      
    2. יוצרים DestinationRule לשער היציאה עם TLS הדדי (mTLS) לאימות ולהצפנה. משתמשים בכלל יעד משותף אחד לכל המארחים החיצוניים.

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: DestinationRule
      metadata:
        name: target-egress-gateway
        namespace: istio-egress
      spec:
        host: istio-egressgateway.istio-egress.svc.cluster.local
        subsets:
        - name: target-egress-gateway-mTLS
          trafficPolicy:
            tls:
              mode: ISTIO_MUTUAL
      EOF
      
    3. יוצרים ServiceEntry במרחב השמות של תעבורת נתונים יוצאת (egress) כדי לרשום באופן מפורש את example.com במרשם השירותים של הרשת עבור מרחב השמות team-x:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: ServiceEntry
      metadata:
        name: example-com-ext
        namespace: istio-egress
        labels:
          # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
          service.istio.io/canonical-name: example.com
      spec:
        hosts:
        - example.com
        ports:
        - number: 80
          name: http
          protocol: HTTP
        - number: 443
          name: tls
          protocol: TLS
        resolution: DNS
        location: MESH_EXTERNAL
        exportTo:
        - 'team-x'
        - 'istio-egress'
      EOF
      
    4. יוצרים VirtualService כדי להפנות תנועה אל example.com דרך שער היציאה. יש שני תנאי התאמה: התנאי הראשון מפנה את התנועה לשער היציאה, והתנאי השני מפנה את התנועה משער היציאה למארח היעד. המאפיין exportTo קובע אילו מרחבי שמות יכולים להשתמש בשירות הווירטואלי.

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: VirtualService
      metadata:
        name: example-com-through-egress-gateway
        namespace: istio-egress
      spec:
        hosts:
        - example.com
        gateways:
        - istio-egress/egress-gateway
        - mesh
        http:
        - match:
          - gateways:
            - mesh
            port: 80
          route:
          - destination:
              host: istio-egressgateway.istio-egress.svc.cluster.local
              subset: target-egress-gateway-mTLS
              port:
                number: 80
            weight: 100
        - match:
          - gateways:
            - istio-egress/egress-gateway
            port: 80
          route:
          - destination:
              host: example.com
              port:
                number: 80
            weight: 100
        exportTo:
        - 'istio-egress'
        - 'team-x'
      EOF
      
    5. מריצים את הפקודה istioctl analyze כדי לבדוק אם יש שגיאות בהגדרות:

      ${ISTIOCTL} analyze -n istio-egress --revision REVISION
      

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

      ✔ No validation issues found when analyzing namespace: istio-egress.
      
    6. שולחים כמה בקשות דרך שער היציאה לאתר החיצוני:

      for i in {1..4}
      do
          kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
              -o jsonpath={.items..metadata.name}) -c test -- \
          curl -s -o /dev/null -w "%{http_code}\n" http://example.com
      done
      

      מוצגים קודי הסטטוס 200 לכל ארבע התגובות.

    7. כדי לוודא שהבקשות הופנו דרך שער היציאה, בודקים את יומני הגישה של ה-proxy. קודם כל, בודקים את יומן הגישה של ה-proxy sidecar שנפרס עם אפליקציית הבדיקה:

      kubectl -n team-x logs -f $(kubectl -n team-x get pod -l app=test \
          -o jsonpath={.items..metadata.name}) istio-proxy
      

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

      [2020-09-14T17:37:08.045Z] "HEAD / HTTP/1.1" 200 - "-" "-" 0 0 5 4 "-" "curl/7.67.0" "d57ea5ad-90e9-46d9-8b55-8e6e404a8f9b" "example.com" "10.1.4.12:8080" outbound|80||istio-egressgateway.istio-egress.svc.cluster.local 10.1.0.17:42140 93.184.216.34:80 10.1.0.17:60326 - -
      
    8. כדאי גם לבדוק את יומן הגישה של שער היציאה:

      kubectl -n istio-egress logs -f $(kubectl -n istio-egress get pod -l istio=egressgateway \
          -o jsonpath="{.items[0].metadata.name}") istio-proxy
      

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

      [2020-09-14T17:37:08.045Z] "HEAD / HTTP/2" 200 - "-" "-" 0 0 4 3 "10.1.0.17" "curl/7.67.0" "095711e6-64ef-4de0-983e-59158e3c55e7" "example.com" "93.184.216.34:80" outbound|80||example.com 10.1.4.12:37636 10.1.4.12:8080 10.1.0.17:44404 outbound_.80_.target-egress-gateway-mTLS_.istio-egressgateway.istio-egress.svc.cluster.local -
      

    הגדרת ניתוב שונה למרחב שמות שני

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

    1. יוצרים משאב Sidecar למרחב השמות team-y:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: Sidecar
      metadata:
        name: default
        namespace: team-y
      spec:
        outboundTrafficPolicy:
          mode: REGISTRY_ONLY
        egress:
        - hosts:
          - 'istio-egress/*'
          - 'team-y/*'
      EOF
      
    2. פורסים את אפליקציית הבדיקה במרחב השמות team-y:

      kubectl -n team-y create -f ./test.yaml
      
    3. רושמים מארח חיצוני שני ומייצאים אותו למרחב השמות team-x ולמרחב השמות team-y:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: ServiceEntry
      metadata:
        name: httpbin-org-ext
        namespace: istio-egress
        labels:
          # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
          service.istio.io/canonical-name: httpbin.org
      spec:
        hosts:
        - httpbin.org
        ports:
        - number: 80
          name: http
          protocol: HTTP
        - number: 443
          name: tls
          protocol: TLS
        resolution: DNS
        location: MESH_EXTERNAL
        exportTo:
        - 'istio-egress'
        - 'team-x'
        - 'team-y'
      EOF
      
    4. יוצרים שירות וירטואלי לניתוב תנועה אל httpbin.org דרך שער היציאה:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: VirtualService
      metadata:
        name: httpbin-org-through-egress-gateway
        namespace: istio-egress
      spec:
        hosts:
        - httpbin.org
        gateways:
        - istio-egress/egress-gateway
        - mesh
        http:
        - match:
          - gateways:
            - mesh
            port: 80
          route:
          - destination:
              host: istio-egressgateway.istio-egress.svc.cluster.local
              subset: target-egress-gateway-mTLS
              port:
                number: 80
            weight: 100
        - match:
          - gateways:
            - istio-egress/egress-gateway
            port: 80
          route:
          - destination:
              host: httpbin.org
              port:
                number: 80
            weight: 100
        exportTo:
        - 'istio-egress'
        - 'team-x'
        - 'team-y'
      EOF
      
    5. מריצים את הפקודה istioctl analyze כדי לבדוק אם יש שגיאות בהגדרות:

      ${ISTIOCTL} analyze -n istio-egress --revision REVISION
      

      הפרטים שמוצגים הם:

      ✔ No validation issues found when analyzing namespace: istio-egress.
      
    6. שליחת בקשה אל httpbin.org מאפליקציית הבדיקה team-y:

      kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test -o \
          jsonpath={.items..metadata.name}) -c test -- curl -I http://httpbin.org
      

      מוצגת 200 OK תשובה.

    7. בנוסף, שולחים בקשה אל httpbin.org מאפליקציית הבדיקה team-x:

      kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
          -o jsonpath={.items..metadata.name}) -c test -- curl -I http://httpbin.org
      

      מוצגת 200 OK תשובה.

    8. ניסיון לשלוח בקשה אל example.com ממרחב השמות team-y:

      kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test \
          -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
      

      הבקשה נכשלת כי לא מוגדר מסלול ליציאה עבור המארח example.com.

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

    במדריך הזה, מדיניות ההרשאות לשער היציאה נוצרת במרחב השמות istio-egress. אפשר להגדיר RBAC ב-Kubernetes כך שרק לאדמינים של הרשת תהיה גישה למרחב השמות istio-egress.

    1. יוצרים AuthorizationPolicy כדי שאפליקציות במרחב השמות team-x יוכלו להתחבר ל-example.com אבל לא למארחים חיצוניים אחרים כשהן שולחות בקשות באמצעות יציאה 80. היציאה התואמת targetPort בתרמילי שער היציאה היא 8080.

      cat <<EOF | kubectl apply -f -
      apiVersion: security.istio.io/v1beta1
      kind: AuthorizationPolicy
      metadata:
        name: egress-team-x-to-example-com
        namespace: istio-egress
      spec:
        action: ALLOW
        rules:
          - from:
            - source:
                namespaces:
                - 'team-x'
            to:
            - operation:
                hosts:
                  - 'example.com'
            when:
            - key: destination.port
              values: ["8080"]
      EOF
      
    2. מוודאים שאפשר לשלוח בקשה אל example.com מאפליקציית הבדיקה במרחב השמות team-x:

      kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
          -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
      

      מוצגת 200 OK תשובה.

    3. מנסים לשלוח בקשה אל httpbin.org מאפליקציית הבדיקה במרחב השמות team-x:

      kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
          -o jsonpath={.items..metadata.name}) -c test -- curl -s -w " %{http_code}\n" \
          http://httpbin.org
      

      הבקשה נכשלת עם ההודעה RBAC: access denied וקוד הסטטוס 403 Forbidden. יכול להיות שתצטרכו להמתין כמה שניות כי בדרך כלל יש עיכוב קצר לפני שמדיניות ההרשאות נכנסת לתוקף.

    4. מדיניות ההרשאות מספקת שליטה מקיפה על התנועה שמותרת או נדחית. כדי לאפשר לאפליקציית הבדיקה במרחב השמות team-y לשלוח בקשות אל httpbin.org באמצעות נתיב כתובת ה-URL מסוים כששולחים בקשות באמצעות יציאה 80, צריך להחיל את מדיניות ההרשאות הבאה. היציאה התואמת targetPort בתרמילי שער תעבורת נתונים יוצאת (egress) היא 8080.

      cat <<EOF | kubectl apply -f -
      apiVersion: security.istio.io/v1beta1
      kind: AuthorizationPolicy
      metadata:
        name: egress-team-y-to-httpbin-teapot
        namespace: istio-egress
      spec:
        action: ALLOW
        rules:
          - from:
            - source:
                namespaces:
                - 'team-y'
            to:
            - operation:
                hosts:
                - httpbin.org
                paths: ['/status/418']
            when:
            - key: destination.port
              values: ["8080"]
      EOF
      
    5. מנסים להתחבר אל httpbin.org מאפליקציית הבדיקה במרחב השמות team-y:

      kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test \
          -o jsonpath={.items..metadata.name}) -c test -- curl -s -w " %{http_code}\n" \
          http://httpbin.org
      

      הבקשה נכשלת עם ההודעה RBAC: access denied וקוד הסטטוס 403 Forbidden.

    6. עכשיו שולחים בקשה אל httpbin.org/status/418 מאותה אפליקציה:

      kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test \
          -o jsonpath={.items..metadata.name}) -c test -- curl http://httpbin.org/status/418
      

      הבקשה מצליחה כי הנתיב תואם לתבנית במדיניות ההרשאה. הפלט אמור להיראות כך:

         -=[ teapot ]=-
            _...._
          .'  _ _ `.
         | ."` ^ `". _,
         \_;`"---"`|//
           |       ;/
           \_     _/
             `"""`
      

    התחלת TLS בשער היציאה

    אפשר להגדיר שערים ליציאת נתונים כדי upgrade (ליצור) בקשות HTTP רגילות ל-TLS או ל-TLS הדדי. יש כמה יתרונות לשימוש באפליקציות המבצעות בקשות HTTP רגילות, בשילוב עם Mutual TLS (mTLS) ועם התחלת TLS ב-Istio. מידע נוסף זמין במדריך לשיטות מומלצות.

    התחלת TLS בשער יציאה

    1. ‫Create a DestinationRule. The DestinationRule מציין ששער יתחיל חיבור TLS אל example.com.

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: DestinationRule
      metadata:
        name: example-com-originate-tls
        namespace: istio-egress
      spec:
        host: example.com
        subsets:
          - name: example-com-originate-TLS
            trafficPolicy:
              portLevelSettings:
              - port:
                  number: 443
                tls:
                  mode: SIMPLE
                  sni: example.com
      EOF
      
    2. מעדכנים את השירות הווירטואלי עבור example.com כך שהבקשות ליציאה 80 בשער יהיו upgraded ל-TLS ביציאה 443 כשהן נשלחות למארח היעד:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: example-com-through-egress-gateway
        namespace: istio-egress
      spec:
        hosts:
        - example.com
        gateways:
        - mesh
        - istio-egress/egress-gateway
        http:
        - match:
          - gateways:
            - mesh
            port: 80
          route:
          - destination:
              host: istio-egressgateway.istio-egress.svc.cluster.local
              subset: target-egress-gateway-mTLS
              port:
                number: 80
        - match:
          - gateways:
            - istio-egress/egress-gateway
            port: 80
          route:
          - destination:
              host: example.com
              port:
                number: 443
              subset: example-com-originate-TLS
            weight: 100
      EOF
      
    3. שליחת כמה בקשות אל example.com מאפליקציית הבדיקה במרחב השמות team-x:

      for i in {1..4}
      do
          kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
              -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
      done
      

      כמו קודם, הבקשות מצליחות עם תגובות 200 OK.

    4. בודקים את היומן של שער היציאה כדי לוודא שהשער ניתב את הבקשות למארח היעד על ידי יצירת חיבורי TLS:

      kubectl -n istio-egress logs -f $(kubectl -n istio-egress get pod -l istio=egressgateway \
          -o jsonpath="    {.items[0].metadata.name}") istio-proxy
      

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

      [2020-09-24T17:58:02.548Z] "HEAD / HTTP/2" 200 - "-" "-" 0 0 6 5 "10.1.1.15" "curl/7.67.0" "83a77acb-d994-424d-83da-dd8eac902dc8" "example.com" "93.184.216.34:443" outbound|443|example-com-originate-TLS|example.com 10.1.4.31:49866 10.1.4.31:8080 10.1.1.15:37334 outbound_.80_.target-egress-gateway-mTLS_.istio-egressgateway.istio-egress.svc.cluster.local -
      

      ה-sidecar של ה-proxy שלח את הבקשה לשער באמצעות יציאה 80, והתחלת TLS נוצרה ביציאה 443 כדי לשלוח את הבקשה למארח היעד.

    העברה של חיבורי HTTPS/TLS

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

    tls pass through

    1. משנים את ההגדרה כך שהשער ליציאה יתבסס על TLS pass-through לחיבורים ליציאה 443:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: Gateway
      metadata:
        name: egress-gateway
        namespace: istio-egress
      spec:
        selector:
          istio: egressgateway
        servers:
        - port:
            number: 80
            name: https
            protocol: HTTPS
          hosts:
            - '*'
          tls:
            mode: ISTIO_MUTUAL
        - port:
            number: 443
            name: tls
            protocol: TLS
          hosts:
          - '*'
          tls:
            mode: PASSTHROUGH
      EOF
      
    2. מעדכנים את DestinationRule שמפנה לשער היציאה כדי להוסיף קבוצת משנה שנייה ליציאה 443 בשער. קבוצת המשנה החדשה הזו לא משתמשת ב-TLS הדדי. אין תמיכה ב-TLS הדדי של Istio להעברה של חיבורי TLS. חיבורים ביציאה 80 עדיין משתמשים ב-mTLS:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1alpha3
      kind: DestinationRule
      metadata:
        name: target-egress-gateway
        namespace: istio-egress
      spec:
        host: istio-egressgateway.istio-egress.svc.cluster.local
        subsets:
        - name: target-egress-gateway-mTLS
          trafficPolicy:
            portLevelSettings:
            - port:
                number: 80
              tls:
                mode: ISTIO_MUTUAL
        - name: target-egress-gateway-TLS-passthrough
      EOF
      
    3. מעדכנים את השירות הווירטואלי עבור example.com כך שתנועת TLS ביציאה 443 תעבור דרך השער:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: example-com-through-egress-gateway
        namespace: istio-egress
      spec:
        hosts:
        - example.com
        gateways:
        - mesh
        - istio-egress/egress-gateway
        http:
        - match:
          - gateways:
            - mesh
            port: 80
          route:
          - destination:
              host: istio-egressgateway.istio-egress.svc.cluster.local
              subset: target-egress-gateway-mTLS
              port:
                number: 80
        - match:
          - gateways:
            - istio-egress/egress-gateway
            port: 80
          route:
          - destination:
              host: example.com
              port:
                number: 443
              subset: example-com-originate-TLS
            weight: 100
        tls:
        - match:
          - gateways:
            - mesh
            port: 443
            sniHosts:
            - example.com
          route:
          - destination:
              host: istio-egressgateway.istio-egress.svc.cluster.local
              subset: target-egress-gateway-TLS-passthrough
              port:
                number: 443
        - match:
          - gateways:
            - istio-egress/egress-gateway
            port: 443
            sniHosts:
            - example.com
          route:
          - destination:
              host: example.com
              port:
                number: 443
            weight: 100
        exportTo:
        - 'istio-egress'
        - 'team-x'
      EOF
      
    4. מעדכנים את השירות הווירטואלי עבור httpbin.org כך שתנועת TLS ביציאה 443 תעבור דרך השער:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: VirtualService
      metadata:
        name: httpbin-org-through-egress-gateway
        namespace: istio-egress
      spec:
        hosts:
        - httpbin.org
        gateways:
        - istio-egress/egress-gateway
        - mesh
        http:
        - match:
          - gateways:
            - mesh
            port: 80
          route:
          - destination:
              host: istio-egressgateway.istio-egress.svc.cluster.local
              subset: target-egress-gateway-mTLS
              port:
                number: 80
            weight: 100
        - match:
          - gateways:
            - istio-egress/egress-gateway
            port: 80
          route:
          - destination:
              host: httpbin.org
              port:
                number: 80
            weight: 100
        tls:
        - match:
          - gateways:
            - mesh
            port: 443
            sniHosts:
            - httpbin.org
          route:
          - destination:
              host: istio-egressgateway.istio-egress.svc.cluster.local
              subset: target-egress-gateway-TLS-passthrough
              port:
                number: 443
        - match:
          - gateways:
            - istio-egress/egress-gateway
            port: 443
            sniHosts:
            - httpbin.org
          route:
          - destination:
              host: httpbin.org
              port:
                number: 443
            weight: 100
        exportTo:
        - 'istio-egress'
        - 'team-x'
        - 'team-y'
      EOF
      
    5. מוסיפים מדיניות הרשאה שמקבלת כל סוג של תנועה שנשלחת ליציאה 443 של שירות שער היציאה. היציאה התואמת targetPort בתרמילי השער היא 8443.

      cat <<EOF | kubectl apply -f -
      apiVersion: security.istio.io/v1beta1
      kind: AuthorizationPolicy
      metadata:
        name: egress-all-443
        namespace: istio-egress
      spec:
        action: ALLOW
        rules:
          - when:
            - key: destination.port
              values: ["8443"]
      EOF
      
    6. מריצים את הפקודה istioctl analyze כדי לבדוק אם יש שגיאות בהגדרות:

      ${ISTIOCTL} analyze -n istio-egress --revision REVISION
      

      הפרטים שמוצגים הם:

      ✔ No validation issues found when analyzing namespace: istio-egress.
      
    7. שליחת בקשת HTTP פשוטה אל example.com מאפליקציית הבדיקה במרחב השמות team-x:

      kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
          -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
      

      הבקשה מצליחה עם תגובה 200 OK.

    8. עכשיו שולחים כמה בקשות TLS ‏ (HTTPS) מאפליקציית הבדיקה במרחב השמות team-x:

      for i in {1..4}
      do
          kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
              -o jsonpath={.items..metadata.name}) -c test -- curl -s -o /dev/null \
              -w "%{http_code}\n" \
              https://example.com
      done
      

      אתם רואים 200 תשובות.

    9. מעיינים שוב ביומן של שער היציאה:

      kubectl -n istio-egress logs -f $(kubectl -n istio-egress get pod -l istio=egressgateway \
          -o jsonpath="{.items[0].metadata.name}") istio-proxy
      

      יופיעו רשומות ביומן שדומות לרשומות הבאות:

      [2020-09-24T18:04:38.608Z] "- - -" 0 - "-" "-" 1363 5539 10 - "-" "-" "-" "-" "93.184.216.34:443" outbound|443||example.com 10.1.4.31:51098 10.1.4.31:8443 10.1.1.15:57030 example.com -
      

      בקשת ה-HTTPS טופלה כטראפיק TCP ועברה דרך השער למארח היעד, ולכן לא נכלל מידע HTTP ביומן.

    שימוש ב-Kubernetes NetworkPolicy כבקרה נוספת

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

    ההדרכה הזו מתייחסת רק לחיבורים יוצאים ולבוררים של תעבורת נתונים יוצאת (egress) במדיניות רשת. אם אתם שולטים בתעבורה הנכנסת באמצעות מדיניות רשת באשכולות שלכם, אתם צריכים ליצור מדיניות תעבורה נכנסת שתתאים למדיניות התעבורה היוצאת. לדוגמה, אם מאפשרים תעבורת נתונים יוצאת (egress) מעומסי עבודה במרחב השמות team-x אל מרחב השמות team-y, צריך גם לאפשר תעבורת נתונים נכנסת (ingress) למרחב השמות team-y ממרחב השמות team-x.

    1. לאפשר לעומסי עבודה ולשרתי proxy שנפרסו במרחב השמות team-x להתחבר אל istiod ואל שער היציאה:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: allow-egress-to-control-plane
        namespace: team-x
      spec:
        podSelector: {}
        policyTypes:
          - Egress
        egress:
        - to:
          - namespaceSelector:
              matchLabels:
                "kubernetes.io/metadata.name": istio-system
            podSelector:
              matchLabels:
                istio: istiod
          - namespaceSelector:
              matchLabels:
                "kubernetes.io/metadata.name": istio-egress
            podSelector:
              matchLabels:
                istio: egressgateway
      EOF
      
    2. מתן הרשאה לעומסי עבודה ולשרתי proxy לשאילתות DNS:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: allow-egress-to-dns
        namespace: team-x
      spec:
        podSelector: {}
        policyTypes:
          - Egress
        egress:
        - to:
          - namespaceSelector:
              matchLabels:
                "kubernetes.io/metadata.name": kube-system
          ports:
          - port: 53
            protocol: UDP
          - port: 53
            protocol: TCP
      EOF
      
    3. מתן אפשרות לעומסי עבודה ולשרתי proxy להתחבר לכתובות ה-IP שמשרתות את ממשקי ה-API והשירותים של Google, כולל רשות האישורים של Cloud Service Mesh:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: allow-egress-to-google-apis
        namespace: team-x
      spec:
        podSelector: {}
        policyTypes:
          - Egress
        egress:
        - to:
          - ipBlock:
              cidr: 199.36.153.4/30
          - ipBlock:
              cidr: 199.36.153.8/30
      EOF
      
    4. מתן אפשרות לעומסי עבודה ולשרתי proxy להתחבר לשרת המטא-נתונים של GKE:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: allow-egress-to-metadata-server
        namespace: team-x
      spec:
        podSelector: {}
        policyTypes:
          - Egress
        egress:
        - to: # For GKE data plane v2
          - ipBlock:
              cidr: 169.254.169.254/32
        - to: # For GKE data plane v1
          - ipBlock:
              cidr: 127.0.0.1/32 # Prior to 1.21.0-gke.1000
          - ipBlock:
              cidr: 169.254.169.252/32 # 1.21.0-gke.1000 and later
          ports:
          - protocol: TCP
            port: 987
          - protocol: TCP
            port: 988
      EOF
      
    5. אופציונלי: מאפשרים לעומסי עבודה ולשרתי proxy במרחב השמות team-x ליצור חיבורים ביניהם:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: allow-egress-to-same-namespace
        namespace: team-x
      spec:
        podSelector: {}
        ingress:
          - from:
            - podSelector: {}
        egress:
          - to:
            - podSelector: {}
      EOF
      
    6. אופציונלי: מאפשרים לעומסי עבודה ולשרתי proxy במרחב השמות team-x ליצור חיבורים לעומסי עבודה שנפרסו על ידי צוות אחר:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.k8s.io/v1
      kind: NetworkPolicy
      metadata:
        name: allow-egress-to-team-y
        namespace: team-x
      spec:
        podSelector: {}
        policyTypes:
          - Egress
        egress:
        - to:
          - namespaceSelector:
              matchLabels:
                "kubernetes.io/metadata.name": team-y
      EOF
      
    7. החיבורים בין שרתי proxy מסוג sidecar נשמרים. חיבורים קיימים לא נסגרים כשמחילים מדיניות רשת חדשה. מפעילים מחדש את עומסי העבודה במרחב השמות team-x כדי לוודא שהחיבורים הקיימים נסגרו:

      kubectl -n team-x rollout restart deployment
      
    8. מוודאים שאפשר עדיין לשלוח בקשת HTTP אל example.com מאפליקציית הבדיקה במרחב השמות team-x:

      kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
          -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
      

      הבקשה מצליחה עם תגובה 200 OK.

    גישה ישירה ל-Google API באמצעות גישה פרטית ל-Google והרשאות IAM

    ממשקי ה-API והשירותים של Google נחשפים באמצעות כתובות IP חיצוניות. כשפודים עם כתובות IP של כינוי מקורי ב-VPC יוצרים חיבורים לממשקי Google API באמצעות גישה פרטית ל-Google, התנועה אף פעם לא יוצאת מהרשת של Google.

    כשמגדירים את התשתית במדריך הזה, מפעילים את הגישה הפרטית ל-Google עבור רשת המשנה שמשמשת את ה-Pods של GKE. כדי לאפשר גישה לכתובות ה-IP שמשמשות את הגישה הפרטית ל-Google, יצרתם נתיב, כלל חומת אש ב-VPC ותחום DNS פרטי. ההגדרה הזו מאפשרת ל-pods להגיע ישירות לממשקי ה-API של Google בלי לשלוח תנועה דרך שער היציאה. אתם יכולים לשלוט בממשקי ה-API שזמינים לחשבונות שירות ספציפיים של Kubernetes (ומכאן גם למרחבי שמות) באמצעות איחוד שירותי אימות הזהות של עומסי עבודה ב-GKE ו-IAM. הרשאות ב-Istio לא נכנסות לתוקף כי שער היציאה לא מטפל בחיבורים ל-Google APIs.

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

    1. יוצרים חשבון שירות של Google לשימוש באפליקציה:

      gcloud iam service-accounts create sa-test-app-team-x
      
    2. מתן אפשרות לחשבון השירות של Kubernetes להתחזות לחשבון השירות של Google:

      gcloud iam service-accounts add-iam-policy-binding \
        --role roles/iam.workloadIdentityUser \
        --member "serviceAccount:${PROJECT_ID}.svc.id.goog[team-x/test]" \
        sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com
      
    3. מוסיפים הערה לחשבון השירות של Kubernetes עבור אפליקציית הבדיקה במרחב השמות team-x עם כתובת האימייל של חשבון השירות של Google:

      cat <<EOF | kubectl apply -f -
      apiVersion: v1
      kind: ServiceAccount
      metadata:
        annotations:
          iam.gke.io/gcp-service-account: sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com
        name: test
        namespace: team-x
      EOF
      
    4. לפוד של אפליקציית הבדיקה צריכה להיות גישה לשרת המטא-נתונים של Google (שפועל כ-DaemonSet) כדי לקבל פרטי כניסה זמניים להתקשרות עם Google APIs. יוצרים רשומה של שירות לשרת המטא-נתונים של GKE:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: ServiceEntry
      metadata:
        name: metadata-google-internal
        namespace: istio-egress
        labels:
          # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
          service.istio.io/canonical-name: metadata.google.internal
      spec:
        hosts:
        - metadata.google.internal
        ports:
        - number: 80
          name: http
          protocol: HTTP
        - number: 443
          name: tls
          protocol: TLS
        resolution: DNS
        location: MESH_EXTERNAL
        exportTo:
        - 'istio-egress'
        - 'team-x'
      EOF
      
    5. צריך גם ליצור רשומה של שירות עבור private.googleapis.com ו-storage.googleapis.com:

      cat <<EOF | kubectl apply -f -
      apiVersion: networking.istio.io/v1beta1
      kind: ServiceEntry
      metadata:
        name: private-googleapis-com
        namespace: istio-egress
        labels:
          # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
          service.istio.io/canonical-name: googleapis.com
      spec:
        hosts:
        - private.googleapis.com
        - storage.googleapis.com
        ports:
        - number: 80
          name: http
          protocol: HTTP
        - number: 443
          name: tls
          protocol: TLS
        resolution: DNS
        location: MESH_EXTERNAL
        exportTo:
        - 'istio-egress'
        - 'team-x'
      EOF
      
    6. מוודאים שחשבון השירות של Kubernetes מוגדר בצורה נכונה לפעול כחשבון השירות של Google:

      kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
          -o jsonpath={.items..metadata.name}) -c test -- gcloud auth list
      

      חשבון השירות של Google מופיע כזהות הפעילה והיחידה.

    7. יוצרים קובץ בדיקה בקטגוריה של Cloud Storage:

      echo "Hello, World!" > /tmp/hello
      gcloud storage buckets create gs://${PROJECT_ID}-bucket
      gcloud storage cp /tmp/hello gs://${PROJECT_ID}-bucket/
      
    8. מעניקים לחשבון השירות הרשאה להציג קבצים בדלי ולראות אותם:

      gcloud storage buckets add-iam-policy-binding gs://${PROJECT_ID}-bucket/ \
          --member=serviceAccount:sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com \
          --role=roles/storage.objectViewer
      
    9. מוודאים שלאפליקציית הבדיקה יש גישה לקטגוריית הבדיקה:

      kubectl -n team-x exec -it \
      $(kubectl -n team-x get pod -l app=test -o jsonpath={.items..metadata.name}) \
      -c test \
      -- gcloud storage cat gs://${PROJECT_ID}-bucket/hello
      

      הפרטים שמוצגים הם:

      Hello, World!
      

    הסרת המשאבים

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

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

    מחיקת הפרויקט

    הדרך הקלה ביותר לבטל את החיוב היא למחוק את הפרויקט שיצרתם בשביל המדריך.

    1. In the Google Cloud console, go to the Manage resources page.

      Go to Manage resources

    2. In the project list, select the project that you want to delete, and then click Delete.
    3. In the dialog, type the project ID, and then click Shut down to delete the project.

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