Exemplo de Cloud Service Mesh: mTLS


No Cloud Service Mesh 1.5 e versões mais recentes, o TLS mútuo automático (mTLS) automático é ativado por padrão. Com o mTLS automático, um proxy sidecar do cliente detecta automaticamente se o servidor tem um sidecar. O sidecar do cliente envia o mTLS para as cargas de trabalho que têm sidecars e texto simples para as que não têm. No entanto, os serviços aceitam o tráfego de texto simples e mTLS. Ao injetar proxies de sidecar nos seus pods, recomendamos que você também configure os serviços para aceitar apenas o tráfego mTLS.

Com o Cloud Service Mesh, você pode aplicar o mTLS fora do código do aplicativo ao aplicar um único arquivo YAML. O Cloud Service Mesh oferece flexibilidade para aplicar uma política de autenticação a toda a malha de serviço, a um namespace ou a uma carga de trabalho individual.

mTLS mútuo

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, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para um teste gratuito.

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

Antes de começar

Implantar um gateway de entrada

  1. Defina o contexto atual de kubectl para o cluster:

    gcloud container clusters get-credentials CLUSTER_NAME  \
    --project=PROJECT_ID \
    --zone=CLUSTER_LOCATION 
    
  2. Crie um namespace para seu gateway de entrada:

    kubectl create namespace asm-ingress
    
  3. Ativar o namespace para injeção. As etapas dependem da implementação do plano de controle.

    Gerenciado (TD)

    Aplique o rótulo de injeção padrão ao namespace:

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

    Gerenciado (Istiod)

    Recomendado:execute o comando a seguir para aplicar o rótulo de injeção padrão ao namespace:

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

    Se você já for usuário do plano de controle do Istiod gerenciado:recomendamos usar a injeção padrão, mas a injeção baseada em revisão é compatível. Siga estas instruções:

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

      kubectl -n istio-system get controlplanerevision
      

      O resultado será assim:

      NAME                AGE
      asm-managed-rapid   6d7h
      

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

    2. Aplique o rótulo de revisão ao namespace:

      kubectl label namespace asm-ingress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      
  4. Implante o gateway de exemplo no repositório anthos-service-mesh-samples:

    kubectl apply -n asm-ingress \
    -f docs/shared/asm-ingress-gateway
    

    Saída esperada:

    serviceaccount/asm-ingressgateway configured
    service/asm-ingressgateway configured
    deployment.apps/asm-ingressgateway configured
    gateway.networking.istio.io/asm-ingressgateway configured
    

Implantar o aplicativo de amostra Online Boutique

  1. Caso contrário, defina o contexto atual de kubectl para o cluster:

      gcloud container clusters get-credentials CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  2. Crie o namespace do aplicativo de amostra:

      kubectl create namespace onlineboutique
    
  3. Rotule o namespace onlineboutique para injetar automaticamente proxies do Envoy. Siga as etapas para ativar a injeção automática de arquivo secundário.

  4. Implante o aplicativo de amostra, o VirtualService para o front-end e as contas de serviço para as cargas de trabalho. Neste tutorial, você vai implantar o Online Boutique, um app de demonstração de microsserviços.

      kubectl apply \
      -n onlineboutique \
      -f docs/shared/online-boutique/virtual-service.yaml
      kubectl apply \
      -n onlineboutique \
      -f docs/shared/online-boutique/service-accounts
    

Ver seus serviços

  1. Veja os pods no namespace onlineboutique:

    kubectl get pods -n onlineboutique
    

    Saída esperada:

    NAME                                     READY   STATUS    RESTARTS   AGE
    adservice-85598d856b-m84m6               2/2     Running   0          2m7s
    cartservice-c77f6b866-m67vd              2/2     Running   0          2m8s
    checkoutservice-654c47f4b6-hqtqr         2/2     Running   0          2m10s
    currencyservice-59bc889674-jhk8z         2/2     Running   0          2m8s
    emailservice-5b9fff7cb8-8nqwz            2/2     Running   0          2m10s
    frontend-77b88cc7cb-mr4rp                2/2     Running   0          2m9s
    loadgenerator-6958f5bc8b-55q7w           2/2     Running   0          2m8s
    paymentservice-68dd9755bb-2jmb7          2/2     Running   0          2m9s
    productcatalogservice-84f95c95ff-c5kl6   2/2     Running   0          114s
    recommendationservice-64dc9dfbc8-xfs2t   2/2     Running   0          2m9s
    redis-cart-5b569cd47-cc2qd               2/2     Running   0          2m7s
    shippingservice-5488d5b6cb-lfhtt         2/2     Running   0          2m7s
    

    Todos os pods do aplicativo devem estar em execução, com um 2/2 na coluna READY. Isso indica que os pods têm um proxy sidecar do Envoy injetado com sucesso. Se ele não mostrar 2/2 após alguns minutos, acesse o Guia de solução de problemas.

  2. Defina o IP externo e uma variável:

    kubectl get services -n asm-ingress
    export FRONTEND_IP=$(kubectl --namespace asm-ingress \
    get service --output jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}' \
    )
    

    Você verá uma saída semelhante a esta:

    NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                      AGE
    asm-ingressgateway   LoadBalancer   10.19.247.233   35.239.7.64   80:31380/TCP,443:31390/TCP,31400:31400/TCP   27m
    
    
  3. Acesse o endereço EXTERNAL-IP no navegador da Web. Você vai encontrar a loja Online Boutique no seu navegador.

    front-end do online boutique

Criar um pod TestCurl

Crie um pod TestCurl para enviar tráfego de texto simples para testes.

  apiVersion: v1
  kind: Pod
  metadata:
    name: testcurl
    namespace: default
    annotations:
      sidecar.istio.io/inject: "false"
  spec:
    containers:
    - name: curl
      image: curlimages/curl
      command: ["sleep", "600"]

Acessar o Online Boutique

  1. Defina o contexto atual de kubectl no cluster em que você implantou o Online Boutique:

    gcloud container clusters get-credentials CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  2. Liste os serviços no namespace frontend:

    kubectl get services -n frontend
    

    Observe que frontend-external é um LoadBalancer e tem um endereço IP externo. O aplicativo de exemplo inclui um serviço que é um balanceador de carga, para que ele possa ser implantado no GKE sem o Cloud Service Mesh.

  3. Acesse o aplicativo no navegador usando o endereço IP externo do serviço frontend-external:

    http://FRONTEND_EXTERNAL_IP/
    
  4. O Cloud Service Mesh permite implantar um gateway de entrada. Também é possível acessar o "Online Boutique" usando o endereço IP externo do gateway de entrada. Consulte o IP externo do gateway: Substitua os marcadores pelas informações a seguir:

    • GATEWAY_SERVICE_NAME : o nome do serviço de gateway de entrada. Se você implantou o gateway de amostra sem modificação ou se implantou o gateway de entrada padrão, o nome é istio-ingressgateway.
    • GATEWAY_NAMESPACE: o namespace em que você implantou o gateway de entrada: Se você implantou o gateway de entrada padrão, o namespace é istio-system.
    kubectl get service GATEWAY_NAME -n GATEWAY_NAMESPACE
    
  5. Abra outra guia no navegador e acesse o aplicativo usando o endereço IP externo do gateway de entrada:

    http://INGRESS_GATEWAY_EXTERNAL_IP/
    
  6. Execute o seguinte comando para fazer curl no serviço frontend usando HTTP simples de outro pod. Como os serviços estão em namespaces diferentes, é necessário agrupar o nome DNS do serviço frontend.

    kubectl debug --image istio/base --target istio-proxy -it \
      $(kubectl get pod -l app=productcatalogservice -n product-catalog -o jsonpath={.items..metadata.name}) \
      -n product-catalog -- \
      curl http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    Sua solicitação é bem-sucedida com o status 200 porque, por padrão, são aceitos tráfego TLS e de texto simples.

Ativar o TLS mútuo por namespace

Você impõe o mTLS aplicando uma política PeerAuthentication com kubectl.

  1. Salve a seguinte política de autenticação como mtls-namespace.yaml.

    cat <<EOF > mtls-namespace.yaml
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "namespace-policy"
    spec:
      mtls:
        mode: STRICT
    EOF
    

    A linha mode: STRICT no YAML configura os serviços para aceitar apenas mTLS. Por padrão, o mode é PERMISSIVE, que configura os serviços para aceitar texto simples e mTLS.

  2. Aplique a política de autenticação para configurar todos os serviços do Online Boutique de modo que aceite apenas mTLS:

    for ns in ad cart checkout currency email frontend loadgenerator \
         payment product-catalog recommendation shipping; do
    kubectl apply -n $ns -f mtls-namespace.yaml
    done
    

    Resposta esperada:

    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created

  3. Acesse o Online Boutique no navegador usando o endereço IP externo do serviço frontend-external:

    http://FRONTEND_EXTERNAL_IP/
    
  4. Atualize a página. O navegador exibirá o seguinte erro:

    não é possível acessar o site

    A atualização da página faz com que o texto simples seja enviado para o serviço frontend. Devido à política de autenticação STRICT, o proxy sidecar bloqueia a solicitação para o serviço.

  5. No seu navegador, vá para a guia que dá acesso ao Online Boutique usando o endereço IP externo de istio-ingressgateway e atualize a página, que é exibida. Ao acessar o "Online Boutique" usando o gateway de entrada, a solicitação usa o seguinte caminho:

    mTLS mútuo

    Fluxo de autenticação do mTLS:

    1. O navegador envia ao servidor uma solicitação HTTP de texto simples.
    2. O contêiner do proxy do gateway de entrada intercepta a solicitação.
    3. O proxy do gateway de entrada executa um handshake de TLS com o proxy do lado do servidor (o serviço de front-end neste exemplo). Esse handshake inclui uma troca de certificados. Esses certificados são pré-carregados nos contêineres de proxy pelo Cloud Service Mesh.
    4. O proxy de entrada executa uma verificação de nomenclatura segura no certificado do servidor, verificando se uma identidade autorizada está em execução no servidor.
    5. Os proxies de servidor e o gateway de entrada estabelecem uma conexão TLS mútua, e o proxy do servidor encaminha a solicitação para o contêiner do aplicativo de servidor (o serviço de front-end).
  6. Execute o seguinte comando para fazer curl no serviço frontend usando HTTP simples de outro pod.

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    Sua solicitação falhou porque estamos enviando tráfego em texto simples de uma carga de trabalho sem sidecar, em que a política peerAuthentication RÍGIDA é aplicada.

Encontrar e excluir políticas de autenticação

  1. Para uma lista de todas as políticas PeerAuthentication na malha de serviço:

    kubectl get peerauthentication --all-namespaces
    

    O resultado será assim:

    NAMESPACE         NAME               MODE     AGE
    ad                namespace-policy   STRICT   17m
    cart              namespace-policy   STRICT   17m
    checkout          namespace-policy   STRICT   17m
    currency          namespace-policy   STRICT   17m
    email             namespace-policy   STRICT   17m
    frontend          namespace-policy   STRICT   17m
    loadgenerator     namespace-policy   STRICT   17m
    payment           namespace-policy   STRICT   17m
    product-catalog   namespace-policy   STRICT   17m
    recommendation    namespace-policy   STRICT   17m
    shipping          namespace-policy   STRICT   17m
    
  2. Exclua a política de autenticação de todos os namespaces do Online Boutique:

    for ns in ad cart checkout currency email frontend loadgenerator payment \
      product-catalog recommendation shipping; do
        kubectl delete peerauthentication -n $ns namespace-policy
    done;
    

    Resposta esperada:

    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    
  3. Acesse o Online Boutique usando o endereço IP externo do serviço frontend-external e atualize a página. A página é exibida conforme o esperado.

  4. Execute o seguinte comando para fazer curl no serviço frontend usando HTTP simples de outro pod.

    kubectl debug --image istio/base --target istio-proxy -it \
      $(kubectl get pod -l app=productcatalogservice -n product-catalog -o jsonpath={.items..metadata.name}) \
      -n product-catalog -- \
      curl http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    Sua solicitação é bem-sucedida com o status 200 porque, por padrão, são aceitos tráfego TLS e de texto simples.

Se você atualizar a página no console Google Cloud que mostra a lista de Cargas de trabalho, ela agora vai mostrar que o status do mTLS é Permissive.

Ativar o TLS mútuo por carga de trabalho

Para definir uma política PeerAuthentication para uma carga de trabalho específica, configure a seção selector e especifique os rótulos que correspondem à carga de trabalho pretendida. No entanto, o Cloud Service Mesh não agrega políticas no nível da carga de trabalho para o tráfego mTLS de saída para um serviço. Você precisa configurar uma regra de destino para gerenciar esse comportamento.

  1. Aplique uma política de autenticação a uma carga de trabalho específica. Observe como a política a seguir usa identificadores e seletores para segmentar a implantação específica de frontend.

    cat <<EOF | kubectl apply -n frontend -f -
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "frontend"
      namespace: "frontend"
    spec:
      selector:
        matchLabels:
          app: frontend
      mtls:
        mode: STRICT
    EOF
    

    Resposta esperada:

    peerauthentication.security.istio.io/frontend created
  2. Configure uma regra de destino correspondente:

    cat <<EOF | kubectl apply -n frontend -f -
    apiVersion: "networking.istio.io/v1alpha3"
    kind: "DestinationRule"
    metadata:
      name: "frontend"
    spec:
      host: "frontend.demo.svc.cluster.local"
      trafficPolicy:
        tls:
          mode: ISTIO_MUTUAL
    EOF
    

    Resposta esperada:

    destinationrule.networking.istio.io/frontend created
  3. Acesse o Online Boutique usando o endereço IP externo do serviço frontend-external e atualize a página. A página não é exibida porque frontend service está definido como mTLSSTRICT e o proxy sidecar bloqueia a solicitação.

  4. Execute o seguinte comando para fazer curl no serviço frontend usando HTTP simples de outro pod.

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    Sua solicitação falhou porque estamos enviando tráfego em texto simples de uma carga de trabalho sem sidecar, em que a política peerAuthentication RÍGIDA é aplicada.

  5. Exclua a política de autenticação:

    kubectl delete peerauthentication -n frontend frontend
    

    Resposta esperada:

    peerauthentication.security.istio.io "frontend" deleted
    
  6. Exclua a regra de destino:

    kubectl delete destinationrule -n frontend frontend
    

    Resposta esperada:

    destinationrule.networking.istio.io "frontend" deleted
    

Como aplicar o mTLS em toda a malha

Para impedir que todos os serviços na malha aceitem tráfego de texto simples, defina uma política PeerAuthentication para toda a malha, com o modo mTLS definido como STRICT. A política PeerAuthentication em toda a malha não pode ter um seletor e precisa ser aplicada no namespace raiz, istio-system. Quando você implanta a política, o plano de controle provisiona certificados TLS automaticamente, para que as cargas de trabalho possam ser autenticadas entre si.

  1. Aplique o mTLS em toda a malha:

    kubectl apply -f - <<EOF
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "mesh-wide"
      namespace: "istio-system"
    spec:
      mtls:
        mode: STRICT
    EOF
    

    Resposta esperada:

    peerauthentication.security.istio.io/mesh-wide created

  2. Acesse o Online Boutique usando o endereço IP externo do serviço frontend-external e atualize a página. A página não é exibida.

  3. Execute o seguinte comando para fazer curl no serviço frontend usando HTTP simples de outro pod.

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    Sua solicitação falhou porque estamos enviando tráfego em texto simples de uma carga de trabalho sem sidecar, em que a política peerAuthentication RÍGIDA é aplicada.

  4. Exclua a política mesh-wide:

    kubectl delete peerauthentication -n istio-system mesh-wide
    

    Saída esperada:

    peerauthentication.security.istio.io "mesh-wide" deleted
    

Limpar

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.

  • Se quiser evitar cobranças adicionais, exclua o cluster:

    gcloud container clusters delete  CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  • Se quiser manter o cluster e remover o aplicativo de amostra do Online Boutique:

    1. Exclua os namespaces do aplicativo:
      kubectl delete -f online-boutique/kubernetes-manifests/namespaces
    

    Saída esperada:

    namespace "ad" deleted
    namespace "cart" deleted
    namespace "checkout" deleted
    namespace "currency" deleted
    namespace "email" deleted
    namespace "frontend" deleted
    namespace "loadgenerator" deleted
    namespace "payment" deleted
    namespace "product-catalog" deleted
    namespace "recommendation" deleted
    namespace "shipping" deleted
    
    1. Exclua as entradas de serviço:
      kubectl delete -f online-boutique/istio-manifests/allow-egress-googleapis.yaml
    

    Resposta esperada:

    serviceentry.networking.istio.io "allow-egress-googleapis" deleted
    serviceentry.networking.istio.io "allow-egress-google-metadata" deleted
    

A seguir