Utiliser les passerelles de sortie de Cloud Service Mesh sur des clusters GKE: tutoriel


Ce tutoriel explique comment utiliser les passerelles de sortie Cloud Service Mesh et d'autres contrôles Google Cloud pour sécuriser le trafic sortant (sortie) à partir de charges de travail déployées sur un cluster Google Kubernetes Engine. Ce tutoriel est destiné à accompagner les bonnes pratiques d'utilisation des passerelles de sortie de Cloud Service Mesh sur les clusters GKE.

L'audience visée pour ce tutoriel comprend les ingénieurs réseau, de plate-forme et de sécurité qui gèrent les clusters Google Kubernetes Engine utilisés par une ou plusieurs équipes de livraison de logiciels. Les contrôles décrits ici peuvent être particulièrement utiles pour les organisations qui doivent démontrer leur conformité avec les réglementations en vigueur (par exemple, le RGPD et le PCI).

Objectifs

  • Configurez l'infrastructure pour exécuter Cloud Service Mesh :
  • Installer Cloud Service Mesh.
  • Installez des proxys de passerelle de sortie s'exécutant sur un pool de nœuds dédié.
  • Configurez des règles de routage mutualisées pour le trafic externe via la passerelle de sortie :
    • Les applications dans l'espace de noms team-x peuvent se connecter à example.com
    • Les applications dans l'espace de noms team-y peuvent se connecter à httpbin.org
  • Utilisez la ressource Sidecar pour limiter le champ d'application de la configuration de sortie du proxy sidecar pour chaque espace de noms.
  • Configurez des règles d'autorisation pour appliquer les règles de sortie.
  • Configurez la passerelle de sortie pour mettre à niveau les requêtes HTTP simples vers TLS (initiation TLS).
  • Configurez la passerelle de sortie pour qu'elle transfère le trafic TLS.
  • Définissez des règles de réseau Kubernetes comme contrôle supplémentaire de sortie.
  • Configurez l'accès direct aux API Google à l'aide de l'accès privé à Google et des autorisations IAM (Identity and Access Management).

Coûts

Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Une fois que vous aurez terminé ce tutoriel, évitez de payer des frais en supprimant les ressources que vous avez créées. Consultez la section Effectuer un nettoyage pour en savoir plus.

Avant de commencer

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

    Go to project selector

  2. Make sure that billing is enabled for your Google Cloud project.

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

    Activate Cloud Shell

  4. Créez un répertoire de travail à utiliser pour suivre le tutoriel :

    mkdir -p ~/WORKING_DIRECTORY
    cd ~/WORKING_DIRECTORY
    
  5. Créez un script d'interface système pour initialiser votre environnement pour ce tutoriel. Remplacez et modifiez les variables en fonction de votre projet et de vos préférences. Exécutez ce script avec la commande source pour réinitialiser votre environnement si votre session d'interface système expire :

    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. Activer compute.googleapis.com :

    gcloud services enable compute.googleapis.com --project=YOUR_PROJECT_ID
    
  7. Rendez le script exécutable et exécutez-le à l'aide de la commande source pour initialiser votre environnement. Sélectionnez Y si vous êtes invité à activer compute.googleapis.com:

    chmod +x ./init-egress-tutorial.sh
    source ./init-egress-tutorial.sh
    

Configurer l'infrastructure

Créer un réseau et un sous-réseau VPC

  1. Créez un nouveau réseau VPC :

    gcloud compute networks create vpc-network \
        --subnet-mode custom
    
  2. Créez un sous-réseau dans lequel le cluster s'exécutera avec des plages d'adresses IP secondaires pré-attribuées pour les pods et les services. L'accès privé à Google est activé, ce qui permet aux applications qui n'ont que des adresses IP internes d'accéder aux API et services 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
    

Configurer Cloud NAT

Cloud NAT permet aux charges de travail sans adresse IP externe de se connecter à des destinations sur Internet et de recevoir des réponses entrantes de ces destinations.

  1. Créez un routeur Cloud Router :

    gcloud compute routers create nat-router \
        --network vpc-network
    
  2. Ajoutez une configuration NAT au routeur :

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

Créer des comptes de service pour chaque pool de nœuds GKE

Créez deux comptes de service à utiliser avec les deux pools de nœuds GKE. Un compte de service distinct est attribué à chaque pool de nœuds afin de pouvoir appliquer des règles de pare-feu VPC à des nœuds spécifiques.

  1. Créez un compte de service à utiliser par les nœuds du pool de nœuds par défaut :

    gcloud iam service-accounts create sa-application-nodes \
        --description="SA for application nodes" \
        --display-name="sa-application-nodes"
    
  2. Créez un compte de service à utiliser par les nœuds du pool de nœuds de passerelle :

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

Accorder des autorisations aux comptes de service

Ajoutez un ensemble minimal de rôles IAM aux comptes de service de l'application et de la passerelle. Ces rôles sont requis pour la journalisation, la surveillance et l'extraction d'images de conteneurs privées à partir de 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

Créer les règles de pare-feu

Dans les étapes suivantes, vous allez appliquer une règle de pare-feu au réseau VPC afin que, par défaut, tout le trafic sortant soit refusé. Une connectivité spécifique est requise pour que le cluster fonctionne et que les nœuds de passerelle puissent atteindre des destinations en dehors du VPC. Un ensemble minimal de règles de pare-feu spécifiques remplace la règle de refus par défaut afin d'autoriser la connectivité nécessaire.

  1. Créez une règle de pare-feu par défaut (faible priorité) afin de refuser toutes les sorties du réseau 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. Créez une règle pour n'autoriser l'accès à Internet que pour les nœuds associés au compte de service de passerelle :

    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. Autorisez les nœuds à accéder au plan de contrôle de 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. Facultatif: Cette règle de pare-feu n'est pas nécessaire si vous utilisez Cloud Service Mesh géré.

    Cloud Service Mesh utilise des webhooks lors de l'injection de proxys sidecar dans les charges de travail. Autorisez le serveur d'API GKE à appeler les webhooks exposés par le plan de contrôle du maillage de services s'exécutant sur les nœuds :

    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. Autorisez la connectivité de sortie entre les nœuds et les pods exécutés sur le cluster. GKE crée automatiquement une règle d'entrée correspondante. Aucune règle n'est requise pour la connectivité du service, car la chaîne de routage iptables convertit toujours les adresses IP de service en adresses IP de 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. Autorisez l'accès aux ensembles réservés d'adresses IP utilisés par l'accès privé à Google pour diffuser les API Google, Container Registry et d'autres services :

    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. Autorisez le service du vérificateur d'état Google Cloud à accéder aux pods qui s'exécutent dans le cluster. Pour en savoir plus, consultez la section Vérifications d'état.

    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"
    

Configurer un accès privé aux API Google Cloud

L'accès privé à Google permet aux VM et aux pods qui n'ont que des adresses IP internes d'accéder aux API et aux services de Google. Bien que les API et services Google soient diffusés à partir d'adresses IP externes, le trafic en provenance des nœuds ne quitte jamais le réseau Google lorsque vous utilisez l'accès privé à Google.

Activez l'API Cloud DNS :

gcloud services enable dns.googleapis.com

Créez une zone DNS privée, des enregistrements CNAME et A afin que les nœuds et les charges de travail puissent se connecter aux API et services Google à l'aide de l'accès privé à Google et du nom d'hôte 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

Configurer un accès privé à Container Registry

Créez une zone DNS privée, un enregistrement CNAME et un enregistrement A afin que les nœuds puissent se connecter à Container Registry à l'aide de l'accès privé à Google et du nom d'hôte 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

Créer un cluster GKE privé

  1. Recherchez l'adresse IP externe de Cloud Shell afin de pouvoir l'ajouter à la liste des réseaux autorisés à accéder au serveur d'API de votre cluster :

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

    Après une période d'inactivité, l'adresse IP externe de votre VM Cloud Shell peut changer. Dans ce cas, vous devez mettre à jour la liste des réseaux autorisés de votre cluster. Ajoutez la commande suivante à votre script d'initialisation :

    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. Activez l'API Google Kubernetes Engine :

    gcloud services enable container.googleapis.com
    
  3. Créez un cluster GKE privé :

    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}
    

    La création du cluster prend quelques minutes. Le cluster inclut des nœuds privés avec des adresses IP internes. Les pods et les services se voient attribuer des adresses IP des plages secondaires nommées que vous avez définies lors de la création du sous-réseau VPC.

    Cloud Service Mesh avec un plan de contrôle dans le cluster nécessite que les nœuds du cluster utilisent un type de machine comportant au moins quatre processeurs virtuels.

    Google recommande que le cluster soit abonné à la version disponible "standard" pour garantir que les nœuds exécutent une version de Kubernetes compatible avec Cloud Service Mesh.

    Pour en savoir plus sur les prérequis pour exécuter Cloud Service Mesh avec un plan de contrôle au sein du cluster, consultez les prérequis au sein du cluster.

    Pour en savoir plus sur les exigences et les limites d'exécution de Cloud Service Mesh géré, consultez les fonctionnalités compatibles avec Cloud Service Mesh géré.

    La fédération d'identité de charge de travail pour GKE est activée sur le cluster. Cloud Service Mesh nécessite la fédération d'identité de charge de travail pour GKE. Il s'agit de la méthode recommandée pour accéder aux API Google à partir des charges de travail GKE.

  4. Créez un pool de nœuds appelé passerelle. C'est dans ce pool de nœuds que sera déployé la passerelle de sortie. Le rejet dedicated=gateway:NoSchedule est ajouté à chaque nœud du pool de nœuds de passerelle.

    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"
    

    Les rejets et tolérances Kubernetes vous permettent de vous assurer que seuls les pods de passerelle de sortie s'exécutent sur des nœuds du pool de nœuds de passerelle.

  5. Téléchargez les identifiants afin de pouvoir vous connecter au cluster avec kubectl :

    gcloud container clusters get-credentials cluster1
    
  6. Vérifiez que les nœuds de la passerelle disposent du rejet approprié :

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

    Le résultat ressemble à ce qui suit :

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

Installer et configurer Cloud Service Mesh

Suivez l'un des guides d'installation de Cloud Service Mesh:

Une fois Cloud Service Mesh installé, arrêtez-vous et revenez à ce tutoriel sans installer de passerelles d'entrée ou de sortie.

Installer une passerelle de sortie

  1. Créez un espace de noms Kubernetes pour la passerelle de sortie:

    kubectl create namespace istio-egress
    
  2. Activez l'espace de noms pour l'injection : La procédure dépend de votre implémentation du plan de contrôle.

    Géré (TD)

    Appliquez le libellé d'injection par défaut à l'espace de noms:

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

    Géré (Istiod)

    Recommandation:Exécutez la commande suivante pour appliquer le libellé d'injection par défaut à l'espace de noms:

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

    Si vous êtes déjà un utilisateur du plan de contrôle Istio géré:nous vous recommandons d'utiliser l'injection par défaut, mais l'injection basée sur les révisions est prise en charge. Suivez les instructions suivantes:

    1. Exécutez la commande suivante pour localiser les versions disponibles:

      kubectl -n istio-system get controlplanerevision
      

      Le résultat ressemble à ce qui suit :

      NAME                AGE
      asm-managed-rapid   6d7h
      

      Dans le résultat, la valeur de la colonne NAME est le libellé de révision qui correspond à la version disponible pour la version de Cloud Service Mesh.

    2. Appliquez le libellé de révision à l'espace de noms.

      kubectl label namespace istio-egress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      

    Dans le cluster

    Recommandation:Exécutez la commande suivante pour appliquer le libellé d'injection par défaut à l'espace de noms:

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

    Nous vous recommandons d'utiliser l'injection par défaut, mais l'injection basée sur les révisions est prise en charge: Suivez les instructions suivantes:

    1. Exécutez la commande suivante pour localiser le libellé de révision sur istiod :

      kubectl get deploy -n istio-system -l app=istiod -o \
         jsonpath={.items[*].metadata.labels.'istio\.io\/rev'}'{"\n"}'
      
    2. Appliquez le libellé de révision à l'espace de noms. Dans la commande suivante, REVISION_LABEL correspond à la valeur du libellé de révision istiod que vous avez notée à l'étape précédente.

      kubectl label namespace istio-egress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      
  3. Créez un fichier manifeste d'opérateur pour la passerelle de sortie:

    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
    
  4. Téléchargez l'outil istioctl. Vous devez utiliser la version 1.16.2-asm.2 ou ultérieure, même si vous utilisez la version 1.15 ou antérieure de Cloud Service Mesh. Consultez la section Télécharger la bonne version d'istioctl.

  5. Après avoir extrait l'archive téléchargée, définissez une variable d'environnement destinée à contenir le chemin d'accès à l'outil istioctl et ajoutez-la à votre script d'initialisation:

    ISTIOCTL=$(find "$(pwd -P)" -name istioctl)
    echo "ISTIOCTL=\"${ISTIOCTL}\"" >> ./init-egress-tutorial.sh
    
  6. Créez le fichier manifeste d'installation de la passerelle de sortie à l'aide du fichier manifeste de l'opérateur et de istioctl:

    ${ISTIOCTL} manifest generate \
        --filename egressgateway-operator.yaml \
        --output egressgateway \
        --cluster-specific
    
  7. Installez la passerelle de sortie:

    kubectl apply --recursive --filename egressgateway/
    
  8. Vérifiez que la passerelle de sortie s'exécute sur les nœuds du pool de nœuds gateway:

    kubectl get pods -n istio-egress -o wide
    
  9. Les pods de passerelle de sortie ont affinity pour les nœuds du pool de nœuds gateway et une tolérance qui leur permet de s'exécuter sur les nœuds de passerelle rejetés. Examinez l'affinité et les tolérances de nœud pour les pods de passerelle de sortie:

    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")]'
    

    Le résultat ressemble à ce qui suit :

    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]
    

Activer la journalisation des accès Envoy

La procédure à suivre pour activer les journaux d'accès Envoy dépend de votre type de Cloud Service Mesh, géré ou intégré au cluster:

Préparer le maillage et une application de test

  1. Assurez-vous que le protocole TLS mutuel STRICT est activé. Appliquez une règle PeerAuthentication par défaut pour le maillage dans l'espace de noms 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
    

    Vous pouvez remplacer cette configuration en créant des ressources PeerAuthentication dans des espaces de noms spécifiques.

  2. Créez des espaces de noms à utiliser pour déployer les charges de travail de test. Les étapes suivantes de ce tutoriel expliquent comment configurer différentes règles de routage de sortie pour chaque espace de noms.

    kubectl create namespace team-x
    kubectl create namespace team-y
    
  3. Attribuez un libellé aux espaces de noms afin de pouvoir les sélectionner en utilisant les règles de réseau Kubernetes :

    kubectl label namespace team-x team=x
    kubectl label namespace team-y team=y
    
  4. Pour que Cloud Service Mesh puisse injecter automatiquement les sidecars de proxy, vous devez définir le libellé de révision du plan de contrôle sur les espaces de noms de la charge de travail:

    kubectl label ns team-x istio.io/rev- istio-injection=enabled --overwrite
    kubectl label ns team-y istio.io/rev- istio-injection=enabled --overwrite
    
  5. Créez un fichier YAML à utiliser pour effectuer des déploiements de test :

    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. Déployez l'application de test dans l'espace de noms team-x :

    kubectl -n team-x create -f ./test.yaml
    
  7. Vérifiez que l'application de test est déployée sur un nœud du pool par défaut et qu'un conteneur sidecar proxy est injecté. Répétez la commande suivante jusqu'à ce que l'état du pod indique Running :

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

    Le résultat ressemble à ce qui suit :

    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
    

    Deux conteneurs sur deux sont Running. L'un est l'application de test, tandis que l'autre est le sidecar de proxy.

    Le pod s'exécute sur un nœud du pool de nœuds par défaut.

  8. Vérifiez qu'il n'est pas possible d'effectuer une requête HTTP à partir du conteneur de test vers un site externe :

    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
    

    Un message d'erreur de proxy side-car est généré car la règle de pare-feu global-deny-egress-all refuse la connexion en amont.

Utiliser la ressource sidecar pour limiter la portée de la configuration du proxy side-car

Vous pouvez utiliser la ressource sidecar pour limiter le champ d'application de l'écouteur de sortie configuré pour les proxys sidecar. Pour réduire le volume de la configuration et l'utilisation de mémoire, il est recommandé d'appliquer une ressource Sidecar par défaut à chaque espace de noms.

Le proxy exécuté par Cloud Service Mesh dans le side-car est Envoy. Dans la terminologie Envoy, un cluster est un groupe similaire de points de terminaison en amont utilisés comme destinations pour l'équilibrage de charge.

  1. Inspectez les clusters sortants configurés dans le proxy sidecar Envoy pour le pod de test en exécutant la commande istioctl proxy-config :

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

    La liste contient environ 11 clusters Envoy, dont certains pour la passerelle de sortie.

  2. Limitez la configuration du proxy aux routes de sortie définies explicitement avec des entrées de service dans les espaces de noms "egress" et "team-x". Appliquez une ressource Sidecar à l'espace de noms 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
    

    Définir le mode de règle de trafic sortant sur REGISTRY_ONLY limite la configuration du proxy pour n'inclure que les hôtes externes qui ont été explicitement ajoutés au registre de services du maillage en définissant des entrées de service.

    Le paramètre egress.hosts indique que le proxy side-car ne sélectionne que les routes de l'espace de noms de sortie qui sont mises à disposition à l'aide de l'attribut exportTo. La partie "team-x/*" inclut toutes les routes qui ont été configurées localement dans l'espace de noms team-x.

  3. Affichez les clusters sortants configurés dans le proxy sidecar Envoy et comparez-les à la liste des clusters configurés avant l'application de la ressource Sidecar :

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

    Vous voyez des clusters pour la passerelle de sortie et un cluster pour le pod de test.

Configurer Cloud Service Mesh pour acheminer le trafic via la passerelle de sortie

  1. Configurez une ressource Gateway pour le trafic HTTP sur le port 80. Gateway sélectionne le proxy de la passerelle de sortie que vous avez déployé dans l'espace de noms de sortie. La configuration Gateway est appliquée à l'espace de noms de sortie et gère le trafic pour tous les hôtes.

    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. Créez un cluster DestinationRule pour la passerelle de sortie avec TLS mutuel pour l'authentification et le chiffrement. Utilisez une seule règle de destination partagée pour tous les hôtes externes.

    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. Créez un ServiceEntry dans l'espace de noms de sortie pour enregistrer explicitement example.com dans le registre de services du maillage pour l'espace de noms 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. Créez une ressource VirtualService pour acheminer le trafic vers example.com via la passerelle de sortie. Il y a deux conditions de correspondance : la première condition dirige le trafic vers la passerelle de sortie et la seconde dirige le trafic de la passerelle de sortie vers l'hôte de destination. La propriété exportTo contrôle les espaces de noms qui peuvent utiliser le service virtuel.

    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. Exécutez istioctl analyze pour rechercher les erreurs de configuration :

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

    Le résultat ressemble à ce qui suit :

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. Envoyez plusieurs requêtes via la passerelle de sortie vers le site externe :

    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
    

    Des codes d'état 200 s'affichent pour les quatre réponses.

  7. Vérifiez que les requêtes ont été dirigées vers la passerelle de sortie en consultant les journaux d'accès proxy. Commencez par vérifier le journal d'accès du proxy sidecar déployé avec l'application de test :

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

    Pour chaque requête envoyée, une entrée de journal semblable à la suivante s'affiche :

    [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. Consultez également le journal d'accès de la passerelle de sortie :

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

    Pour chaque requête envoyée, une entrée de journal d'accès de passerelle de sortie semblable à celle-ci s'affiche :

    [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 -
    

Configurer un routage différent pour un second espace de noms

Configurez le routage pour un deuxième hôte externe afin de découvrir comment configurer différentes connexions externes pour différentes équipes.

  1. Créez une ressource Sidecar pour l'espace de noms 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. Déployez l'application de test dans l'espace de noms team-y :

    kubectl -n team-y create -f ./test.yaml
    
  3. Enregistrez un deuxième hôte externe et exportez-le vers les espaces de noms team-x et 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. Créez un service virtuel pour acheminer le trafic vers httpbin.org via la passerelle de sortie :

    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. Exécutez istioctl analyze pour rechercher les erreurs de configuration :

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

    Voici les informations disponibles :

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. Envoyez une requête à httpbin.org à partir de l'application de test 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
    

    Une réponse 200 OK s'affiche.

  7. Envoyez également une requête à httpbin.org à partir de l'application de test 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
    

    Une réponse 200 OK s'affiche.

  8. Essayez d'envoyer une requête à example.com à partir de l'espace de noms 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
    

    La requête échoue, car aucune route sortante n'est configurée pour l'hôte example.com.

Utiliser la règle d'autorisation pour mieux contrôler le trafic

Dans ce tutoriel, les règles d'autorisation pour la passerelle de sortie sont créées dans l'espace de noms istio-egress. Vous pouvez configurer Kubernetes RBAC pour que seuls les administrateurs réseau puissent accéder à l'espace de noms istio-egress.

  1. Créez une AuthorizationPolicy pour que les applications de l'espace de noms team-x puissent se connecter à example.com mais pas à d'autres hôtes externes lors de l'envoi de requêtes à l'aide du port 80. La valeur targetPort correspondante sur les pods de la passerelle de sortie est 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. Vérifiez que vous pouvez envoyer une requête à example.com à partir de l'application de test dans l'espace de noms 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
    

    Une réponse 200 OK s'affiche.

  3. Essayez d'envoyer une requête à httpbin.org à partir de l'application de test dans l'espace de noms 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
    

    La requête échoue avec le message RBAC: access denied et le code d'état 403 Forbidden. Vous devrez peut-être attendre quelques secondes car on observe souvent un délai avant que la règle d'autorisation n'entre en vigueur.

  4. Les stratégies d'autorisation permettent de contrôler efficacement le trafic autorisé ou refusé. Appliquez la règle d'autorisation suivante pour permettre à l'application de test de l'espace de noms team-y d'envoyer des requêtes à httpbin.org en utilisant un chemin d'URL particulier lors de l'envoi de requêtes à l'aide du port 80. La valeur targetPort correspondante sur les pods de la passerelle de sortie est 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. Essayez de vous connecter à httpbin.org à partir de l'application de test dans l'espace de noms 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
    

    La requête échoue avec le RBAC : accès refusé et message d'état 403 Forbidden.

  6. Envoyez une requête à httpbin.org/status/418 à partir de la même application :

    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
    

    La requête aboutit, car le chemin correspond à celui qui figure dans la règle d'autorisation. Le résultat ressemble à ce qui suit :

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

Initiation TLS au niveau de la passerelle de sortie

Vous pouvez configurer les passerelles de sortie pour upgrade (initier) les requêtes HTTP vers le protocole TLS ou TLS mutuel. Autoriser les applications à effectuer des requêtes HTTP simples présente plusieurs avantages lorsque ces requêtes sont utilisées avec le TLS mutuel et l'initiation TLS d'Istio. Pour en savoir plus, consultez le guide des bonnes pratiques.

Initiation TLS au niveau de la passerelle de sortie

  1. Créez un fichier DestinationRule. The DestinationRule qui indique que la passerelle crée une connexion 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. Mettez à jour le service virtuel pour example.com afin que les requêtes envoyées au port 80 de la passerelle soient upgraded vers TLS sur le port 443 lorsqu'elles sont envoyées à l'hôte de destination:

    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. Envoyez plusieurs requêtes à example.com à partir de l'application de test dans l'espace de noms 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
    

    Comme auparavant, les requêtes aboutissent avec les réponses 200 OK.

  4. Consultez le journal de passerelle de sortie pour vérifier que la passerelle a acheminé les requêtes vers l'hôte de destination en initiant des connexions TLS :

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

    Le résultat ressemble à ce qui suit :

    [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 -
    

    Le sidecar de proxy a envoyé la requête à la passerelle via le port 80 et initié une connexion TLS sur le port 443 pour envoyer la requête vers l'hôte de destination.

Transmission des connexions HTTPS/TLS

Il se peut que vos applications existantes utilisent déjà des connexions TLS lors de la communication avec des services externes. Vous pouvez configurer la passerelle de sortie pour qu'elle transmette les connexions TLS sans les déchiffrer.

transmission TLS

  1. Modifiez votre configuration de sorte à ce que la passerelle de sortie utilise une stratégie TLS pour les connexions au port 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. Mettez à jour le port DestinationRule pointant vers la passerelle de sortie afin d'ajouter un second sous-ensemble pour le port 443 sur la passerelle. Ce nouveau sous-ensemble n'utilise pas le protocole TLS mutuel. Le protocole TLS mutuel d'Istio n'est pas compatible avec la transmission des connexions TLS. Les connexions sur le port 80 utilisent toujours 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. Mettez à jour le service virtuel pour example.com afin que le trafic TLS sur le port 443 soit transmis via la passerelle :

    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. Mettez à jour le service virtuel pour httpbin.org de manière à ce que le trafic TLS sur le port 443 soit transmis via la passerelle :

    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. Ajoutez une règle d'autorisation qui accepte tous les types de trafic envoyés au port 443 du service de passerelle de sortie. La valeur targetPort correspondante sur les pods de la passerelle est 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. Exécutez istioctl analyze pour rechercher les erreurs de configuration :

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

    Voici les informations disponibles :

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  7. Envoyez une requête HTTP simple à example.com à partir de l'application de test dans l'espace de noms 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
    

    La requête aboutit avec une réponse 200 OK.

  8. Envoyez maintenant plusieurs requêtes TLS (HTTPS) à partir de l'application de test dans l'espace de noms 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
    

    Vous pouvez voir des réponses 200.

  9. Consultez à nouveau le journal de passerelle de sortie :

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

    Vous pouvez voir des entrées de journal semblables à ce qui suit :

    [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 -
    

    La requête HTTPS a été traitée en tant que trafic TCP et transmise via la passerelle à l'hôte de destination. Par conséquent, aucune information HTTP n'est incluse dans le journal.

Utiliser Kubernetes NetworkPolicy comme mécanisme de contrôle supplémentaire

Il existe de nombreux scénarios dans lesquels une application peut contourner un proxy sidecar. Vous pouvez utiliser Kubernetes NetworkPolicy pour spécifier en outre les connexions que les charges de travail sont autorisées à créer. Une fois qu'une règle de réseau est appliquée, toutes les connexions qui ne sont pas spécifiquement autorisées sont refusées.

Ce tutoriel ne prend en compte que les connexions de sortie et les sélecteurs de sortie pour les règles de réseau. Si vous contrôlez l'entrée avec des règles de réseau sur vos propres clusters, vous devez créer des règles d'entrée correspondant à vos règles de sortie. Par exemple, si vous autorisez la sortie à partir de charges de travail dans l'espace de noms team-x vers l'espace de noms team-y, vous devez également autoriser l'entrée de l'espace de noms team-y à partir de l'espace de noms team-x.

  1. Autorisez les charges de travail et les proxys déployés dans l'espace de noms team-x à se connecter à istiod et à la passerelle de sortie :

    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. Autorisez les charges de travail et les proxys à interroger le 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. Autorisez les charges de travail et les proxys à se connecter aux adresses IP des API et des services Google, y compris l'autorité de certification 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. Autorisez les charges de travail et les proxys à se connecter au serveur de métadonnées 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. Facultatif : autorisez les charges de travail et les proxys dans l'espace de noms team-x à établir des connexions entre eux :

    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. Facultatif : autorisez les charges de travail et les proxys dans l'espace de noms team-x à établir des connexions avec les charges de travail déployées par une autre équipe :

    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. Les connexions entre les proxys side-car persistent. Les connexions existantes ne sont pas fermées lorsque vous appliquez une nouvelle règle de réseau. Redémarrez les charges de travail dans l'espace de noms "team-x" pour vous assurer que les connexions existantes sont fermées :

    kubectl -n team-x rollout restart deployment
    
  8. Vérifiez que vous pouvez toujours envoyer une requête HTTP à example.com à partir de l'application de test dans l'espace de noms 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
    

    La requête aboutit avec une réponse 200 OK.

Accéder directement aux API Google à l'aide de l'accès privé à Google et des autorisations IAM

Les API et services de Google sont exposés à l'aide d'adresses IP externes. Lorsque des pods avec des adresses IP d'alias de VPC natif établissent des connexions aux API Google à l'aide de l'accès privé à Google, le trafic ne quitte jamais le réseau de Google.

Lorsque vous avez configuré l'infrastructure de ce tutoriel, vous avez activé l'accès privé à Google pour le sous-réseau utilisé par les pods GKE. Pour autoriser l'accès aux adresses IP utilisées par l'accès privé à Google, vous avez créé une route, une règle de pare-feu VPC et une zone DNS privée. Cette configuration permet aux pods d'atteindre directement les API Google sans envoyer de trafic via la passerelle de sortie. Vous pouvez contrôler les API disponibles pour des comptes de service Kubernetes spécifiques (et donc des espaces de noms) à l'aide de la fédération d'identité de charge de travail pour GKE et d'IAM. L'autorisation Istio ne prend pas effet car la passerelle de sortie ne gère pas les connexions aux API Google.

Pour que les pods puissent appeler les API Google, vous devez utiliser IAM pour accorder des autorisations. Le cluster que vous utilisez dans ce tutoriel est configuré pour utiliser Workload Identity Federation for GKE, ce qui permet à un compte de service Kubernetes d'agir en tant que compte de service Google.

  1. Créez un compte de service Google que votre application pourra utiliser :

    gcloud iam service-accounts create sa-test-app-team-x
    
  2. Autorisez le compte de service Kubernetes à emprunter l'identité du compte de service 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. Annotez le compte de service Kubernetes de l'application de test dans l'espace de noms team-x avec l'adresse e-mail du compte de service 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. Le pod de l'application de test doit pouvoir accéder au serveur de métadonnées Google (s'exécutant en tant que DaemonSet) afin d'obtenir des identifiants temporaires pour appeler les API Google. Créez une entrée de service pour le serveur de métadonnées 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. Créez également une entrée de service pour private.googleapis.com et 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. Vérifiez que le compte de service Kubernetes est correctement configuré pour agir en tant que compte de service 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
    

    Le compte de service Google est répertorié comme étant l'identité active unique.

  7. Créez un fichier de test dans un bucket 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. Autorisez le compte de service à répertorier et à afficher les fichiers du bucket :

    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. Vérifiez que l'application de test peut accéder au bucket de test :

    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
    

    Voici les informations disponibles :

    Hello, World!
    

Nettoyer

Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et les ressources individuelles.

Pour éviter que les ressources utilisées lors de ce tutoriel ne soient facturées sur votre compte Google Cloud, procédez comme suit :

Supprimer le projet

Le moyen le plus simple d'empêcher la facturation est de supprimer le projet que vous avez créé pour ce tutoriel.

  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.

Étape suivante