Usar gateways de saída do Cloud Service Mesh em clusters do GKE: tutorial

Este tutorial mostra como usar gateways de saída do Cloud Service Mesh e outros Google Cloud controlos para proteger o tráfego de saída (saída) de cargas de trabalho implementadas num cluster do Google Kubernetes Engine. Este tutorial destina-se a complementar o artigo Práticas recomendadas para usar gateways de saída da malha de serviços na nuvem em clusters do GKE.

O público-alvo deste tutorial inclui engenheiros de rede, plataforma e segurança que administram clusters do Google Kubernetes Engine usados por uma ou mais equipas de entrega de software. Os controlos descritos aqui são especialmente úteis para organizações que têm de demonstrar a conformidade com os regulamentos, por exemplo, o RGPD e a PCI.

Configurar a infraestrutura

Crie uma rede de VPC e uma sub-rede

  1. Criar uma nova rede de VPC:

    gcloud compute networks create vpc-network \
        --subnet-mode custom
    
  2. Crie uma sub-rede para o cluster ser executado com intervalos de endereços IP secundários pré-atribuídos para pods e serviços. O acesso privado à Google está ativado para que as aplicações com apenas endereços IP internos possam alcançar as APIs e os serviços 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
    

Configure o Cloud NAT

O Cloud NAT permite que as cargas de trabalho sem endereços IP externos se liguem a destinos na Internet e recebam respostas de entrada desses destinos.

  1. Crie um Cloud Router:

    gcloud compute routers create nat-router \
        --network vpc-network
    
  2. Adicione uma configuração NAT ao router:

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

Crie contas de serviço para cada conjunto de nós do GKE

Crie duas contas de serviço para utilização pelos dois conjuntos de nós do GKE. É atribuída uma conta de serviço separada a cada conjunto de nós para que possa aplicar regras de firewall da VPC a nós específicos.

  1. Crie uma conta de serviço para utilização pelos nós no node pool predefinido:

    gcloud iam service-accounts create sa-application-nodes \
        --description="SA for application nodes" \
        --display-name="sa-application-nodes"
    
  2. Crie uma conta de serviço para utilização pelos nós no node pool do gateway:

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

Conceda autorizações às contas de serviço

Adicione um conjunto mínimo de funções de IAM às contas de serviço da aplicação e da gateway. Estas funções são necessárias para o registo, a monitorização e a obtenção de imagens de contentores privados do 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

Criar as regras de firewall

Nos passos seguintes, aplica uma regra de firewall à rede VPC para que, por predefinição, todo o tráfego de saída seja recusado. É necessária uma conetividade específica para o cluster funcionar e para os nós de gateway poderem alcançar destinos fora da VPC. Um conjunto mínimo de regras de firewall específicas substitui a regra de recusa de tudo predefinida para permitir a conetividade necessária.

  1. Crie uma regra de firewall predefinida (prioridade baixa) para negar toda a saída da rede 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. Crie uma regra para permitir que apenas os nós com a conta do serviço de gateway acedam à 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. Permita que os nós alcancem o plano de controlo do 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 regra de firewall não é necessária se usar o Managed Cloud Service Mesh.

    O Cloud Service Mesh usa webhooks quando injeta proxies sidecar em cargas de trabalho. Permita que o servidor da API GKE chame os webhooks expostos pelo plano de controlo da malha de serviços em execução nos nós:

    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. Permita a conetividade de saída entre os nós e os pods em execução no cluster. O GKE cria automaticamente uma regra de entrada correspondente. Não é necessária nenhuma regra para a conetividade do serviço, porque a cadeia de encaminhamento iptables converte sempre os endereços IP do serviço em endereços IP do 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. Permita o acesso aos conjuntos reservados de endereços IP usados pelo acesso privado Google para publicar APIs Google, pelo Container Registry e por outros serviços:

    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. Permita que o serviço de verificação de saúde Google Cloud aceda aos pods em execução no cluster. Consulte as verificações de estado: para mais informações.

    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"
    

Configurar o acesso privado às Google Cloud APIs

O acesso privado à Google permite que as VMs e os pods que só têm endereços IP internos acedam às APIs e aos serviços Google. Embora as APIs e os serviços Google sejam fornecidos a partir de IPs externos, o tráfego dos nós nunca sai da rede Google quando usa o acesso privado à Google.

Ative a API Cloud DNS:

gcloud services enable dns.googleapis.com

Crie uma zona de DNS privada e registos CNAME e A para que os nós e as cargas de trabalho possam ligar-se às APIs e aos serviços Google através do acesso privado à Google e do nome do anfitrião 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

Configurar o acesso privado ao Container Registry

Crie uma zona DNS privada, um registo CNAME e um registo A para que os nós possam estabelecer ligação ao Container Registry através do acesso privado da Google e do nome de anfitrião 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

Crie um cluster privado do GKE

  1. Encontre o endereço IP externo do Cloud Shell para que o possa adicionar à lista de redes autorizadas a aceder ao servidor da API do cluster:

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

    Após um período de inatividade, o endereço IP externo da sua VM do Cloud Shell pode mudar. Se isso acontecer, tem de atualizar a lista de redes autorizadas do cluster. Adicione o seguinte comando ao script de inicialização:

    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. Ative a API Google Kubernetes Engine:

    gcloud services enable container.googleapis.com
    
  3. Crie um cluster privado do GKE:

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

    A criação do cluster demora alguns minutos. O cluster tem nós privados com endereços IP internos. Os pods e os serviços são atribuídos a IPs dos intervalos secundários com nomes que definiu quando criou a sub-rede da VPC.

    A Cloud Service Mesh com um plano de controlo no cluster requer que os nós do cluster usem um tipo de máquina com, pelo menos, 4 vCPUs.

    A Google recomenda que o cluster esteja subscrito no canal de lançamento "regular" para garantir que os nós estão a executar uma versão do Kubernetes suportada pelo Cloud Service Mesh.

    Para mais informações sobre os pré-requisitos para executar o Cloud Service Mesh com um plano de controlo no cluster, consulte os pré-requisitos no cluster.

    Para mais informações sobre os requisitos e as limitações da execução do Cloud Service Mesh gerido, consulte as funcionalidades suportadas do Cloud Service Mesh gerido.

    A federação de identidade da carga de trabalho para o GKE está ativada no cluster. O Cloud Service Mesh requer a Workload Identity Federation para o GKE e é a forma recomendada de aceder às APIs Google a partir de cargas de trabalho do GKE.

  4. Crie um node pool denominado gateway. É neste conjunto de nós que o gateway de saída é implementado. O dedicated=gateway:NoSchedule taint é adicionado a todos os nós no conjunto de nós do gateway.

    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"
    

    As restrições e as tolerâncias do Kubernetes ajudam a garantir que apenas os pods do gateway de saída são executados em nós no conjunto de nós do gateway.

  5. Transfira as credenciais para poder estabelecer ligação ao cluster com o kubectl:

    gcloud container clusters get-credentials cluster1
    
  6. Verifique se os nós de gateway têm a rejeição correta:

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

    O resultado é semelhante ao seguinte:

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

Instalar e configurar o Cloud Service Mesh

Siga um dos guias de instalação do Cloud Service Mesh:

Depois de instalar o Cloud Service Mesh, pare e regresse a este tutorial sem instalar gateways de entrada ou saída.

Instale um gateway de saída

  1. Crie um espaço de nomes do Kubernetes para o gateway de saída:

    kubectl create namespace istio-egress
    
  2. Ative o espaço de nomes para injeção. Os passos dependem da sua implementação do plano de controlo.

    Gerido (TD)

    Aplique a etiqueta de injeção predefinida ao espaço de nomes:

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

    Gerido (Istiod)

    Recomendado: execute o seguinte comando para aplicar a etiqueta de injeção predefinida ao espaço de nomes:

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

    Se for um utilizador existente com o plano de controlo do Istiod gerido: recomendamos que use a injeção predefinida, mas a injeção baseada em revisões é suportada. Siga as instruções abaixo:

    1. Execute o seguinte comando para localizar os canais de lançamento disponíveis:

      kubectl -n istio-system get controlplanerevision
      

      O resultado é semelhante ao seguinte:

      NAME                AGE
      asm-managed-rapid   6d7h
      

      Na saída, o valor na coluna NAME é a etiqueta de revisão que corresponde ao canal de lançamento disponível para a versão do Cloud Service Mesh.

    2. Aplique a etiqueta de revisão ao espaço de nomes:

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

    No cluster

    Recomendado: execute o seguinte comando para aplicar a etiqueta de injeção predefinida ao espaço de nomes:

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

    Recomendamos que use a injeção predefinida, mas a injeção baseada em revisões é suportada: siga estas instruções:

    1. Use o seguinte comando para localizar a etiqueta de revisão em istiod:

      kubectl get deploy -n istio-system -l app=istiod -o \
         jsonpath={.items[*].metadata.labels.'istio\.io\/rev'}'{"\n"}'
      
    2. Aplique a etiqueta de revisão ao espaço de nomes. No comando seguinte, REVISION_LABEL é o valor da etiqueta de revisão istiod que anotou no passo anterior.

      kubectl label namespace istio-egress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      
  3. Crie um manifesto do operador para o gateway de saída:

    cat << EOF > egressgateway-operator.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: egressgateway-operator
      annotations:
        config.kubernetes.io/local-config: "true"
    spec:
      profile: empty
      revision: REVISION
      components:
        egressGateways:
        - name: istio-egressgateway
          namespace: istio-egress
          enabled: true
      values:
        gateways:
          istio-egressgateway:
            injectionTemplate: gateway
            tolerations:
              - key: "dedicated"
                operator: "Equal"
                value: "gateway"
            nodeSelector:
              cloud.google.com/gke-nodepool: "gateway"
    EOF
    
  4. Transfira a ferramenta istioctl. Tem de usar a versão 1.16.2-asm.2 ou mais recente, mesmo que esteja a usar a versão 1.15 ou inferior do Cloud Service Mesh. Consulte o artigo Transferir a versão correta do istioctl.

  5. Depois de extrair o arquivo transferido, defina uma variável de ambiente para guardar o caminho para a ferramenta istioctl e adicione-o ao script de inicialização:

    ISTIOCTL=$(find "$(pwd -P)" -name istioctl)
    echo "ISTIOCTL=\"${ISTIOCTL}\"" >> ./init-egress-tutorial.sh
    
  6. Crie o manifesto de instalação do gateway de saída com o manifesto do operador e istioctl:

    ${ISTIOCTL} manifest generate \
        --filename egressgateway-operator.yaml \
        --output egressgateway \
        --cluster-specific
    
  7. Instale o gateway de saída:

    kubectl apply --recursive --filename egressgateway/
    
  8. Verifique se o gateway de saída está a ser executado em nós no conjunto de nós gateway:

    kubectl get pods -n istio-egress -o wide
    
  9. Os pods do gateway de saída têm affinity para nós no conjunto de nós gateway e uma tolerância que lhes permite serem executados nos nós do gateway contaminados. Examine a afinidade de nós e as tolerâncias para os pods do gateway de saída:

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

    O resultado é semelhante ao seguinte:

    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]
    

Ative o registo de acesso do Envoy

Os passos necessários para ativar os registos de acesso do Envoy dependem do tipo de Cloud Service Mesh, gerido ou no cluster:

Preparar a malha e uma aplicação de teste

  1. Certifique-se de que o TLS mútuo STRICT está ativado. Aplique uma política PeerAuthentication predefinida para a malha no espaço de nomes 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
    

    Pode substituir esta configuração criando PeerAuthentication recursos em espaços de nomes específicos.

  2. Crie espaços de nomes para usar na implementação de cargas de trabalho de teste. Os passos posteriores neste tutorial explicam como configurar diferentes regras de encaminhamento de saída para cada espaço de nomes.

    kubectl create namespace team-x
    kubectl create namespace team-y
    
  3. Etiquete os espaços de nomes para que possam ser selecionados pelas políticas de rede do Kubernetes:

    kubectl label namespace team-x team=x
    kubectl label namespace team-y team=y
    
  4. Para que o Cloud Service Mesh injete automaticamente sidecars de proxy, tem de definir a etiqueta de revisão do plano de controlo nos espaços de nomes da carga de trabalho:

    kubectl label ns team-x istio.io/rev- istio-injection=enabled --overwrite
    kubectl label ns team-y istio.io/rev- istio-injection=enabled --overwrite
    
  5. Crie um ficheiro YAML para usar na criação de implementações de teste:

    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. Implemente a aplicação de teste no espaço de nomes team-x:

    kubectl -n team-x create -f ./test.yaml
    
  7. Verifique se a aplicação de teste está implementada num nó no conjunto predefinido e se é injetado um contentor sidecar de proxy. Repita o seguinte comando até que o estado do pod seja Running:

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

    O resultado é semelhante ao seguinte:

    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 contentores estão Running. Um contentor é a aplicação de teste e o outro é o sidecar do proxy.

    O pod está a ser executado num nó no conjunto de nós predefinido.

  8. Verifique se não é possível fazer um pedido HTTP do contentor de teste para um site 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
    

    É gerada uma mensagem de erro do proxy sidecar porque a regra de firewall global-deny-egress-all nega a ligação a montante.

Usar o recurso Sidecar para restringir o âmbito da configuração do proxy sidecar

Pode usar o recurso Sidecar para restringir o âmbito do ouvinte de saída configurado para proxies sidecar. Para reduzir o inchaço da configuração e a utilização de memória, é uma boa prática aplicar um recurso Sidecar predefinido para cada espaço de nomes.

O proxy que o Cloud Service Mesh executa no sidecar é o Envoy. Na terminologia do Envoy, um cluster é um grupo logicamente semelhante de pontos finais a montante usado como destinos para o equilíbrio de carga.

  1. Inspecione os clusters de saída configurados no proxy sidecar do Envoy para o pod de teste executando o 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
    

    Existem aproximadamente 11 clusters do Envoy na lista, incluindo alguns para o gateway de saída.

  2. Restrinja a configuração do proxy a rotas de saída que foram explicitamente definidas com entradas de serviço nos espaços de nomes de saída e team-x. Aplique um recurso Sidecar ao espaço de nomes 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
    

    A definição do modo de política de tráfego de saída para REGISTRY_ONLY restringe a configuração do proxy para incluir apenas os anfitriões externos que foram adicionados explicitamente ao registo de serviços da malha através da definição de entradas de serviço.

    A definição de egress.hosts especifica que o proxy sidecar só seleciona rotas do espaço de nomes de saída que são disponibilizadas através do atributo exportTo. A parte "team-x/*" inclui todas as rotas que foram configuradas localmente no espaço de nomes team-x.

  3. Veja os clusters de saída configurados no proxy sidecar do Envoy e compare-os com a lista de clusters que foram configurados antes de aplicar o recurso Sidecar:

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

    São apresentados clusters para o gateway de saída e um para o próprio pod de teste.

Configurar o Cloud Service Mesh para encaminhar o tráfego através do gateway de saída

  1. Configure um Gateway para tráfego HTTP na porta 80. O Gateway seleciona o proxy de gateway de saída que implementou no namespace de saída. A configuração Gateway é aplicada ao espaço de nomes de saída e processa o tráfego de qualquer anfitrião.

    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. Crie um DestinationRule para o gateway de saída com TLS mútuo para autenticação e encriptação. Use uma única regra de destino partilhada para todos os anfitriões 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. Crie um ServiceEntry no espaço de nomes de saída para registar explicitamente o example.com no registo de serviços da malha para o espaço de nomes team-x:

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: example-com-ext
      namespace: istio-egress
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: example.com
    spec:
      hosts:
      - example.com
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'team-x'
      - 'istio-egress'
    EOF
    
  4. Crie um VirtualService para encaminhar o tráfego para example.com através do gateway de saída. Existem duas condições de correspondência: a primeira condição direciona o tráfego para o gateway de saída e a segunda direciona o tráfego do gateway de saída para o anfitrião de destino. A propriedade exportTo controla que espaços de nomes podem usar o serviço 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. Execute istioctl analyze para verificar se existem erros de configuração:

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

    O resultado é semelhante ao seguinte:

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. Envie vários pedidos através do gateway de saída para o site 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
    

    Vê códigos de estado 200 para todas as quatro respostas.

  7. Verifique se os pedidos foram encaminhados através do gateway de saída, verificando os registos de acesso do proxy. Primeiro, verifique o registo de acesso do sidecar do proxy implementado com a aplicação de teste:

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

    Para cada pedido que enviar, é apresentada uma entrada de registo semelhante à seguinte:

    [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. Verifique também o registo de acesso do gateway de saída:

    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 pedido que enviar, vê uma entrada do registo de acesso do gateway de saída semelhante à seguinte:

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

Configure um encaminhamento diferente para um segundo espaço de nomes

Configure o encaminhamento para um segundo anfitrião externo para saber como a conetividade externa diferente pode ser configurada para diferentes equipas.

  1. Crie um recurso Sidecar para o espaço de nomes 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. Implemente a aplicação de teste no espaço de nomes team-y:

    kubectl -n team-y create -f ./test.yaml
    
  3. Registe um segundo anfitrião externo e exporte-o para o espaço de nomes team-x e 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. Crie um serviço virtual para encaminhar o tráfego para httpbin.org através do gateway de saída:

    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. Execute istioctl analyze para verificar se existem erros de configuração:

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

    Você vê:

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. Faça um pedido a httpbin.org a partir da app de teste 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 uma resposta 200 OK.

  7. Faça também um pedido a httpbin.org a partir da app de teste 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 uma resposta 200 OK.

  8. Tentar fazer um pedido a example.com a partir do espaço de nomes 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
    

    O pedido falha porque não existe uma rota de saída configurada para o anfitrião example.com.

Usar a política de autorização para fornecer controlo adicional sobre o tráfego

Neste tutorial, as políticas de autorização para o gateway de saída são criadas no espaço de nomes istio-egress. Pode configurar o RBAC do Kubernetes para que apenas os administradores de rede possam aceder ao espaço de nomes istio-egress.

  1. Crie um AuthorizationPolicy para que as aplicações no espaço de nomes team-x possam estabelecer ligação a example.com, mas não a outros anfitriões externos quando enviam pedidos através da porta 80. A porta targetPort correspondente nos pods do gateway de saída é 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. Verifique se pode fazer um pedido a example.com a partir da aplicação de teste no espaço de nomes 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 uma resposta 200 OK.

  3. Tente fazer um pedido a httpbin.org a partir da aplicação de teste no espaço de nomes 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
    

    O pedido falha com uma mensagem RBAC: access denied e um código de estado 403 Forbidden. Pode ter de aguardar alguns segundos porque existe, muitas vezes, um pequeno atraso antes de a política de autorização entrar em vigor.

  4. As políticas de autorização oferecem um controlo avançado sobre o tráfego que é permitido ou recusado. Aplique a seguinte política de autorização para permitir que a app de teste no espaço de nomes team-y faça pedidos a httpbin.org através de um caminho de URL específico quando envia pedidos através da porta 80. A porta targetPort correspondente nos pods do gateway de saída é 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. Tente estabelecer ligação a httpbin.org a partir da app de teste no espaço de nomes 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
    

    O pedido falha com uma mensagem RBAC: acesso recusado e um código de estado 403 Proibido.

  6. Agora, faça um pedido a httpbin.org/status/418 a partir da mesma app:

    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
    

    O pedido é bem-sucedido porque o caminho corresponde ao padrão na política de autorização. O resultado é semelhante ao seguinte:

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

Originação de TLS no gateway de saída

Pode configurar gateways de saída para upgrade (originar) pedidos HTTP simples para TLS ou TLS mútuo. Permitir que as aplicações façam pedidos HTTP simples tem várias vantagens quando usado com o TLS mútuo do Istio e a origem do TLS. Para mais informações, consulte o guia de práticas recomendadas.

Originação de TLS no gateway de saída

  1. Create a DestinationRule. The DestinationRule especifica que o gateway origina uma ligação 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. Atualize o serviço virtual para example.com de modo que os pedidos à porta 80 no gateway sejam upgraded para TLS na porta 443 quando forem enviados para o anfitrião 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. Faça vários pedidos a example.com a partir da app de teste no espaço de nomes 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
    

    Tal como antes, os pedidos são bem-sucedidos com 200 OK respostas.

  4. Verifique o registo do gateway de saída para confirmar se o gateway encaminhou os pedidos para o anfitrião de destino ao originar ligações TLS:

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

    O resultado é semelhante ao seguinte:

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

    O sidecar do proxy enviou o pedido para o gateway através da porta 80 e o TLS foi originado na porta 443 para enviar o pedido para o anfitrião de destino.

Encaminhamento de ligações HTTPS/TLS

As suas aplicações existentes podem já estar a usar ligações TLS quando comunicam com serviços externos. Pode configurar o gateway de saída para transmitir ligações TLS sem as desencriptar.

tls pass through

  1. Modifique a configuração para que o gateway de saída use a passagem direta de TLS para ligações à porta 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. Atualize o DestinationRule que aponta para o gateway de saída para adicionar um segundo subconjunto para a porta 443 no gateway. Este novo subconjunto não usa TLS mútuo. O TLS mútuo do Istio não é suportado para a transferência de ligações TLS. As ligações na porta 80 continuam a usar 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. Atualize o serviço virtual para example.com de modo que o tráfego TLS na porta 443 seja transmitido através do gateway:

    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. Atualize o serviço virtual para httpbin.org de modo que o tráfego TLS na porta 443 seja transmitido através do gateway:

    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. Adicione uma política de autorização que aceite qualquer tipo de tráfego enviado para a porta 443 do serviço de gateway de saída. A porta targetPort correspondente nos pods do gateway é 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. Execute istioctl analyze para verificar se existem erros de configuração:

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

    Você vê:

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  7. Faça um pedido HTTP simples a example.com a partir da aplicação de teste no team-xnamespace:

    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
    

    O pedido é bem-sucedido com uma resposta 200 OK.

  8. Agora, faça vários pedidos TLS (HTTPS) a partir da aplicação de teste no espaço de nomes 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
    

    Vê 200 respostas.

  9. Veja novamente o registo do gateway de saída:

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

    São apresentadas entradas do registo semelhantes às seguintes:

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

    O pedido HTTPS foi tratado como tráfego TCP e transmitido através do gateway para o anfitrião de destino, pelo que não são incluídas informações HTTP no registo.

Usar a NetworkPolicy do Kubernetes como um controlo adicional

Existem muitos cenários em que uma aplicação pode ignorar um proxy sidecar. Pode usar o Kubernetes NetworkPolicy para especificar adicionalmente as ligações que as cargas de trabalho podem estabelecer. Depois de aplicar uma única política de rede, todas as ligações que não sejam especificamente permitidas são recusadas.

Este tutorial considera apenas as ligações de saída e os seletores de saída para as políticas de rede. Se controlar a entrada com políticas de rede nos seus próprios clusters, tem de criar políticas de entrada que correspondam às suas políticas de saída. Por exemplo, se permitir a saída de cargas de trabalho do espaço de nomes team-x para o espaço de nomes team-y, também tem de permitir a entrada no espaço de nomes team-y a partir do espaço de nomes team-x.

  1. Permita que as cargas de trabalho e os proxies implementados no espaço de nomes team-x estabeleçam ligação a istiod e à gateway de saída:

    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. Permita que as cargas de trabalho e os proxies consultem o 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. Permita que as cargas de trabalho e os proxies se liguem aos IPs que fornecem APIs e serviços Google, incluindo a autoridade de certificação do 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. Permita que as cargas de trabalho e os proxies se liguem ao servidor de metadados do 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: permita que as cargas de trabalho e os proxies no espaço de nomes team-x estabeleçam ligações entre si:

    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: permita que as cargas de trabalho e os proxies no espaço de nomes team-x estabeleçam ligações a cargas de trabalho implementadas por uma equipa 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. As ligações entre proxies sidecar persistem. As ligações existentes não são fechadas quando aplica uma nova política de rede. Reinicie as cargas de trabalho no espaço de nomes team-x para garantir que as associações existentes são fechadas:

    kubectl -n team-x rollout restart deployment
    
  8. Verifique se ainda pode fazer um pedido HTTP para example.com a partir da aplicação de teste no espaço de nomes 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
    

    O pedido é bem-sucedido com uma resposta 200 OK.

Aceder diretamente às APIs Google através do acesso privado Google e das autorizações da IAM

As APIs e os serviços da Google são expostos através de endereços IP externos. Quando os pods com endereços IP de alias nativos da VPC estabelecem ligações às APIs Google através do acesso privado à Google, o tráfego nunca sai da rede da Google.

Quando configurou a infraestrutura para este tutorial, ativou o acesso privado à Google para a sub-rede usada pelos pods do GKE. Para permitir o acesso aos endereços IP usados pelo acesso privado à Google, criou uma rota, uma regra de firewall da VPC e uma zona DNS privada. Esta configuração permite que os pods alcancem as APIs Google diretamente sem enviar tráfego através do gateway de saída. Pode controlar que APIs estão disponíveis para contas de serviço do Kubernetes específicas (e, por conseguinte, espaços de nomes) através da Workload Identity Federation para o GKE e do IAM. A autorização do Istio não tem efeito porque o gateway de saída não está a processar as ligações às APIs Google.

Antes de os pods poderem chamar as APIs Google, tem de usar o IAM para conceder autorizações. O cluster que está a usar para este tutorial está configurado para usar a Workload Identity Federation para o GKE, o que permite que uma conta de serviço do Kubernetes atue como uma conta de serviço Google.

  1. Crie uma conta de serviço Google para a sua aplicação usar:

    gcloud iam service-accounts create sa-test-app-team-x
    
  2. Permita que a conta de serviço do Kubernetes se faça passar pela conta de serviço da 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. Anote a conta de serviço do Kubernetes para a app de teste no espaço de nomes team-x com o endereço de email da conta de serviço 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. O pod da aplicação de teste tem de conseguir aceder ao servidor de metadados da Google (executado como um DaemonSet) para obter credenciais temporárias para chamar as APIs Google. Crie uma entrada de serviço para o servidor de metadados do 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. Crie também uma entrada de serviço para private.googleapis.com e 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. Verifique se a conta de serviço do Kubernetes está configurada corretamente para agir como a conta de serviço 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
    

    Vê a conta de serviço Google apresentada como a identidade ativa e única.

  7. Crie um ficheiro de teste num contentor do 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. Conceda autorização à conta de serviço para listar e ver ficheiros no contentor:

    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. Verifique se a aplicação de teste consegue aceder ao contentor de teste:

    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
    

    Você vê:

    Hello, World!