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


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

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 Anthos Service Mesh:
  • Instalar o Anthos Service Mesh com gateways 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. Torne o script executável e o execute com o comando source para inicializar seu ambiente:

    chmod +x ./init-egress-tutorial.sh
    source ./init-egress-tutorial.sh
    
  7. Defina os papéis necessários de gerenciamento de identidade e acesso (IAM, na sigla em inglês). Se você for um proprietário do projeto, terá todas as permissões necessárias para concluir a instalação. Se você não for um proprietário do projeto, peça ao administrador que conceda a você os papéis do IAM a seguir. No comando a seguir, altere PROJECT_EMAIL_ADDRESS para a conta que você usa para fazer login no Google Cloud.

    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member user:PROJECT_EMAIL_ADDRESS \
        --role=roles/editor \
        --role=roles/compute.admin \
        --role=roles/container.admin \
        --role=roles/resourcemanager.projectIamAdmin \
        --role=roles/iam.serviceAccountAdmin \
        --role=roles/iam.serviceAccountKeyAdmin \
        --role=roles/gkehub.admin \
        --role=roles/serviceusage.serviceUsageAdmin
    
  8. Ative as APIs necessárias para o tutorial:

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

    A ativação das APIs pode levar um minuto ou mais para ser concluída. Quando as APIs estão ativadas, você vê uma saída semelhante a esta:

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

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 \
        --destination-ranges 10.5.0.0/28 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow nodes to reach the Kubernetes API server."
    
  4. O Anthos 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 \
        --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 pods e serviços em execução no cluster. O GKE cria automaticamente uma regra de entrada correspondente.

    gcloud compute firewall-rules create allow-egress-pods-and-services \
        --action ALLOW \
        --direction EGRESS \
        --rules all \
        --destination-ranges 10.1.0.0/16,10.2.0.0/20 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow pods and services on nodes to reach each other"
    
  6. Um serviço chamado Calico fornece a funcionalidade de API NetworkPolicy para o GKE. Permita a conectividade para Calico na sub-rede:

    gcloud compute firewall-rules create allow-egress-calico \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:5473 \
        --destination-ranges 10.0.0.0/24 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow Calico Typha within the subnet"
    
  7. A porta somente leitura do kubelet é necessária para que o GKE leia as métricas do nó. Permitir o acesso dentro da sub-rede:

    gcloud compute firewall-rules create allow-egress-kubelet-readonly \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:10255 \
        --destination-ranges 10.0.0.0/24 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow access to the kubelet read-only port within the subnet"
    
  8. 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 \
        --destination-ranges 199.36.153.8/30 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow access to the VIPs used by Google Cloud APIs (Private Google Access)"
    
  9. Permita que o serviço do verificador de integridade do Google Cloud acesse os pods em execução no cluster:

    gcloud compute firewall-rules create allow-ingress-gcp-health-checker \
        --action ALLOW \
        --direction INGRESS \
        --rules tcp:80,tcp:443 \
        --source-ranges 130.211.0.0/22,35.191.0.0/16,35.191.0.0/16,209.85.152.0/22,209.85.204.0/22 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow workloads to respond to Google Cloud health checks"
    

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.

O

Crie uma zona de DNS privada, além de um registro "CNAME" e "A", para que os nós e as cargas de trabalho possam se conectar a serviços e APIs 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 de DNS particular, além de um registro "CNAME" e um registro "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. Criar um cluster do GKE particular

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

    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 Anthos Service Mesh requer que os nós do cluster usem um tipo de máquina que tenha pelo menos quatro vCPUs. O Google recomenda que o cluster seja inscrito no canal de lançamento "regular" para ajudar a garantir que os nós estejam executando uma versão do Kubernetes compatível com o Anthos Service Mesh. Para mais informações, consulte os guias de instalação do Anthos Service Mesh.

    A Identidade da carga de trabalho está ativada no cluster. O Anthos Service Mesh requer a identidade da carga de trabalho e é o modo recomendado para acessar as APIs do Google a partir das cargas de trabalho do GKE.

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

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

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

Neste tutorial, são usados os recursos opcionais do Anthos Service Mesh. Para saber como instalar o Anthos Service Mesh com um script, leia o guia de instalação na documentação.

  1. Crie namespaces para o plano de controle da malha de serviço e os gateways de saída a serem implantados em:

    kubectl create ns istio-system
    kubectl create ns istio-egress
    
  2. Rotule os namespaces istio-egress, istio-system e kube-system:

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

    Esses rótulos são usados posteriormente para aplicar a Kubernetes NetworkPolicy. O rótulo istio-injection=disabled evita avisos errados ao executar a análise de istioctl.

  3. Crie um arquivo de manifesto para personalizar a instalação do Anthos Service Mesh usando a API Istio Operator:

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

    Esse arquivo é fornecido como um argumento para o script de instalação e especifica a seguinte configuração:

    • Uma implantação de gateway de saída em execução no namespace istio-egress com uma tolerância e nodeSelector para que seja executada apenas em nós gateway.
    • Registro de acesso a "stdout" para todos os proxies secundários.
  4. Faça o download do script de instalação:

    curl -O https://storage.googleapis.com/csm-artifacts/asm/install_asm
    
  5. Faça o download da assinatura SHA-256 do arquivo para o diretório de trabalho:

    curl -O https://storage.googleapis.com/csm-artifacts/asm/install_asm.sha256
    
  6. Verifique o download com os dois arquivos no mesmo diretório:

    sha256sum -c --ignore-missing install_asm.sha256
    

    Se a verificação for bem-sucedida, a saída será a seguinte:

    install_asm: OK
    

    Para compatibilidade, o arquivo install_asm.sha256 inclui a soma de verificação duas vezes para permitir que qualquer versão do script seja renomeada como install_asm. Se você receber um erro informando que --ignore-missing não existe, execute novamente o comando anterior sem a sinalização --ignore-missing.

  7. Torne o script executável:

    chmod +x install_asm
    
  8. Instale o Anthos Service Mesh executando o script a seguir:

    ./install_asm \
        --mode install \
        --project_id ${PROJECT_ID} \
        --cluster_name cluster1 \
        --cluster_location ${ZONE} \
        --custom_overlay ./asm-custom-install.yaml \
        --output_dir ./ \
        --enable_all
    
  9. Quando o script for concluído, defina uma variável de ambiente para manter 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
    

Como verificar a instalação do Anthos Service Mesh

  1. Verifique se os componentes do plano de controle do Anthos Service Mesh estão em execução no namespace istio-system:

    kubectl get pod -n istio-system
    

    Você verá istio-ingressgateway e istiod-asm pods em execução.

  2. Verifique se os pods do gateway de saída estão em execução no namespace istio-egress e nos nós no pool de nós gateway:

    kubectl get pods -n istio-egress -o wide
    
  3. Os pods do gateway de saída têm um nodeSelector para selecionar nós no pool de nós gateway e uma tolerância que permite que eles sejam executados nos nós de gateway com taint. Examine o nodeSelector e as tolerâncias para os pods do gateway de saída:

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

    O resultado será assim:

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

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 Anthos Service Mesh injete automaticamente proxies sidecar, você precisa definir um rótulo de revisão nos namespaces da carga de trabalho. O rótulo de revisão precisa corresponder à revisão do plano de controle do Anthos Service Mesh implantado no cluster. Procure o rótulo de revisão no pod istiod e armazene-o em uma variável de ambiente:

    REVISION_LABEL=$(kubectl get pod -n istio-system -l app=istiod \
      -o jsonpath='{.items[0].metadata.labels.istio\.io/rev}')
    
  5. Defina o rótulo de revisão nos namespaces team-x e team-y:

    kubectl label ns team-x istio.io/rev=${REVISION_LABEL}
    kubectl label ns team-y istio.io/rev=${REVISION_LABEL}
    
  6. 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
    
  7. Implante o aplicativo de teste no namespace team-x:

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

    O resultado será assim:

    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.

  9. 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 do arquivo secundário 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 Anthos Service Mesh no arquivo secundário é 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 20 clusters Envoy na lista, incluindo vários 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 istio-egress 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.

    A parte "istio-egress/*" especifica que o proxy sidecar seleciona rotas do namespace istio-egress que estão disponíveis 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
    

    A saída inclui apenas alguns clusters para o gateway de saída e um para o próprio pod de teste.

Como configurar o Anthos Service Mesh para rotear o tráfego por meio do gateway de saída

  1. Configure um Gateway para tráfego HTTP na porta 80. O Gateway seleciona o proxy istio-egressgateway que o instalador implantou no namespace istio-egress. A configuração Gateway é aplicada ao namespace istio-egress 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: egress
      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:
          loadBalancer:
            simple: ROUND_ROBIN
          tls:
            mode: ISTIO_MUTUAL
    EOF
    
  3. Crie um ServiceEntry no namespace istio-egress 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
    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
    

    O resultado será assim:

    ✔ 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=egress \
        -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
    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
    

    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:
      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:
      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 fazer upgrades de solicitações HTTP simples para a TLS (origem de TLS). 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:
            loadBalancer:
              simple: ROUND_ROBIN
            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 "atualizadas" 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=egress \
        -o jsonpath="    {.items[0].metadata.name}") istio-proxy
    

    O resultado será assim:

    [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: egress
      servers:
      - port:
          number: 80
          name: https
          protocol: HTTPS
        hosts:
          - '*'
        tls:
          mode: ISTIO_MUTUAL
      - port:
          number: 443
          name: tls
          protocol: TLS
        hosts:
        - '*'
        tls:
          mode: PASSTHROUGH
    EOF
    
  2. 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:
          loadBalancer:
            simple: ROUND_ROBIN
          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:
      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
    

    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=egress \
        -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:
              istio: system
          podSelector:
            matchLabels:
              istio: istiod
        - namespaceSelector:
            matchLabels:
              istio: egress
          podSelector:
            matchLabels:
              istio: egress
    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:
              kube-system: "true"
        ports:
        - port: 53
          protocol: UDP
        - port: 53
          protocol: TCP
    EOF
    
  3. Permita que cargas de trabalho e os proxies se conectem aos IPs que disponibilizam APIs e serviços do Google, incluindo o Mesh CA:

    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
        ports:
        - 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:
              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 Identidade da carga de trabalho 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 Identidade da carga de trabalho, 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
    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
    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
    gsutil mb gs://${PROJECT_ID}-bucket
    gsutil cp /tmp/hello gs://${PROJECT_ID}-bucket/
    
  8. Conceda permissão à conta de serviço para listar e visualizar arquivos no bucket:

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

Excluir 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