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


Neste tutorial, mostramos como usar os gateways de saída do Cloud Service Mesh e outros controles do Google Cloud para proteger o tráfego de saída (saída) das cargas de trabalho implantadas em um cluster do Google Kubernetes Engine. O tutorial serve como complemento das Práticas recomendadas para usar gateways de saída do Cloud Service Mesh em clusters do GKE.

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

Objetivos

  • Configure a infraestrutura para executar o Cloud Service Mesh:
  • Instale o Cloud Service Mesh.
  • Instale proxies de gateway de saída em execução em um pool de nós dedicado.
  • Configure regras de roteamento de vários locatários para o tráfego externo por meio do gateway de saída:
    • Os aplicativos no namespace team-x podem se conectar a example.com
    • Os aplicativos no namespace team-y podem se conectar a httpbin.org
  • Use o recurso Sidecar para restringir o escopo da configuração de saída do proxy sidecar para cada namespace.
  • Configure políticas de autorização para aplicar regras de saída.
  • Configure o gateway de saída para fazer upgrade de solicitações HTTP simples para a TLS (origem de TLS).
  • Configure o gateway de saída para transmitir o tráfego TLS de passagem.
  • Configure as políticas de rede do Kubernetes como um controle de saída adicional.
  • Configure o acesso direto às APIs do Google usando permissões de Acesso privado do Google e de Gerenciamento de identidade e acesso (IAM).

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Ao concluir este tutorial, exclua os recursos criados para evitar custos contínuos. Para mais informações, consulte Como fazer a limpeza.

Antes de começar

  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. Crie um diretório de trabalho para usar e siga este tutorial:

    mkdir -p ~/WORKING_DIRECTORY
    cd ~/WORKING_DIRECTORY
    
  5. Crie um script de shell para inicializar seu ambiente para o tutorial. Substitua e edite as variáveis de acordo com o projeto e as preferências. Execute este script com o comando source para reinicializar o ambiente se a sessão de shell expirar.

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

    gcloud services enable compute.googleapis.com --project=YOUR_PROJECT_ID
    
  7. Torne o script executável e o execute com o comando source para inicializar seu ambiente. Selecione Y se for necessário ativar compute.googleapis.com:

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

Como configurar a infraestrutura

Crie uma rede e uma sub-rede VPC

  1. Crie uma nova rede VPC:

    gcloud compute networks create vpc-network \
        --subnet-mode custom
    
  2. Crie uma sub-rede para o cluster executar em intervalos com endereços IP secundários pré-atribuídos para pods e serviços. O Acesso privado do Google é ativado para que os aplicativos apenas com endereços IP internos possam acessar as APIs e os serviços do 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
    

Configurar o Cloud NAT

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

  1. Criar um Cloud Router

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

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

Criar contas de serviço para cada pool de nós do GKE

Crie duas contas de serviço para uso pelos dois pools de nós do GKE. Uma conta de serviço separada é atribuída a cada pool de nós para que você possa aplicar regras de firewall VPC a nós específicos.

  1. Crie uma conta de serviço para ser usada pelos nós no pool de nós padrão:

    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 ser usada pelos nós no pool de nós de gateway:

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

Conceder permissões às contas de serviço

Adicione um conjunto mínimo de papéis do IAM às contas de serviço do aplicativo e do gateway. Esses papéis são necessários para registrar, monitorar e extrair imagens de contêiner particular 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

Como criar as regras de firewall

Nas etapas a seguir, você aplica uma regra de firewall à rede VPC para que, por padrão, todo o tráfego de saída seja negado. É necessária uma conectividade específica para que o cluster funcione e os nós de gateway possam alcançar destinos fora da VPC. Um conjunto mínimo de regras de firewall específicas substitui a regra padrão de negação de tudo para permitir a conectividade necessária.

  1. Crie uma regra de firewall padrão (de baixa prioridade) para negar todas as saídas 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 de serviço de gateway cheguem à 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 cheguem ao plano de controle 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: essa regra de firewall não será necessária se você usar o Cloud Service Mesh gerenciado.

    O Cloud Service Mesh usa webhooks ao injetar proxies sidecar em cargas de trabalho. Permita que o servidor da API GKE chame os webhooks expostos pelo plano de controle da malha de serviço 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 conectividade de saída entre os nós e os pods em execução no cluster. O GKE cria automaticamente uma regra de entrada correspondente. Nenhuma regra é necessária para conectividade de serviço porque a cadeia de roteamento do iptables sempre converte endereços IP de serviço em endereços 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. Permita o acesso aos conjuntos de endereços IP usados pelo Acesso privado do Google para disponibilizar APIs do Google, o Container Registry e 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 do verificador de integridade do Google Cloud acesse os pods em execução no cluster. Consulte para mais informações em verificações de integridade.

    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"
    

Como configurar o acesso particular às APIs do Google Cloud

O Acesso privado do Google permite que VMs e pods que tenham apenas endereços IP internos acessem APIs e serviços do Google. As APIs e os serviços do Google são exibidos do IPs externos, mas o tráfego dos nós nunca sai da rede do Google quando o Acesso privado do Google é usado.

Ative a API Cloud DNS:

gcloud services enable dns.googleapis.com

Crie uma zona DNS particular, um CNAME e registros A para que os nós e as cargas de trabalho possam se conectar a APIs e serviços do Google usando o Acesso privado do Google e o nome do 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

Como configurar o acesso particular ao Container Registry

Crie uma zona DNS particular, um CNAME e registros A para que os nós possam se conectar ao Container Registry usando o Acesso privado do Google e o nome do 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

Criar um cluster do GKE particular

  1. Encontre o endereço IP externo do seu Cloud Shell para que você possa adicioná-lo à lista de redes que podem acessar o 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 VM do Cloud Shell pode mudar. Se isso acontecer, você precisará atualizar a lista de redes autorizadas do cluster. Adicione o seguinte comando ao seu 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. Criar um cluster do GKE particular

    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}
    

    O cluster leva alguns minutos para ser criado. O cluster tem nós particulares com endereços IP internos. Os pods e serviços recebem os IPs dos intervalos secundários nomeados que você definiu ao criar a sub-rede VPC.

    O Cloud Service Mesh com um plano de controle no cluster requer que os nós do cluster usem um tipo de máquina com pelo menos quatro vCPUs.

    O Google recomenda que o cluster seja inscrito no canal de lançamento "regular" para garantir que os nós estejam executando uma versão do Kubernetes compatível com o Cloud Service Mesh.

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

    Para mais informações sobre os requisitos e as limitações para executar o Cloud Service Mesh gerenciado, consulte os Recursos compatíveis do Cloud Service Mesh gerenciado.

    A federação de identidade da carga de trabalho do GKE está ativada no cluster. O Cloud Service Mesh requer a federação de identidade da carga de trabalho para o GKE e é a maneira recomendada de acessar as APIs do Google nas cargas de trabalho do GKE.

  4. Crie um pool de nós chamado gateway. Esse pool de nós é onde o gateway de saída é implantado. O taint dedicated=gateway:NoSchedule é adicionado a cada nó no pool de nós de 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"
    

    Os taints e as tolerâncias do Kubernetes ajudam a garantir que somente os pods do gateway de saída sejam executados nos nós do pool.

  5. Faça o download de credenciais para se conectar ao cluster com a kubectl:

    gcloud container clusters get-credentials cluster1
    
  6. Verifique se os nós de gateway têm o taint correto:

    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 será assim:

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

Como 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 retorne a este tutorial sem instalar gateways de entrada ou saída.

Instalar um gateway de saída

  1. Crie um namespace do Kubernetes para o gateway de saída:

    kubectl create namespace istio-egress
    
  2. Quando você implanta o gateway de saída, a configuração é injetada automaticamente com base em um identificador aplicado à implantação ou ao namespace. Se a tag padrão estiver configurada, você definirá o namespace com os identificadores de injeção padrão ou usará o identificador de revisão no plano de controle instalado. O identificador adicionado também depende de você ter implantado o Cloud Service Mesh gerenciado ou instalado o plano de controle no cluster.

    Selecione a guia abaixo de acordo com o tipo de instalação (gerenciada ou em cluster).

    Gerenciado

    Use o comando a seguir para localizar as revisões do plano de controle disponíveis:

    kubectl -n istio-system get controlplanerevision
    

    O resultado será assim:

    NAME          RECONCILED   STALLED   AGE
    asm-managed   True         False     112m
    

    Anote o valor na coluna NAME da revisão do plano de controle que você quer usar. Normalmente, o canal de lançamento do Cloud Service Mesh corresponde ao canal de lançamento do seu cluster do Google Kubernetes Engine.

    No cluster

    Em planos de controle no cluster, o Serviço e a Implantação istiod normalmente têm um rótulo de revisão semelhante a istio.io/rev=, em que identifica a versão do Cloud Service Mesh. A revisão se torna parte do nome do Serviço istiod, por exemplo, istiod-.istio-system

    Use o comando a seguir para localizar o rótulo de revisão em istiod para o plano de controle no cluster:

    kubectl get deploy -n istio-system -l app=istiod \
      -o=jsonpath='{.items[*].metadata.labels.istio\.io\/rev}''{"\n"}'
    
  3. Opcional: rotule o namespace para que a configuração do gateway seja injetada automaticamente. É suficiente rotular o namespace ou a implantação. Para este tutorial, rotule os dois para evitar avisos da ferramenta istioctl analyze.

    kubectl label namespace istio-egress istio.io/rev=REVISION
    
  4. 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
    
  5. Faça o download da ferramenta istioctl. É necessário usar a versão 1.16.2-asm.2 ou mais recente, mesmo que você esteja usando a versão 1.15 ou anterior do Cloud Service Mesh. Consulte Como fazer o download da versão correta do istioctl.

  6. Depois de extrair o arquivo do download, defina uma variável de ambiente para manter o caminho para a ferramenta istioctl e adicioná-lo ao script de inicialização:

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

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

    kubectl apply --recursive --filename egressgateway/
    
  9. Verifique se o gateway de saída está em execução nos nós no pool de nós gateway:

    kubectl get pods -n istio-egress -o wide
    
  10. Os pods do gateway de saída têm affinity para os nós no pool de nós gateway e uma tolerância que permite a execução nos nós do gateway com taint. Examine a afinidade e as tolerâncias dos nós 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 será assim:

    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]
    

Ativar a geração de registros de acesso do Envoy

As etapas necessárias para ativar os registros de acesso do Envoy dependem do tipo de Cloud Service Mesh, gerenciado ou no cluster:

Como preparar a malha e um aplicativo de teste

  1. Verifique se a TLS mútuo do STRICT está ativada. Aplique uma política de PeerAuthentication padrão para a malha no namespace 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
    

    É possível modificar essa configuração criando recursos PeerAuthentication em namespaces específicos.

  2. Crie namespaces a serem usados para implantar cargas de trabalho de teste. As etapas posteriores neste tutorial explicam como configurar diferentes regras de roteamento de saída para cada namespace.

    kubectl create namespace team-x
    kubectl create namespace team-y
    
  3. Rotule os namespaces para que eles 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 proxies sidecar, defina o identificador da revisão do plano de controle nos namespaces da carga de trabalho:

    kubectl label ns team-x istio.io/rev=REVISION
    kubectl label ns team-y istio.io/rev=REVISION
    
  5. Crie um arquivo YAML a ser usado para fazer implantaçõ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. Implante o aplicativo de teste no namespace team-x:

    kubectl -n team-x create -f ./test.yaml
    
  7. Verifique se o aplicativo de teste está implantado em um nó no pool padrão e se um contêiner de arquivo secundário de proxy está injetado. Repita o comando a seguir até o status do pod ser Running:

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

    A resposta será semelhante a:

    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
    

    Dois de dois contêineres são Running. Um contêiner é o aplicativo de teste, e o outro é o arquivo secundário do proxy.

    O pod está sendo executado em um nó no pool de nós padrão.

  8. Verifique se não é possível fazer uma solicitação HTTP do contêiner 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
    

    Uma mensagem de erro do proxy sidecar é gerada porque a regra de firewall global-deny-egress-all nega a conexão upstream.

Como usar o recurso do arquivo secundário para restringir o escopo da configuração de proxy sidcar

Use o recurso sidecar para restringir o escopo do listener de saída configurado para proxies sidecar. Para reduzir a ocupação excessiva da configuração e do uso da memória, é recomendável aplicar um recurso Sidecar padrão para cada namespace.

O proxy executado pelo Cloud Service Mesh no sidecar é o Envoy. Na terminologia Envoy, um cluster é um grupo logicamente similar de endpoints upstream usados como destinos para balanceamento 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
    

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

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

    Definir o modo de política de tráfego de saída como REGISTRY_ONLY restringe a configuração de proxy para incluir somente os hosts externos que foram explicitamente adicionados ao registro de serviço da malha definindo entradas de serviço.

    Definir egress.hosts especifica que o proxy sidecar seleciona apenas rotas do namespace de saída disponibilizadas usando o atributo exportTo. A parte "team-x/*" inclui todas as rotas que foram configuradas localmente no namespace team-x.

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

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

    Você verá clusters para o gateway de saída e um para o próprio pod de teste.

Como configurar o Cloud Service Mesh para rotear o tráfego pelo gateway de saída

  1. Configure um Gateway para tráfego HTTP na porta 80. O Gateway seleciona o proxy do gateway de saída que você implantou no namespace de saída. A configuração Gateway é aplicada ao namespace de saída e processa o tráfego para qualquer 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. Criar um DestinationRule para o gateway de saída com TLS mútua para autenticação e criptografia. Usar uma única regra de destino compartilhada para todos os 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. Crie um ServiceEntry no namespace de saída para registrar explicitamente example.com no registro de serviço da malha para o namespace 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 rotear o tráfego para example.com por meio do gateway de saída. Há 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 host de destino. A propriedade exportTo controla quais namespaces 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 há erros de configuração:

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

    A resposta será semelhante a:

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. Envie várias solicitações pelo gateway de saída ao 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
    

    Você verá 200 códigos de status para as quatro respostas.

  7. Verifique se as solicitações foram direcionadas por meio do gateway de saída. Para isso, confira os registros de acesso do proxy. Primeiro, verifique o registro de acesso para o proxy sidecar implantado com o aplicativo 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 solicitação enviada, você vê uma entrada de registro semelhante a esta:

    [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 registro 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 solicitação enviada, você vê uma entrada de registro de acesso ao gateway de saída semelhante a esta:

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

Configurar um roteamento diferente para um segundo namespace

Configure o roteamento para um segundo host externo e saiba como diferentes conectividades diferentes podem ser configuradas para equipes diferentes.

  1. Crie um recurso Sidecar para o namespace 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. Implante o aplicativo de teste no namespace team-y:

    kubectl -n team-y create -f ./test.yaml
    
  3. Registre um segundo host externo e exporte para o team-x e o namespace 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 rotear o tráfego para httpbin.org por meio 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 há erros de configuração:

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

    Você verá:

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. Faça uma solicitação para httpbin.org pelo 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
    

    Você verá uma resposta 200 OK.

  7. Faça também uma solicitação para httpbin.org do 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
    

    Você verá uma resposta 200 OK.

  8. Tente fazer uma solicitação para example.com pelo namespace 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
    

    A solicitação falha porque não há uma rota de saída configurada para o host example.com.

Como usar a política de autorização para fornecer mais controle sobre o tráfego

Neste tutorial, as políticas de autorização para o gateway de saída são criadas no namespace istio-egress. É possível configurar o RBAC do Kubernetes para que apenas administradores de rede possam acessar o namespace istio-egress.

  1. Crie um AuthorizationPolicy para que os aplicativos no namespace team-x possam se conectar a example.com, mas não a outros hosts externos ao enviar solicitações usando a porta 80. O 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 é possível fazer uma solicitação para example.com pelo aplicativo de teste no namespace 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
    

    Você verá uma resposta 200 OK.

  3. Tente fazer uma solicitação para httpbin.org pelo aplicativo de teste no namespace 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
    

    A solicitação falha com uma mensagem RBAC: access denied e um código de status "403 Forbidden". Aguarde alguns segundos, porque às vezes ocorre um pequeno atraso antes que a política de autorização entre em vigor.

  4. As políticas de autorização fornecem controle avançado sobre qual tráfego é permitido ou negado. Aplique a seguinte política de autorização para permitir que o app de teste no namespace team-y faça solicitações a httpbin.org usando um caminho de URL específico ao enviar solicitações usando a porta 80. O 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 se conectar a httpbin.org pelo app de teste no namespace 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
    

    A solicitação falha com uma mensagem de RBAC: acesso negado e um código de status "403 Forbidden".

  6. Agora, faça uma solicitação para httpbin.org/status/418 no mesmo 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
    

    A solicitação é bem-sucedida porque o caminho corresponde ao padrão na política de autorização. A resposta será semelhante a:

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

Origem da TLS no gateway de saída

É possível configurar gateways de saída para solicitações HTTP simples (upgrade) de origem para TLS ou TLS mútuo. Permitir que os aplicativos façam solicitações HTTP simples tem muitas vantagens quando usados com a TLS mútua do Istio e do TLS. Para mais informações, consulte o guia de práticas recomendadas.

Origem de TLS no gateway de saída

  1. Criar um DestinationRule. The DestinationRule especifica que o gateway originou uma conexão TLS com 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 de example.com para que as solicitações para a porta 80 no gateway sejam upgraded para TLS na porta 443 quando forem enviadas ao 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. Faça várias solicitações para example.com pelo app de teste no namespace 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, as solicitações são bem-sucedidas com respostas 200 OK.

  4. Verifique o registro do gateway de saída para verificar se o gateway roteou as solicitações para o host de destino originando conexõ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
    

    A resposta será semelhante a:

    [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 proxy sidecar enviou a solicitação para o gateway usando a porta 80 e a TLS originada na porta 443 para enviar a solicitação ao host de destino.

Passagem de conexões HTTPS/TLS

Talvez seus aplicativos atuais já estejam usando conexões TLS ao se comunicar com serviços externos. É possível configurar o gateway de saída para transmitir conexões TLS sem descriptografá-lo.

passagem de tls

  1. Modifique sua configuração para que o gateway de saída use a passagem TLS para conexões com a 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 e adicione um segundo subconjunto para a porta 443 no gateway. Esse novo subconjunto não usa TLS mútuo. O TLS mútuo do Istio não é compatível com a passagem de conexões TLS. As conexões na porta 80 ainda usam 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 para que o tráfego TLS na porta 443 seja transmitido por meio 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 para que o tráfego TLS na porta 443 seja transmitido por meio 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. O targetPort correspondente nos pods de 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 há erros de configuração:

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

    Você verá:

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  7. Faça uma solicitação HTTP simples para example.com do aplicativo de teste no namespace 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
    

    A solicitação é bem-sucedida com uma resposta 200 OK.

  8. Agora, faça várias solicitações TLS (HTTPS) do aplicativo de teste no namespace 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
    

    Você vê 200 respostas.

  9. Verifique o registro do gateway de saída novamente:

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

    Você verá entradas de registro semelhantes a estas:

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

    A solicitação HTTPS foi tratada como tráfego TCP e passada pelo gateway para o host de destino. Portanto, nenhuma informação HTTP está incluída no registro.

Como usar o Kubernetes NetworkPolicy como controle adicional

Há muitas situações em que um aplicativo pode ignorar um proxy sidecar. Use o Kubernetes NetworkPolicy para especificar ainda mais quais conexões podem ser feitas. Depois que uma única política de rede é aplicada, todas as conexões que não são especificamente permitidas são negadas.

Neste tutorial, são consideradas somente conexões de saída e seletores de saída para políticas de rede. Se você controlar a entrada com políticas de rede nos seus próprios clusters, precisará criar políticas de entrada para corresponder às suas políticas de saída. Por exemplo, se você permitir a saída de cargas de trabalho no namespace team-x para o namespace team-y, também precisará permitir a entrada para o namespace team-y do namespace team-x.

  1. Permita que cargas de trabalho e proxies implantados no namespace team-x se conectem a istiod e o 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. Permitir que cargas de trabalho e proxies consultem 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 cargas de trabalho e proxies se conectem aos IPs que disponibilizam APIs e serviços do 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 cargas de trabalho e proxies se conectem 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 cargas de trabalho e proxies no namespace team-x se conectem umas às outras:

    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 cargas de trabalho e proxies no namespace team-x se conectem a cargas de trabalho implantadas por outra equipe:

    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 conexões entre proxies sidecar permanecem. As conexões atuais não são encerradas quando você aplica uma nova política de rede. Reinicie as cargas de trabalho no namespace team-x para garantir que as conexões atuais sejam fechadas:

    kubectl -n team-x rollout restart deployment
    
  8. Verifique se ainda é possível fazer uma solicitação HTTP para example.com pelo aplicativo de teste no namespace 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
    

    A solicitação é bem-sucedida com uma resposta 200 OK.

Como acessar diretamente as APIs do Google usando o Acesso privado do Google e as permissões do IAM

As APIs e os serviços do Google são expostos usando endereços IP externos. Quando os pods com endereços IP de alias nativos de VPC fazem conexões com as APIs do Google usando o Acesso privado do Google, o tráfego nunca sai da rede do Google.

Ao configurar a infraestrutura para este tutorial, você ativou o Acesso privado do Google para a sub-rede usada pelos pods do GKE. Para permitir o acesso aos endereços IP usados pelo Acesso privado do Google, você criou uma rota, uma regra de firewall VPC e uma zona de DNS particular. Essa configuração permite que os pods alcancem as APIs do Google diretamente sem enviar tráfego por meio do gateway de saída. É possível controlar quais APIs estão disponíveis para contas de serviço específicas do Kubernetes e, portanto, namespaces, usando a federação de identidade da carga de trabalho do GKE e o IAM. A autorização do Istio não tem efeito porque o gateway de saída não processa conexões com as APIs do Google.

Antes que os pods chamem APIs do Google, você precisa usar o IAM para conceder permissões. O cluster que você está usando para este tutorial está configurado para usar a Federação de identidade da carga de trabalho para GKE, o que permite que uma conta de serviço do Kubernetes atue como uma conta de serviço do Google.

  1. Crie uma conta de serviço do Google para o aplicativo usar:

    gcloud iam service-accounts create sa-test-app-team-x
    
  2. Permita que a conta de serviço do Kubernetes personifique a conta de serviço do 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 app de teste no namespace team-x com o endereço de e-mail da conta de serviço do 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 do aplicativo de teste precisa acessar o servidor de metadados do Google, executado como um DaemonSet, para receber credenciais temporárias de chamada de APIs do 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 atuar como a conta de serviço do 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
    

    Você verá a conta de serviço do Google listada como a identidade ativa e única.

  7. Crie um arquivo de teste em um bucket 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 permissão à conta de serviço para listar e visualizar arquivos no 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. Verifique se o aplicativo de teste pode acessar o bucket 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ê verá:

    Hello, World!
    

Limpeza

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto que os contém ou mantenha o projeto e exclua os recursos individuais.

Para evitar cobranças sobre os recursos usados neste tutorial na conta do Google Cloud, conclua as etapas nas seções a seguir:

Exclua o projeto

A maneira mais fácil de evitar o faturamento é excluir o projeto criado para o tutorial.

  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.

A seguir