Cloud Service Mesh mediante ejemplo: mTLS


En Cloud Service Mesh 1.5 y versiones posteriores, la autenticación TLS mutua automática (mTLS automática) está habilitada de forma predeterminada. Con mTLS automático, un proxy sidecar del cliente detecta automáticamente si el servidor tiene un sidecar. El sidecar del cliente envía mTLS a las cargas de trabajo con sidecars y texto sin formato a las cargas de trabajo sin sidecars. Sin embargo, los servicios aceptan tráfico tanto de texto sin formato como de mTLS. A medida que insertes proxies sidecar en tus pods, te recomendamos que también configures tus servicios para que solo acepten tráfico mTLS.

Con Cloud Service Mesh, puedes aplicar mTLS fuera del código de tu aplicación aplicando un solo archivo YAML. Cloud Service Mesh te ofrece la flexibilidad necesaria para aplicar una política de autenticación a toda la malla de servicios, a un espacio de nombres o a una carga de trabajo concreta.

mTLS mutuo

Costes

En este documento, se utilizan los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costes basada en el uso previsto, utiliza la calculadora de precios.

Los usuarios nuevos Google Cloud pueden disfrutar de una prueba gratuita.

Cuando termines este tutorial, puedes evitar costes continuos eliminando los recursos que hayas creado. Para obtener más información, consulta la sección Limpiar.

Antes de empezar

Desplegar una pasarela de entrada

  1. Define el contexto actual de kubectl en el clúster:

    gcloud container clusters get-credentials CLUSTER_NAME  \
    --project=PROJECT_ID \
    --zone=CLUSTER_LOCATION 
    
  2. Crea un espacio de nombres para tu pasarela de entrada:

    kubectl create namespace asm-ingress
    
  3. Habilita el espacio de nombres para la inyección. Los pasos dependen de la implementación del plano de control.

    Gestionado (TD)

    Aplica la etiqueta de inyección predeterminada al espacio de nombres:

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

    Gestionado (Istiod)

    Recomendación: Ejecuta el siguiente comando para aplicar la etiqueta de inyección predeterminada al espacio de nombres:

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

    Si ya eres usuario del plano de control de Istiod gestionado: Te recomendamos que utilices la inyección predeterminada, pero también se admite la inyección basada en revisiones. Sigue estas instrucciones:

    1. Ejecuta el siguiente comando para localizar los canales de lanzamiento disponibles:

      kubectl -n istio-system get controlplanerevision
      

      El resultado debería ser similar al siguiente:

      NAME                AGE
      asm-managed-rapid   6d7h
      

      En el resultado, el valor de la columna NAME es la etiqueta de revisión que corresponde al canal de lanzamiento disponible para la versión de Cloud Service Mesh.

    2. Aplica la etiqueta de revisión al espacio de nombres:

      kubectl label namespace asm-ingress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      
  4. Despliega la pasarela de ejemplo en el repositorio anthos-service-mesh-samples:

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

    Resultado esperado:

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

Implementar la aplicación de ejemplo Online Boutique

  1. Si no lo ha hecho, defina el contexto actual de kubectl en el clúster:

      gcloud container clusters get-credentials CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  2. Crea el espacio de nombres de la aplicación de ejemplo:

      kubectl create namespace onlineboutique
    
  3. Etiqueta el espacio de nombres onlineboutique para insertar automáticamente proxies de Envoy. Sigue los pasos para habilitar la inyección automática de sidecar.

  4. Implementa la aplicación de ejemplo, el VirtualService del frontend y las cuentas de servicio de las cargas de trabajo. En este tutorial, desplegarás Online Boutique, una aplicación de demostración de microservicios.

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

Ver tus servicios

  1. Consulta los pods del espacio de nombres onlineboutique:

    kubectl get pods -n onlineboutique
    

    Resultado esperado:

    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 los pods de tu aplicación deben estar operativos y tener un 2/2 en la columna READY. Esto indica que los pods tienen un proxy sidecar de Envoy insertado correctamente. Si no aparece 2/2 al cabo de un par de minutos, consulta la guía de solución de problemas.

  2. Obtén la IP externa y asígnala a una variable:

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

    Verá un resultado similar al siguiente:

    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. Visita la dirección EXTERNAL-IP en tu navegador web. Deberías ver la tienda Online Boutique en tu navegador.

    frontend de boutique online

Crear un pod TestCurl

Crea un pod TestCurl para enviar tráfico de texto sin formato con fines de prueba.

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

Acceder a Online Boutique

  1. Define el contexto actual de kubectl en el clúster en el que has desplegado Online Boutique:

    gcloud container clusters get-credentials CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  2. Muestra los servicios del espacio de nombres frontend:

    kubectl get services -n frontend
    

    Observa que frontend-external es un LoadBalancer y tiene una dirección IP externa. La aplicación de ejemplo incluye un servicio que es un balanceador de carga para que se pueda implementar en GKE sin Cloud Service Mesh.

  3. Visita la aplicación en tu navegador con la dirección IP externa del servicio frontend-external:

    http://FRONTEND_EXTERNAL_IP/
    
  4. Cloud Service Mesh te permite desplegar una pasarela de entrada. También puedes acceder a Online Boutique mediante la dirección IP externa de la pasarela de entrada. Obtén la IP externa de la pasarela. Sustituye los marcadores de posición por la siguiente información:

    • GATEWAY_SERVICE_NAME : nombre del servicio de pasarela de entrada. Si has implementado la pasarela de ejemplo sin modificarla o si has implementado la pasarela de entrada predeterminada, el nombre es istio-ingressgateway.
    • GATEWAY_NAMESPACE: el espacio de nombres en el que has implementado la pasarela de entrada. Si has implementado la pasarela de entrada predeterminada, el espacio de nombres es istio-system.
    kubectl get service GATEWAY_NAME -n GATEWAY_NAMESPACE
    
  5. Abre otra pestaña en el navegador y visita la aplicación con la dirección IP externa de la pasarela de entrada:

    http://INGRESS_GATEWAY_EXTERNAL_IP/
    
  6. Ejecuta el siguiente comando para curl el servicio frontend con HTTP sin cifrar desde otro pod. Como los servicios están en espacios de nombres diferentes, debes usar curl en el nombre DNS del servicio 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'
    

    Tu solicitud se ha completado correctamente con el estado 200 porque, de forma predeterminada, se acepta tanto el tráfico TLS como el de texto sin cifrar.

Habilitar TLS mutuo por espacio de nombres

Para aplicar mTLS, debes aplicar una política PeerAuthentication con kubectl.

  1. Guarda la siguiente política de autenticación 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
    

    La línea mode: STRICT del archivo YAML configura los servicios para que solo acepten mTLS. De forma predeterminada, mode es PERMISSIVE, lo que configura los servicios para que acepten tanto texto sin formato como mTLS.

  2. Aplica la política de autenticación para configurar todos los servicios de Online Boutique de forma que solo acepten 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
    

    Resultado esperado:

    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. Ve a la pestaña de tu navegador que accede a Online Boutique mediante la dirección IP externa del servicio frontend-external:

    http://FRONTEND_EXTERNAL_IP/
    
  4. Actualiza la página. El navegador muestra el siguiente error:

    No se puede acceder al sitio

    Al actualizar la página, se envía texto sin formato al servicio frontend. Debido a la STRICTpolítica de autenticación, el proxy sidecar bloquea la solicitud al servicio.

  5. Ve a la pestaña del navegador que accede a Online Boutique mediante la dirección IP externa de istio-ingressgateway y actualiza la página, que se mostrará correctamente. Cuando accedes a Online Boutique mediante la puerta de enlace de entrada, la solicitud sigue esta ruta:

    mTLS mutuo

    Flujo de autenticación mTLS:

    1. El navegador envía una solicitud HTTP en texto sin cifrar al servidor.
    2. El contenedor proxy de la puerta de enlace de entrada intercepta la solicitud.
    3. El proxy de la pasarela de entrada realiza un handshake TLS con el proxy del lado del servidor (el servicio frontend en este ejemplo). Este handshake incluye un intercambio de certificados. Cloud Service Mesh precarga estos certificados en los contenedores proxy.
    4. El proxy de la pasarela de entrada realiza una comprobación de nombres segura en el certificado del servidor para verificar que una identidad autorizada esté ejecutando el servidor.
    5. La pasarela de entrada y los proxies del servidor establecen una conexión TLS mutua, y el proxy del servidor reenvía la solicitud al contenedor de la aplicación del servidor (el servicio frontend).
  6. Ejecuta el siguiente comando para curl el servicio frontend con HTTP sin cifrar desde otro pod.

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

    Tu solicitud falla porque estamos enviando tráfico de texto sin formato desde una carga de trabajo sin sidecar en la que se aplica la política STRICT peerAuthentication.

Buscar y eliminar políticas de autenticación

  1. Para ver una lista de todas las políticas de PeerAuthentication en la malla de servicios, haz lo siguiente:

    kubectl get peerauthentication --all-namespaces
    

    El resultado debería ser similar al siguiente:

    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. Elimina la política de autenticación de todos los espacios de nombres de 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;
    

    Resultado esperado:

    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. Accede a Online Boutique con la dirección IP externa del servicio frontend-external y actualiza la página. La página se muestra como se esperaba.

  4. Ejecuta el siguiente comando para curl el servicio frontend con HTTP sin cifrar desde otro 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'
    

    Tu solicitud se ha completado correctamente con el estado 200 porque, de forma predeterminada, se acepta tanto el tráfico TLS como el de texto sin cifrar.

Si actualizas la página de la consola Google Cloud que muestra la lista Cargas de trabajo, ahora verás que el estado de mTLS es Permissive.

Habilitar TLS mutuo por carga de trabajo

Para definir una política de PeerAuthentication en una carga de trabajo específica, debe configurar la sección selector y especificar las etiquetas que coincidan con la carga de trabajo deseada. Sin embargo, Cloud Service Mesh no puede agregar políticas a nivel de carga de trabajo para el tráfico mTLS saliente a un servicio. Para gestionar este comportamiento, debe configurar una regla de destino.

  1. Aplica una política de autenticación a una carga de trabajo específica. Fíjate en cómo la siguiente política usa etiquetas y selectores para segmentar una frontend implementación específica.

    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
    

    Resultado esperado:

    peerauthentication.security.istio.io/frontend created
  2. Configura una regla de destino de coincidencia.

    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
    

    Resultado esperado:

    destinationrule.networking.istio.io/frontend created
  3. Accede a Online Boutique con la dirección IP externa del servicio frontend-external y actualiza la página. La página no se muestra porque frontend service está configurado como STRICT mTLS y el proxy sidecar bloquea la solicitud.

  4. Ejecuta el siguiente comando para curl el servicio frontend con HTTP sin cifrar desde otro pod.

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

    Tu solicitud falla porque estamos enviando tráfico de texto sin formato desde una carga de trabajo sin sidecar en la que se aplica la política STRICT peerAuthentication.

  5. Elimina la política de autenticación:

    kubectl delete peerauthentication -n frontend frontend
    

    Resultado esperado:

    peerauthentication.security.istio.io "frontend" deleted
    
  6. Elimina la regla de destino:

    kubectl delete destinationrule -n frontend frontend
    

    Resultado esperado:

    destinationrule.networking.istio.io "frontend" deleted
    

Implementar mTLS en toda la malla

Para evitar que todos los servicios de la malla acepten tráfico sin cifrar, define una PeerAuthentication política en toda la malla con el modo mTLS definido como STRICT. La política PeerAuthentication de toda la malla no debe tener un selector y debe aplicarse en el espacio de nombres raíz, istio-system. Cuando implementas la política, el plano de control aprovisiona automáticamente certificados TLS para que las cargas de trabajo puedan autenticarse entre sí.

  1. Implementa mTLS en toda la malla:

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

    Resultado esperado:

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

  2. Accede a Online Boutique con la dirección IP externa del servicio frontend-external y actualiza la página. La página no se muestra.

  3. Ejecuta el siguiente comando para curl el servicio frontend con HTTP sin cifrar desde otro pod.

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

    Tu solicitud falla porque estamos enviando tráfico de texto sin formato desde una carga de trabajo sin sidecar en la que se aplica la política STRICT peerAuthentication.

  4. Elimina la política mesh-wide:

    kubectl delete peerauthentication -n istio-system mesh-wide
    

    Resultado esperado:

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

Limpieza

Para evitar que los recursos utilizados en este tutorial se cobren en tu cuenta de Google Cloud, elimina el proyecto que contiene los recursos o conserva el proyecto y elimina los recursos.

  • Si quieres evitar cargos adicionales, elimina el clúster:

    gcloud container clusters delete  CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  • Si quieres conservar tu clúster y eliminar la muestra de Online Boutique, sigue estos pasos:

    1. Elimina los espacios de nombres de la aplicación:
      kubectl delete -f online-boutique/kubernetes-manifests/namespaces
    

    Resultado esperado:

    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. Elimina las entradas de servicio:
      kubectl delete -f online-boutique/istio-manifests/allow-egress-googleapis.yaml
    

    Resultado esperado:

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

Siguientes pasos