Usa puertas de enlace de salida de Cloud Service Mesh en los clústeres de GKE: Instructivo


En este instructivo, se muestra cómo usar las puertas de enlace de salida de Cloud Service Mesh y otros controles de Google Cloud para proteger el tráfico saliente (salida) de las cargas de trabajo implementadas en un clúster de Google Kubernetes Engine. El instructivo está diseñado como un complemento de las prácticas recomendadas para usar las puertas de enlace de salida de Cloud Service Mesh en los clústeres de GKE.

El público previsto para este instructivo incluye ingenieros de redes, seguridad y plataforma que administran los clústeres de Google Kubernetes Engine que usan uno o más equipos de entrega de software. Los controles que se describen aquí son especialmente útiles para las organizaciones que deben demostrar el cumplimiento de las regulaciones (por ejemplo, GDPR y PCI).

Objetivos

  • Configura la infraestructura para ejecutar Cloud Service Mesh:
  • Instala Cloud Service Mesh.
  • Instala proxies de puerta de enlace de salida que se ejecutan en un grupo de nodos dedicado.
  • Configura reglas de enrutamiento de varios usuarios para tráfico externo a través de la puerta de enlace de salida:
    • Las aplicaciones del espacio de nombres team-x se pueden conectar a example.com
    • Las aplicaciones del espacio de nombres team-y se pueden conectar a httpbin.org
  • Usa el recurso Sidecar a fin de restringir el alcance de la configuración de salida del proxy de sidecar para cada espacio de nombres.
  • Configura políticas de autorización para aplicar reglas de salida.
  • Configura la puerta de enlace de salida para actualizar solicitudes HTTP simples a TLS (TLS de origen).
  • Configura la puerta de enlace de salida para pasar por el tráfico de TLS.
  • Configura las políticas de red de Kubernetes como un control de salida adicional.
  • Configura el acceso directo a las API de Google con permisos de Identity and Access Management (IAM) y Acceso privado a Google.

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Cuando completes el instructivo puedes borrar los recursos que hayas creado para evitar que se te sigan cobrando. Para obtener más información, consulta Realiza una limpieza.

Antes de comenzar

  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. Crea un directorio de trabajo para usar mientras sigues el instructivo:

    mkdir -p ~/WORKING_DIRECTORY
    cd ~/WORKING_DIRECTORY
    
  5. Crea una secuencia de comandos de shell a fin de inicializar tu entorno para el instructivo. Reemplaza y edita las variables según tu proyecto y tus preferencias. Ejecuta esta secuencia de comandos con el comando source para reinicializar tu entorno si se vence la sesión de shell:

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

    gcloud services enable compute.googleapis.com --project=YOUR_PROJECT_ID
    
  7. Haz que la secuencia de comandos sea ejecutable y ejecútala con el comando source para inicializar tu entorno. Selecciona Y si se te solicita que habilites compute.googleapis.com:

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

Configurar la infraestructura

Crea una red y subred de VPC

  1. Crea una red de VPC nueva:

    gcloud compute networks create vpc-network \
        --subnet-mode custom
    
  2. Crea una subred para que el clúster se ejecute en ella con rangos de direcciones IP secundarios asignados previamente para los Pods y los servicios. El Acceso privado a Google está habilitado para que las aplicaciones con solo direcciones IP internas puedan alcanzar los servicios y las API de 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
    

Configura Cloud NAT

Cloud NAT permite que las cargas de trabajo sin direcciones IP externas se conecten a destinos en Internet y reciban respuestas entrantes de esos destinos.

  1. Crea un Cloud Router:

    gcloud compute routers create nat-router \
        --network vpc-network
    
  2. Agrega una configuración de NAT al router:

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

Crea cuentas de servicio para cada grupo de nodos de GKE

Crea dos cuentas de servicio para que la usen los dos grupos de nodos de GKE. Se asigna una cuenta de servicio independiente a cada grupo de nodos para que puedas aplicar reglas de firewall de VPC a nodos específicos.

  1. Crea una cuenta de servicio para que la usen los nodos en el grupo de nodos predeterminado:

    gcloud iam service-accounts create sa-application-nodes \
        --description="SA for application nodes" \
        --display-name="sa-application-nodes"
    
  2. Crea una cuenta de servicio para que la usen los nodos en el grupo de nodos de puerta de enlace:

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

Otorga permisos a las cuentas de servicio

Agrega un conjunto mínimo de roles de IAM a las cuentas de servicio de la aplicación y de la puerta de enlace. Estos roles son necesarios para registrar, supervisar y extraer imágenes de contenedores privadas 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

Cómo crear las reglas de firewall

En los pasos siguientes, aplica una regla de firewall a la red de VPC para que, de forma predeterminada, se deniegue todo el tráfico de salida. Se requiere conectividad específica para que el clúster funcione y que los nodos de puerta de enlace puedan llegar a destinos fuera de la VPC. Un conjunto mínimo de reglas de firewall específicas anulan la regla predeterminada para denegar todo para permitir la conectividad necesaria.

  1. Crea una regla de firewall predeterminada (de baja prioridad) para denegar toda la salida desde la red de 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. Crea una regla para permitir que solo los nodos con la cuenta de servicio de la puerta de enlace lleguen a Internet:

    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. Permite que los nodos lleguen al plano de control de Kubernetes:

    gcloud compute firewall-rules create allow-egress-to-api-server \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:443,tcp:10250 \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --destination-ranges 10.5.0.0/28 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow nodes to reach the Kubernetes API server."
    
  4. Opcional: Esta regla de firewall no es necesaria si usas Cloud Service Mesh administrado.

    Cloud Service Mesh usa webhooks cuando se inyectan proxies de sidecar en las cargas de trabajo. Permite que el servidor de la API de GKE llame a los webhooks expuestos por el plano de control de la malla de servicios que se ejecuta en los nodos:

    gcloud compute firewall-rules create allow-ingress-api-server-to-webhook \
        --action ALLOW \
        --direction INGRESS \
        --rules tcp:15017 \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --source-ranges 10.5.0.0/28 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow the API server to call the webhooks exposed by istiod discovery"
    
  5. Permite la conectividad de salida entre nodos y Pods que se ejecutan en el clúster. GKE crea automáticamente una regla de entrada correspondiente. No se requiere ninguna regla para la conectividad de Service, ya que la cadena de enrutamiento de iptables siempre convierte las direcciones IP de Service en direcciones IP de Pod.

    gcloud compute firewall-rules create allow-egress-nodes-and-pods \
        --action ALLOW \
        --direction EGRESS \
        --rules all \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --destination-ranges 10.0.0.0/24,10.1.0.0/16 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow egress to other Nodes and Pods"
    
  6. Permite el acceso a los conjuntos reservados de direcciones IP que usa el Acceso privado a Google para entregar las API de Google, Container Registry y otros servicios:

    gcloud compute firewall-rules create allow-egress-gcp-apis \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --destination-ranges 199.36.153.8/30 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow access to the VIPs used by Google Cloud APIs (Private Google Access)"
    
  7. Permite que el servicio de verificador de estado de Google Cloud acceda a los Pods que se ejecutan en el clúster. Consulta Verificaciones de estado para obtener más información.

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

Configura el acceso privado a las API de Google Cloud

El Acceso privado a Google permite que las VM y los Pods que solo tienen direcciones IP internas tengan acceso a las API y los servicios de Google. Aunque las API y los servicios de Google se entregan desde IP externas, el tráfico de los nodos nunca sale de la red de Google cuando se usa el Acceso privado a Google.

Habilita la API de Cloud DNS.

gcloud services enable dns.googleapis.com

Crea una zona de DNS privada, un registro CNAME y A para que los nodos y las cargas de trabajo puedan conectarse a los servicios y las APIs de Google mediante el Acceso privado a Google y el nombre de host 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

Configura el acceso privado a Container Registry

Crea una zona de DNS privada, un CNAME y un registro A para que los nodos se puedan conectar a Container Registry mediante el Acceso privado a Google y el nombre de host 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

Crea un clúster de GKE privado

  1. Busca la dirección IP externa de Cloud Shell para que puedas agregarla a la lista de redes que tienen acceso al servidor de la API de tu clúster:

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

    Después de un período de inactividad, la dirección IP externa de la VM de Cloud Shell puede cambiar. Si eso sucede, debes actualizar la lista de redes autorizadas de tu clúster. Agrega el siguiente comando a la secuencia de comandos de inicialización:

    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. Habilita la API de Google Kubernetes Engine:

    gcloud services enable container.googleapis.com
    
  3. Crea un clúster de GKE privado:

    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}
    

    El clúster tarda unos minutos en crearse. El clúster tiene nodos privados con direcciones IP internas. A los pods y los servicios se les asignan IP de los rangos secundarios con nombre que definiste cuando creaste la subred de VPC.

    Cloud Service Mesh con un plano de control en el clúster requiere que los nodos del clúster usen un tipo de máquina que tenga al menos 4 CPU virtuales.

    Google recomienda que el clúster esté suscrito al canal de versiones "normales" para garantizar que los nodos ejecuten una versión de Kubernetes compatible con Cloud Service Mesh.

    Para obtener más información sobre los requisitos previos para ejecutar Cloud Service Mesh con un plano de control en el clúster, consulta los requisitos previos del clúster.

    Para obtener más información sobre los requisitos y las limitaciones para ejecutar Cloud Service Mesh administrado, consulta las funciones compatibles con Cloud Service Mesh administrado.

    La federación de identidades para cargas de trabajo para GKE está habilitada en el clúster. Cloud Service Mesh requiere la federación de identidades para cargas de trabajo para GKE y es la forma recomendada de acceder a las APIs de Google desde cargas de trabajo de GKE.

  4. Crea un grupo de nodos llamado gateway. Este grupo de nodos es donde se implementa la puerta de enlace de salida. El taint dedicated=gateway:NoSchedule se agrega a cada nodo en el grupo de nodos de puerta de enlace.

    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"
    

    Los taints y las tolerancias de Kubernetes ayudan a garantizar que solo los Pods de puerta de enlace de salida se ejecuten en nodos del grupo de nodos de puerta de enlace.

  5. Descarga credenciales para que puedas conectarte al clúster con kubectl:

    gcloud container clusters get-credentials cluster1
    
  6. Verifica que los nodos de puerta de enlace tengan el taint correcto:

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

    El resultado es similar a este:

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

Instala y configura Cloud Service Mesh

Sigue una de las guías de instalación de Cloud Service Mesh:

Una vez que hayas instalado Cloud Service Mesh, detente y regresa a este instructivo sin instalar puertas de enlace de entrada o salida.

Instala una puerta de enlace de salida

  1. Crea un espacio de nombres de Kubernetes para la puerta de enlace de salida:

    kubectl create namespace istio-egress
    
  2. Cuando implementes la puerta de enlace de salida, la configuración se insertará automáticamente según una etiqueta que apliques a la implementación o al espacio de nombres. Si la etiqueta predeterminada está configurada, etiqueta tu espacio de nombres con las etiquetas de inserción predeterminadas. De lo contrario, usa la etiqueta de revisión para el plano de control que instalaste. La etiqueta de revisión que agregues también depende de si implementaste Cloud Service Mesh administrado o instalaste el plano de control en el clúster.

    A continuación, selecciona la pestaña según tu tipo de instalación (ya sea administrada o en el clúster).

    Administrado

    Usa el siguiente comando para ubicar las revisiones del plano de control disponibles:

    kubectl -n istio-system get controlplanerevision
    

    El resultado es similar a este:

    NAME          RECONCILED   STALLED   AGE
    asm-managed   True         False     112m
    

    Anota el valor de la columna NAME para la revisión del plano de control que deseas usar. Por lo general, el canal de versiones de Cloud Service Mesh corresponde al canal de versiones de tu clúster de Google Kubernetes Engine.

    En el clúster

    En los planos de control en el clúster, el servicio y Deployment de istiod suelen tener una etiqueta de revisión similar a istio.io/rev=, en la que identifica la versión de Cloud Service Mesh. La revisión pasa a formar parte del nombre del servicio istiod, por ejemplo: istiod-.istio-system.

    Usa el siguiente comando a fin de encontrar la etiqueta de revisión en istiod para el plano de control en el clúster:

    kubectl get deploy -n istio-system -l app=istiod \
      -o=jsonpath='{.items[*].metadata.labels.istio\.io\/rev}''{"\n"}'
    
  3. Opcional: Etiqueta el espacio de nombres para que la configuración de la puerta de enlace se inserte automáticamente. Es suficiente etiquetar el espacio de nombres o la implementación. Para los fines de este instructivo, etiqueta ambos para evitar advertencias de la herramienta istioctl analyze.

    kubectl label namespace istio-egress istio.io/rev=REVISION
    
  4. Crea un manifiesto del operador para la puerta de enlace de salida:

    cat << EOF > egressgateway-operator.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: egressgateway-operator
      annotations:
        config.kubernetes.io/local-config: "true"
    spec:
      profile: empty
      revision: REVISION
      components:
        egressGateways:
        - name: istio-egressgateway
          namespace: istio-egress
          enabled: true
      values:
        gateways:
          istio-egressgateway:
            injectionTemplate: gateway
            tolerations:
              - key: "dedicated"
                operator: "Equal"
                value: "gateway"
            nodeSelector:
              cloud.google.com/gke-nodepool: "gateway"
    EOF
    
  5. Descarga la herramienta istioctl. Debes usar la versión 1.16.2-asm.2 o una posterior, incluso si usas la versión 1.15 o una anterior de Cloud Service Mesh. Consulta Descarga la versión correcta de istioctl.

  6. Después de extraer el archivo descargado, configura una variable de entorno para conservar la ruta de acceso a la herramienta de istioctl y agrégala a tu secuencia de comandos de inicialización:

    ISTIOCTL=$(find "$(pwd -P)" -name istioctl)
    echo "ISTIOCTL=\"${ISTIOCTL}\"" >> ./init-egress-tutorial.sh
    
  7. Crea el manifiesto de instalación de la puerta de enlace de salida con el manifiesto del operador y istioctl:

    ${ISTIOCTL} manifest generate \
        --filename egressgateway-operator.yaml \
        --output egressgateway \
        --cluster-specific
    
  8. Instala la puerta de enlace de salida:

    kubectl apply --recursive --filename egressgateway/
    
  9. Verifica que la puerta de enlace de salida se ejecute en los nodos del grupo de nodos gateway:

    kubectl get pods -n istio-egress -o wide
    
  10. Los Pods de puerta de enlace de salida tienen affinity para los nodos del grupo de nodos gateway y una tolerancia que les permite ejecutarse en los nodos de puerta de enlace con taints. Examina la afinidad y las tolerancias de los nodos para los pods de puerta de enlace de salida:

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

    El resultado es similar a este:

    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]
    

Habilita el registro de acceso de Envoy

Los pasos necesarios para habilitar los registros de acceso de Envoy dependen del tipo de Cloud Service Mesh, ya sea administrado o en el clúster:

Prepara la malla y una aplicación de prueba

  1. Asegúrate de que la TLS mutua STRICT esté habilitada. Aplica una política de PeerAuthentication predeterminada para la malla en el espacio de nombres 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
    

    Puedes anular esta configuración creando recursos de PeerAuthentication en espacios de nombres específicos.

  2. Crea espacios de nombres para usar en la implementación de cargas de trabajo de prueba. En los pasos posteriores de este instructivo, se explica cómo configurar diferentes reglas de enrutamiento de salida para cada espacio de nombres.

    kubectl create namespace team-x
    kubectl create namespace team-y
    
  3. Etiqueta los espacios de nombres para que las políticas de red de Kubernetes puedan seleccionarlos:

    kubectl label namespace team-x team=x
    kubectl label namespace team-y team=y
    
  4. Para que Cloud Service Mesh inserte de forma automática sidecars de proxy, debes configurar la etiqueta de revisión del plano de control en los espacios de nombres de la carga de trabajo:

    kubectl label ns team-x istio.io/rev=REVISION
    kubectl label ns team-y istio.io/rev=REVISION
    
  5. Crea un archivo YAML para usarlo en la implementación de pruebas:

    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. Implementa la aplicación de prueba en el espacio de nombres team-x:

    kubectl -n team-x create -f ./test.yaml
    
  7. Verifica que la aplicación de prueba se implemente en un nodo en el grupo predeterminado y que se inserte un contenedor de sidecar del proxy. Repite el siguiente comando hasta que el estado del Pod sea Running:

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

    El resultado es similar al siguiente:

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

    2 de 2 contenedores son Running. Un contenedor es la aplicación de prueba y el otro es el sidecar del proxy.

    El Pod se ejecuta en un nodo en el grupo de nodos predeterminado.

  8. Verifica que no sea posible realizar una solicitud HTTP desde el contenedor de prueba a un sitio externo:

    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
    

    Se genera un mensaje de error del proxy de sidecar porque la regla de firewall global-deny-egress-all deniega la conexión ascendente.

Usa el recurso de sidecar para restringir el alcance de la configuración del proxy de sidecar

Puedes usar el recurso de sidecar para restringir el alcance del objeto de escucha de salida que se configura para proxies de sidecar. A fin de reducir un sobredimensionamiento en la configuración y el uso de memoria, se recomienda aplicar un recurso de Sidecar predeterminado para cada espacio de nombres.

El proxy que Cloud Service Mesh ejecuta en el archivo de sidecar es Envoy. En la Terminología de Envoy, un cluster es un grupo lógicamente similar de extremos ascendentes que se usa como destino para el balanceo de cargas.

  1. Inspecciona los clústeres salientes configurados en el proxy de sidecar de Envoy para el Pod de prueba mediante la ejecución del comando istioctl proxy-config:

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

    Hay alrededor de 11 clústeres de Envoy en la lista, incluidos algunos para la puerta de enlace de salida.

  2. Restringe la configuración del proxy a las rutas de salida que se definieron de forma explícita con entradas de servicio en los espacios de nombres de salida y team-x. Aplica un recurso de Sidecar al espacio de nombres 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
    

    La configuración del modo de política de tráfico saliente en REGISTRY_ONLY restringe la configuración del proxy para incluir solo los hosts externos que se agregaron de manera explícita al registro de servicio de la malla mediante la definición de las entradas de servicio.

    La configuración de egress.hosts especifica que el proxy de sidecar solo selecciona rutas del espacio de nombres de salida que están disponibles mediante el atributo exportTo. La parte “team-x/*” incluye cualquier ruta que se haya configurado de forma local en el espacio de nombres team-x.

  3. Visualiza los clústeres salientes configurados en el proxy de sidecar de Envoy y compáralos con la lista de clústeres que se configuraron antes de aplicar el recurso Sidecar:

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

    Verás clústeres para la puerta de enlace de salida y uno para el propio Pod de prueba.

Configura Cloud Service Mesh para enrutar el tráfico a través de la puerta de enlace de salida

  1. Configura una Gateway para el tráfico HTTP en el puerto 80. Gateway selecciona el proxy de la puerta de enlace de salida que implementaste en el espacio de nombres de salida. La configuración de Gateway se aplica al espacio de nombres de salida y controla el tráfico para cualquier host.

    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. Crea una DestinationRule para la puerta de enlace de salida con TLS mutua para la autenticación y encriptación. Usa una sola regla de destino compartida para todos los hosts externos.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: target-egress-gateway
      namespace: istio-egress
    spec:
      host: istio-egressgateway.istio-egress.svc.cluster.local
      subsets:
      - name: target-egress-gateway-mTLS
        trafficPolicy:
          tls:
            mode: ISTIO_MUTUAL
    EOF
    
  3. Crea una ServiceEntry en el espacio de nombres de salida para registrar example.com en el registro de servicio de la malla para el espacio de nombres team-x de manera explícita:

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: example-com-ext
      namespace: istio-egress
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: example.com
    spec:
      hosts:
      - example.com
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'team-x'
      - 'istio-egress'
    EOF
    
  4. Crea un VirtualService para enrutar el tráfico a example.com a través de la puerta de enlace de salida. Existen dos condiciones de coincidencia: la primera condición dirige el tráfico a la puerta de enlace de salida y la segunda dirige el tráfico de la puerta de enlace de salida al host de destino. La propiedad exportTo controla qué espacios de nombres pueden usar el servicio virtual.

    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. Ejecuta istioctl analyze para comprobar si hay errores de configuración:

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

    El resultado es similar al siguiente:

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. Envía varias solicitudes a través de la puerta de enlace de salida al sitio externo:

    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
    

    Verás los códigos de estado 200 para las cuatro respuestas.

  7. Verifica que las solicitudes se hayan dirigido a través de la puerta de enlace de salida mediante la verificación de los registros de acceso del proxy. Primero, verifica el registro de acceso del sidecar del proxy implementado con la aplicación de prueba:

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

    Para cada solicitud que envíes, verás una entrada de registro similar a la que se muestra a continuación:

    [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. Verifica también el registro de acceso de la puerta de enlace de salida:

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

    Para cada solicitud que envíes, verás una entrada de registro de acceso de puerta de enlace de salida similar a la siguiente:

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

Configura un enrutamiento diferente para un segundo espacio de nombres

Configura el enrutamiento de un segundo host externo a fin de obtener información sobre cómo se pueden configurar una conectividad externa diferente para distintos equipos.

  1. Crea un recurso de Sidecar para el espacio de nombres 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. Implementa la aplicación de prueba en el espacio de nombres team-y:

    kubectl -n team-y create -f ./test.yaml
    
  3. Registra un segundo host externo y expórtalo al team-x y al espacio de nombres team-y:

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: httpbin-org-ext
      namespace: istio-egress
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: httpbin.org
    spec:
      hosts:
      - httpbin.org
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'istio-egress'
      - 'team-x'
      - 'team-y'
    EOF
    
  4. Crea un servicio virtual para enrutar el tráfico a httpbin.org a través de la puerta de enlace de salida:

    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. Ejecuta istioctl analyze para comprobar si hay errores de configuración:

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

    Verás lo siguiente:

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. Haz una solicitud a httpbin.org desde la aplicación de prueba 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
    

    Verás una respuesta 200 OK.

  7. También haz una solicitud a httpbin.org desde la aplicación de prueba 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
    

    Verás una respuesta 200 OK.

  8. Intenta realizar una solicitud a example.com desde el espacio de nombres 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 solicitud falla porque no hay una ruta de salida configurada para el host example.com.

Usa la política de autorización para proporcionar control adicional sobre el tráfico

En este instructivo, las políticas de autorización para la puerta de enlace de salida se crean en el espacio de nombres istio-egress. Puedes configurar el RBAC de Kubernetes para que solo los administradores de red puedan acceder al espacio de nombres istio-egress.

  1. Crea una AuthorizationPolicy para que las aplicaciones en el espacio de nombres team-x puedan conectarse a example.com, pero no a otros hosts externos cuando envíes solicitudes usando el puerto 80. El targetPort correspondiente en los Pods de la puerta de enlace de salida es 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. Verifica que puedes realizar una solicitud a example.com desde la aplicación de prueba en el espacio de nombres 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
    

    Verás una respuesta 200 OK.

  3. Intenta realizar una solicitud a httpbin.org desde la aplicación de prueba en el espacio de nombres 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 solicitud falla con un mensaje RBAC: access denied y un código de estado 403 Forbidden. Es posible que debas esperar unos segundos porque, por lo general, la política de autorización suele tardarse un poco en aplicarse.

  4. Las políticas de autorización proporcionan un control detallado sobre el tráfico que se permite o se deniega. Aplica la siguiente política de autorización para permitir que la app de prueba en el espacio de nombres team-y realice solicitudes a httpbin.org mediante una ruta de URL específica cuando se envíen solicitudes con el puerto 80. El targetPort correspondiente en los Pods de la puerta de enlace de salida es 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. Intenta conectarte a httpbin.org desde la aplicación de prueba en el espacio de nombres 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 solicitud falla con un RBAC: el mensaje de acceso denegado y un código de estado 403 Forbidden.

  6. Ahora, realiza una solicitud a httpbin.org/status/418 desde la misma aplicación:

    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 solicitud se ejecuta de forma correcta porque la ruta de acceso coincide con el patrón en la política de autorización. El resultado es similar al siguiente:

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

TLS de origen en la puerta de enlace de salida

Puedes configurar puertas de enlace de salida en upgrade (originar) solicitudes HTTP simples a TLS o TLS mutua. Permitir que las aplicaciones realicen solicitudes HTTP simples tiene varias ventajas cuando se usan con la TLS mutua y la iniciación de TLS. Para obtener más información, consulta la guía de prácticas recomendadas.

TLS de origen en la puerta de enlace de salida

  1. Crea una DestinationRule. The DestinationRule que especifique que la puerta de enlace origina una conexión TLS a example.com.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: example-com-originate-tls
      namespace: istio-egress
    spec:
      host: example.com
      subsets:
        - name: example-com-originate-TLS
          trafficPolicy:
            portLevelSettings:
            - port:
                number: 443
              tls:
                mode: SIMPLE
                sni: example.com
    EOF
    
  2. Actualiza el servicio virtual para example.com a fin de que las solicitudes al puerto 80 en la puerta de enlace se upgraded a TLS en el puerto 443 cuando se envíen al host de destino:

    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. Realiza varias solicitudes a example.com desde la app de prueba en el espacio de nombres 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
    

    Como antes, las solicitudes funcionan correctamente con respuestas 200 OK.

  4. Verifica el registro de puerta de enlace de salida para comprobar que la puerta de enlace enruta las solicitudes al host de destino mediante conexiones TLS de origen:

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

    El resultado es similar al siguiente:

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

    El sidecar del proxy envió la solicitud a la puerta de enlace usando el puerto 80 y la TLS de origen en el puerto 443 para enviar la solicitud al host de destino.

Paso de conexiones HTTPS/TLS

Es posible que tus aplicaciones existentes ya usen conexiones TLS cuando se comunican con servicios externos. Puedes configurar la puerta de enlace de salida para traspasar conexiones TLS sin desencriptarlas.

paso de tls

  1. Modifica tu configuración de modo que la puerta de enlace de salida use el paso TLS para las conexiones al puerto 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. Actualiza la DestinationRule que apunta a la puerta de enlace de salida para agregar un segundo subconjunto para el puerto 443 en la puerta de enlace. Este subconjunto nuevo no usa TLS mutua. La TLS mutua de Istio no es compatible para el paso de conexiones TLS. Las conexiones en el puerto 80 siguen usando mTLS:

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: target-egress-gateway
      namespace: istio-egress
    spec:
      host: istio-egressgateway.istio-egress.svc.cluster.local
      subsets:
      - name: target-egress-gateway-mTLS
        trafficPolicy:
          portLevelSettings:
          - port:
              number: 80
            tls:
              mode: ISTIO_MUTUAL
      - name: target-egress-gateway-TLS-passthrough
    EOF
    
  3. Actualiza el servicio virtual para example.com a fin de que el tráfico TLS en el puerto 443 se pase a través de la puerta de enlace:

    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. Actualiza el servicio virtual para httpbin.org a fin de que el tráfico TLS en el puerto 443 se pase a través de la puerta de enlace:

    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. Agrega una política de autorización que acepte cualquier tipo de tráfico enviado al puerto 443 del servicio de puerta de enlace de salida. El targetPort correspondiente en los Pods de puerta de enlace es 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. Ejecuta istioctl analyze para comprobar si hay errores de configuración:

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

    Verás lo siguiente:

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  7. Realiza una solicitud HTTP simple a example.com desde la aplicación de prueba en el espacio de nombres 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 solicitud se ejecuta correctamente con una respuesta 200 OK.

  8. Ahora realiza varias solicitudes TLS (HTTPS) desde la aplicación de prueba en el espacio de nombres 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
    

    Verás 200 respuestas.

  9. Mira el registro de la puerta de enlace de salida nuevamente:

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

    Verás entradas de registro similares a las siguientes:

    [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 solicitud HTTPS se procesó como tráfico de TCP y se pasó por la puerta de enlace al host de destino, por lo que no se incluye información HTTP en el registro.

Usa Kubernetes NetworkPolicy como un control adicional

Hay muchas situaciones en las que una aplicación puede omitir un proxy de sidecar. Puedes usar NetworkPolicy de Kubernetes a fin de especificar de forma adicional qué conexiones pueden realizar las cargas de trabajo. Una vez que se aplica una sola política de red, se deniegan todas las conexiones que no se permiten de forma específica.

En este instructivo, solo se consideran las conexiones de salida y los selectores de salida para las políticas de red. Si controlas la entrada con políticas de red en tus propios clústeres, debes crear políticas de entrada para que coincidan con tus políticas de salida. Por ejemplo, si permites la salida de cargas de trabajo en el espacio de nombres team-x al espacio de nombres team-y, también debes permitir la entrada al espacio de nombres team-y desde el espacio de nombres team-x.

  1. Permite que las cargas de trabajo y los proxies implementados en el espacio de nombres team-x se conecten a istiod y la puerta de enlace de salida:

    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. Permite que las cargas de trabajo y los proxies consulten el 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. Permite que las cargas de trabajo y los proxies se conecten a las IP que entregan las APIs y los servicios de Google, incluida la autoridad certificadora de Cloud Service Mesh:

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-google-apis
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to:
        - ipBlock:
            cidr: 199.36.153.4/30
        - ipBlock:
            cidr: 199.36.153.8/30
    EOF
    
  4. Permite que las cargas de trabajo y los proxies se conecten al servidor de metadatos de GKE:

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-metadata-server
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to: # For GKE data plane v2
        - ipBlock:
            cidr: 169.254.169.254/32
      - to: # For GKE data plane v1
        - ipBlock:
            cidr: 127.0.0.1/32 # Prior to 1.21.0-gke.1000
        - ipBlock:
            cidr: 169.254.169.252/32 # 1.21.0-gke.1000 and later
        ports:
        - protocol: TCP
          port: 987
        - protocol: TCP
          port: 988
    EOF
    
  5. Opcional: Permite que las cargas de trabajo y los proxies del espacio de nombres team-x hagan conexiones entre sí:

    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. Opcional: Permite que las cargas de trabajo y los proxies en el espacio de nombres team-x establezcan conexiones con las cargas de trabajo implementadas por un equipo diferente:

    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. Las conexiones entre proxies de sidecar persisten. Las conexiones existentes no se cierran cuando aplicas una política de red nueva. Reinicia las cargas de trabajo en el espacio de nombres de Team-x para asegurarte de que las conexiones existentes estén cerradas:

    kubectl -n team-x rollout restart deployment
    
  8. Verifica que aún puedas realizar una solicitud HTTP a example.com desde la aplicación de prueba en el espacio de nombres 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 solicitud se ejecuta correctamente con una respuesta 200 OK.

Accede directamente a las API de Google con permisos de IAM y Acceso privado a Google

Las API y los servicios de Google se exponen mediante direcciones IP externas. Cuando los Pods con direcciones IP de alias nativos de VPC realizan conexiones a las API de Google mediante el Acceso privado a Google, el tráfico nunca sale de la red de Google.

Cuando configuraste la infraestructura para este instructivo, habilitaste el Acceso privado a Google para la subred que usan los Pods de GKE. Para permitir el acceso a las direcciones IP que usa el Acceso privado a Google, creaste una ruta, una regla de firewall de VPC y una zona de DNS privado. Esta configuración permite que los Pods lleguen a las API de Google directamente sin enviar tráfico a través de la puerta de enlace de salida. Puedes controlar qué APIs están disponibles para cuentas de servicio de Kubernetes específicas (y, por lo tanto, espacios de nombres) mediante Workload Identity Federation for GKE y IAM. La autorización de Istio no se aplica porque la puerta de enlace de salida no maneja conexiones a las APIs de Google.

Antes de que los pods puedan llamar a las APIs de Google, debes usar IAM para otorgar permisos. El clúster que usas para este instructivo está configurado para usar la federación de identidades para cargas de trabajo para GKE, que permite que una cuenta de servicio de Kubernetes actúe como una cuenta de servicio de Google.

  1. Crea una cuenta de servicio de Google para que tu aplicación use:

    gcloud iam service-accounts create sa-test-app-team-x
    
  2. Permite que la cuenta de servicio de Kubernetes actúe en nombre de la cuenta de servicio de 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. Anota la cuenta de servicio de Kubernetes para la app de prueba en el espacio de nombres team-x con la dirección de correo electrónico de la cuenta de servicio de 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. El Pod de la aplicación de prueba debe poder acceder al servidor de metadatos de Google (que se ejecuta como DaemonSet) a fin de obtener credenciales temporales para llamar a las API de Google. Crea una entrada de servicio para el servidor de metadatos de GKE:

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: metadata-google-internal
      namespace: istio-egress
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: metadata.google.internal
    spec:
      hosts:
      - metadata.google.internal
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'istio-egress'
      - 'team-x'
    EOF
    
  5. También crea una entrada de servicio para private.googleapis.com y storage.googleapis.com:

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: private-googleapis-com
      namespace: istio-egress
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: googleapis.com
    spec:
      hosts:
      - private.googleapis.com
      - storage.googleapis.com
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'istio-egress'
      - 'team-x'
    EOF
    
  6. Verifica que la cuenta de servicio de Kubernetes esté configurada correctamente para que actúe como la cuenta de servicio de 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
    

    Verás la cuenta de servicio de Google como la identidad activa y única.

  7. Crea un archivo de prueba en un bucket de Cloud Storage:

    echo "Hello, World!" > /tmp/hello
    gcloud storage buckets create gs://${PROJECT_ID}-bucket
    gcloud storage cp /tmp/hello gs://${PROJECT_ID}-bucket/
    
  8. Otorga permiso a la cuenta de servicio para enumerar y ver archivos en el bucket:

    gcloud storage buckets add-iam-policy-binding gs://${PROJECT_ID}-bucket/ \
        --member=serviceAccount:sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com \
        --role=roles/storage.objectViewer
    
  9. Verifica que la aplicación de prueba pueda acceder al bucket de prueba:

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

    Verás lo siguiente:

    Hello, World!
    

Limpia

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos usados en este instructivo, borra el proyecto que contiene los recursos o conserva el proyecto y borra los recursos individuales.

Para evitar que se generen cargos en tu cuenta de Google Cloud por los recursos que se usaron en este instructivo, sigue los pasos de las siguientes secciones:

Borra el proyecto

La manera más fácil de eliminar la facturación es borrar el proyecto que creaste para el instructivo.

  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.

¿Qué sigue?