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 est destiné à accompagner le guide des bonnes pratiques.

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 :
  • Installez Anthos Service Mesh avec des passerelles 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. Dans Google Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.

    Accéder au sélecteur de projet

  2. Vérifiez que la facturation est activée pour votre projet Google Cloud.

  3. Dans la console Google Cloud, activez Cloud Shell.

    Activer 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. Rendez le script exécutable et exécutez-le à l'aide de la commande source pour initialiser votre environnement :

    chmod +x ./init-egress-tutorial.sh
    source ./init-egress-tutorial.sh
    
  7. Définissez les rôles IAM (gestion de l'authentification et des accès). Si vous êtes un propriétaire de projet, vous disposez de toutes les autorisations nécessaires pour terminer l'installation. Si vous n'êtes pas un propriétaire de projet, demandez à votre administrateur de vous accorder les rôles IAM suivants. Dans la commande suivante, remplacez PROJECT_EMAIL_ADDRESS par le compte que vous utilisez pour vous connecter à Google Cloud.

    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member user:PROJECT_EMAIL_ADDRESS \
        --role=roles/editor \
        --role=roles/compute.admin \
        --role=roles/container.admin \
        --role=roles/resourcemanager.projectIamAdmin \
        --role=roles/iam.serviceAccountAdmin \
        --role=roles/iam.serviceAccountKeyAdmin \
        --role=roles/gkehub.admin \
        --role=roles/serviceusage.serviceUsageAdmin
    
  8. Activez les API requises pour le tutoriel :

    gcloud services enable \
        dns.googleapis.com \
        container.googleapis.com \
        compute.googleapis.com \
        monitoring.googleapis.com \
        logging.googleapis.com \
        cloudtrace.googleapis.com \
        meshca.googleapis.com \
        meshtelemetry.googleapis.com \
        meshconfig.googleapis.com \
        iamcredentials.googleapis.com \
        gkeconnect.googleapis.com \
        gkehub.googleapis.com \
        cloudresourcemanager.googleapis.com \
        stackdriver.googleapis.com
    

    L'activation des API peut prendre une minute ou plus. Lorsque les API sont activées, un résultat semblable au suivant s'affiche :

    Operation "operations/acf.601db672-88e6-4f98-8ceb-aa3b5725533c" finished
    successfully.
    

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 \
        --destination-ranges 10.5.0.0/28 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow nodes to reach the Kubernetes API server."
    
  4. 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. 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.

Créez une zone DNS privée ainsi que des enregistrements "CNAME" et "A" de manière à ce 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 ainsi que des enregistrements "CNAME" et "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. Créez un cluster GKE privé :

    gcloud container clusters create cluster1 \
        --enable-ip-alias \
        --enable-private-nodes \
        --release-channel "regular" \
        --no-enable-basic-auth \
        --no-issue-client-certificate \
        --enable-master-authorized-networks \
        --master-authorized-networks ${SHELL_IP//\"}/32 \
        --master-ipv4-cidr 10.5.0.0/28 \
        --enable-network-policy \
        --service-account "sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
        --machine-type "e2-standard-4" \
        --num-nodes "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 nécessite que les nœuds du cluster utilisent un type de machine disposant d'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 Anthos Service Mesh. Pour en savoir plus, consultez les guides d'installation d'Anthos Service Mesh.

    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.

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

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

    gcloud container clusters get-credentials cluster1
    
  5. 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

Ce tutoriel utilise des fonctionnalités facultatives d'Anthos Service Mesh. Pour en savoir plus sur l'installation d'Anthos Service Mesh avec un script de lecture, consultez le guide d'installation dans la documentation.

  1. Créez des espaces de noms pour le déploiement du plan de contrôle du maillage de services et des passerelles de sortie :

    kubectl create ns istio-system
    kubectl create ns istio-egress
    
  2. Attribuez des libellés aux espaces de noms istio-egress, istio-system et kube-system :

    kubectl label ns istio-egress istio=egress istio-injection=disabled
    kubectl label ns istio-system istio=system
    kubectl label ns kube-system kube-system=true
    

    Ces libellés seront utilisés ultérieurement pour appliquer Kubernetes NetworkPolicy. Le libellé istio-injection=disabled évite les avertissements parasites lors de l'analyse istioctl.

  3. Créez un fichier de manifeste pour personnaliser l'installation d'Anthos Service Mesh à l'aide de l'API Istio Operator :

    cat << 'EOF' > ./asm-custom-install.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: "egress-gateway"
    spec:
      meshConfig:
        accessLogFile: "/dev/stdout"
      components:
        egressGateways:
          - name: "istio-egressgateway"
            enabled: true
            namespace: "istio-egress"
            label:
              istio: "egress"
            k8s:
              tolerations:
              - key: "dedicated"
                operator: "Equal"
                value: "gateway"
              nodeSelector:
                cloud.google.com/gke-nodepool: "gateway"
    EOF
    

    Ce fichier est fourni en tant qu'argument au script d'installation et spécifie la configuration suivante :

    • Un déploiement de passerelle de sortie qui s'exécute dans l'espace de noms istio-egress avec une tolérance et une fonction nodeSelector afin de garantir qu'il ne s'exécute que sur les nœuds gateway
    • Journalisation d'accès à "stdout" pour tous les proxys side-car.
  4. Téléchargez le script d'installation :

    curl -O https://storage.googleapis.com/csm-artifacts/asm/install_asm
    
  5. Téléchargez la signature SHA-256 du fichier dans le répertoire de travail :

    curl -O https://storage.googleapis.com/csm-artifacts/asm/install_asm.sha256
    
  6. Vérifiez le téléchargement avec les deux fichiers dans le même répertoire :

    sha256sum -c --ignore-missing install_asm.sha256
    

    Si la validation réussit, le résultat est le suivant :

    install_asm: OK
    

    Pour assurer la compatibilité, le fichier install_asm.sha256 inclut la somme de contrôle deux fois pour permettre de renommer n'importe quelle version du script en install_asm. Si vous obtenez une erreur indiquant que --ignore-missing n'existe pas, réexécutez la commande précédente sans l'option --ignore-missing.

  7. Rendez le script exécutable :

    chmod +x install_asm
    
  8. Installez Anthos Service Mesh en exécutant le script :

    ./install_asm \
        --mode install \
        --project_id ${PROJECT_ID} \
        --cluster_name cluster1 \
        --cluster_location ${ZONE} \
        --custom_overlay ./asm-custom-install.yaml \
        --output_dir ./ \
        --enable_all
    
  9. Une fois le script terminé, 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
    

Vérifier l'installation d'Anthos Service Mesh

  1. Vérifiez que les composants du plan de contrôle Anthos Service Mesh sont en cours d'exécution dans l'espace de noms istio-system :

    kubectl get pod -n istio-system
    

    Vous pouvez voir les pods istio-ingressgateway et istiod-asm en cours d'exécution.

  2. Vérifiez que les pods de passerelle de sortie s'exécutent dans l'espace de noms istio-egress et sur les nœuds du pool de nœuds gateway :

    kubectl get pods -n istio-egress -o wide
    
  3. Les pods de passerelle de sortie ont un nodeSelector pour sélectionner les nœuds du pool gateway et une tolérance qui leur permet de s'exécuter sur les nœuds de passerelle rejetés. Examinez le nodeSelector et les tolérances pour les pods de passerelle de sortie :

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

    Le résultat ressemble à ce qui suit :

    name                                   nodeSelector                                 tolerations
    istio-egressgateway-74687946f5-dg9mp   map[cloud.google.com/gke-nodepool:gateway]   map[key:dedicated operator:Equal value:gateway]
    

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 puisse injecter automatiquement les sidecars de proxy, vous devez définir un libellé de révision sur les espaces de noms de la charge de travail. L'étiquette de révision doit correspondre à la version du plan de contrôle d'Anthos Service Mesh qui a été déployée sur votre cluster. Recherchez le libellé de révision sur le pod istiod puis stockez-le dans une variable d'environnement :

    REVISION_LABEL=$(kubectl get pod -n istio-system -l app=istiod \
      -o jsonpath='{.items[0].metadata.labels.istio\.io/rev}')
    
  5. Définissez le libellé de révision sur les espaces de noms team-x et team-y :

    kubectl label ns team-x istio.io/rev=${REVISION_LABEL}
    kubectl label ns team-y istio.io/rev=${REVISION_LABEL}
    
  6. 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
    
  7. Déployez l'application de test dans l'espace de noms team-x :

    kubectl -n team-x create -f ./test.yaml
    
  8. 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.

  9. 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 sidecar 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 20 clusters Envoy, dont plusieurs pour la passerelle de sortie.

  2. Restreignez la configuration du proxy aux routes de sortie définies explicitement avec des entrées de service dans les espaces de noms istio-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.

    La partie "istio-egress/*" indique que le proxy sidecar sélectionne des routes de l'espace de noms istio-egress 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
    

    Le résultat ne comprend que quelques clusters pour la passerelle de sortie et un cluster pour le pod de test.

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. Le Gateway sélectionne le proxy istio-egressgateway déployé par le programme d'installation dans l'espace de noms istio-egress. La configuration Gateway est appliquée à l'espace de noms istio-egress 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: egress
      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 istio-egress 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=egress \
        -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:
      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:
      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 "mettre à niveau" (initier) les requêtes HTTP vers le protocole TLS. 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:
            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 "mises à niveau" 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=egress \
        -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: egress
      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:
      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=egress \
        -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:
              istio: system
          podSelector:
            matchLabels:
              istio: istiod
        - namespaceSelector:
            matchLabels:
              istio: egress
          podSelector:
            matchLabels:
              istio: egress
    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:
              kube-system: "true"
        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:
              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 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, 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. Dans la console Google Cloud, accédez à la page Gérer les ressources.

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer.
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Étapes suivantes