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


Ce tutoriel explique comment utiliser les passerelles de sortie Anthos Service Mesh ainsi que 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 accompagne les bonnes pratiques pour l'utilisation des passerelles de sortie Anthos 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 Anthos Service Mesh :
  • Installer Anthos Service Mesh
  • Installer 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 de l'espace de noms team-x peuvent se connecter à example.com
    • Les applications de 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 avec 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

Ajouter 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 \
        --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 Anthos Service Mesh géré.

    Anthos 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 \
        --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 pods et les services exécutés sur le cluster. Notez que GKE crée automatiquement une règle d'entrée correspondante.

    gcloud compute firewall-rules create allow-egress-pods-and-services \
        --action ALLOW \
        --direction EGRESS \
        --rules all \
        --destination-ranges 10.1.0.0/16,10.2.0.0/20 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow pods and services on nodes to reach each other"
    
  6. Facultatif: Cette règle de pare-feu n'est pas nécessaire si vous utilisez GKE Dataplane V2.

    Un service appelé Calico fournit les fonctionnalités de l'API NetworkPolicy pour GKE. Autorisez la connectivité pour Calico dans le sous-réseau :

    gcloud compute firewall-rules create allow-egress-calico \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:5473 \
        --destination-ranges 10.0.0.0/24 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow Calico Typha within the subnet"
    
  7. Le port en lecture seule du kubelet est nécessaire pour que GKE puisse lire les métriques de nœud. Autorisez-le à y accéder au sein du sous-réseau :

    gcloud compute firewall-rules create allow-egress-kubelet-readonly \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:10255 \
        --destination-ranges 10.0.0.0/24 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow access to the kubelet read-only port within the subnet"
    
  8. 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 \
        --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)"
    
  9. Autorisez le service du vérificateur d'état Google Cloud à accéder aux pods qui s'exécutent dans le cluster :

    gcloud compute firewall-rules create allow-ingress-gcp-health-checker \
        --action ALLOW \
        --direction INGRESS \
        --rules tcp:80,tcp:443 \
        --source-ranges 130.211.0.0/22,35.191.0.0/16,35.191.0.0/16,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.

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

    Google vous recommande d'opter pour le canal de publication "standard" pour le cluster afin de vous assurer que les nœuds exécutent une version de Kubernetes compatible avec Anthos Service Mesh.

    Pour en savoir plus sur les conditions préalables à l'exécution d'Anthos Service Mesh avec un plan de contrôle interne au cluster, consultez les conditions préalables au sein du cluster.

    Pour en savoir plus sur les exigences et les limites liées à l'exécution d'Anthos Service Mesh géré, consultez la page Fonctionnalités compatibles avec Anthos Service Mesh géré.

    Workload Identity est activé sur le cluster. Anthos Service Mesh nécessite Workload Identity. 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 Anthos Service Mesh

Suivez l'un des guides d'installation d'Anthos Service Mesh:

Une fois Anthos 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. Lorsque vous déployez la passerelle de sortie, la configuration est automatiquement injectée en fonction d'une étiquette que vous appliquez au déploiement ou à l'espace de noms. Si le tag par défaut est configuré, attribuez un libellé à votre espace de noms avec les libellés d'injection par défaut. Sinon, vous utilisez le libellé de révision pour le plan de contrôle que vous avez installé. Le libellé de révision que vous ajoutez varie également selon que vous avez déployé Anthos Service Mesh géré ou installé le plan de contrôle dans le cluster.

    Sélectionnez l'onglet ci-dessous en fonction de votre type d'installation (gérée ou dans le cluster).

    Géré

    Utilisez la commande suivante pour localiser les révisions de plan de contrôle disponibles:

    kubectl -n istio-system get controlplanerevision
    

    Le résultat ressemble à ce qui suit :

    NAME          RECONCILED   STALLED   AGE
    asm-managed   True         False     112m
    

    Notez la valeur de la colonne NAME pour la révision du plan de contrôle que vous souhaitez utiliser. En règle générale, la version disponible d'Anthos Service Mesh correspond à la version disponible de votre cluster Google Kubernetes Engine.

    Dans le cluster

    Pour les plans de contrôle intégrés au cluster, le service et le déploiement istiod ont généralement un libellé de révision semblable à istio.io/rev=asm-1146-16, où asm-1146-16 identifie la version d'Anthos Service Mesh. La révision fait partie du nom du service istiod, par exemple : istiod-asm-1146-16.istio-system.

    Utilisez la commande suivante pour localiser le libellé de révision sur istiod pour le plan de contrôle du cluster :

    kubectl get deploy -n istio-system -l app=istiod \
      -o=jsonpath='{.items[*].metadata.labels.istio\.io\/rev}''{"\n"}'
    
  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. Consultez la section Télécharger la version correcte 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
    
  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 disposent de affinity pour les nœuds du pool gateway et d'une tolérance leur permettant 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 la 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

Les étapes requises pour activer les journaux d'accès Envoy dépendent de votre type d'Anthos Service Mesh, qu'il soit géré ou dans le 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 qu'Anthos Service Mesh injecte automatiquement des proxys side-car, vous devez définir l'étiquette 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=REVISION
    kubectl label ns team-y istio.io/rev=REVISION
    
  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 du 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 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 explicitement définies avec des entrées de service dans les espaces de noms de sortie 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.

    La définition de egress.hosts indique que le proxy side-car ne sélectionne que les routes à partir de l'espace de noms de sortie mis à disposition à l'aide de l'attribut exportTo. La partie "team-x/*" inclut toutes les routes 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 pour le pod de test lui-même.

Configurer Anthos 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é sur l'espace de noms de sortie. La configuration Gateway est appliquée à l'espace de noms de sortie et gère le trafic pour n'importe quel hôte.

    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:
          loadBalancer:
            simple: ROUND_ROBIN
          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
    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
    

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

    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 des passerelles de sortie pour upgrade (créer) des requêtes HTTP simples vers TLS ou TLS mutuel. Permettre aux applications d'effectuer des requêtes HTTP simples présente plusieurs avantages lorsqu'elle est utilisée avec l'initialisation TLS et TLS mutuelle 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:
            loadBalancer:
              simple: ROUND_ROBIN
            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:
          loadBalancer:
            simple: ROUND_ROBIN
          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
    

    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 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
        ports:
        - 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 Workload Identity 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 des API Google, vous devez utiliser IAM pour accorder des autorisations. Le cluster que vous utilisez pour ce tutoriel est configuré pour utiliser Workload Identity, 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
    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
    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
    gsutil mb gs://${PROJECT_ID}-bucket
    gsutil cp /tmp/hello gs://${PROJECT_ID}-bucket/
    
  8. Autorisez le compte de service à répertorier et à afficher les fichiers du bucket :

    gsutil iam ch \
    serviceAccount:sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com:objectViewer \
        gs://${PROJECT_ID}-bucket/
    
  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 \
    -- gsutil 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.

Étapes suivantes