Integrar IAP con Cloud Service Mesh

En esta guía se describe cómo integrar Identity-Aware Proxy (IAP) con Cloud Service Mesh. La integración de IAP con Cloud Service Mesh te permite acceder de forma segura a los servicios basados en los principios de BeyondCorp de Google. IAP verifica la identidad de los usuarios y el contexto de la solicitud para determinar si se les debe conceder acceso a una aplicación o a un recurso. La integración de IAP con Cloud Service Mesh te ofrece las siguientes ventajas:

  • Control contextual completo del acceso a las cargas de trabajo que se ejecutan en Cloud Service Mesh. Puedes definir políticas de acceso detalladas basadas en atributos de la solicitud de origen, como la identidad del usuario, la dirección IP y el tipo de dispositivo. Puedes combinar tus políticas de acceso con restricciones basadas en el nombre de host y la ruta de una URL de solicitud.

  • Habilita la compatibilidad con las reclamaciones contextuales en la autorización de Cloud Service Mesh.

  • Acceso escalable, seguro y de alta disponibilidad a tu aplicación a través de un balanceador de carga de Google Cloud. El balanceo de carga de alto rendimiento ofrece protección integrada contra ataques de denegación de servicio distribuido (DDoS) y admite direcciones IP anycast globales.

Requisitos previos

Sigue los pasos que se indican en Instalar herramientas dependientes y validar el clúster para:

Además, en esta guía se da por hecho que tienes lo siguiente:

Configurar un clúster con Cloud Service Mesh

En esta sección se explica cómo configurar la integración de IAP tanto para las nuevas instalaciones de Cloud Service Mesh como para las actualizaciones.

Nuevas instalaciones

  1. Habilita iap.googleapis.com. En el siguiente comando, sustituye PROJECT_ID por el proyecto en el que instalarás Cloud Service Mesh:

    gcloud services enable \
      --project=PROJECT_ID \
      iap.googleapis.com
    
  2. El clúster que estés actualizando debe tener la opción --addons=HttpLoadBalancing definida. El complemento HttpLoadBalancing habilita un controlador de balanceo de carga HTTP (L7) para el clúster. Ejecuta el siguiente comando para actualizar el clúster con las opciones que requiere Cloud Service Mesh. A menos que hayas definido una zona o una región predeterminadas, debes indicar la región (--region=REGION) o la zona (--zone=ZONE) en el comando.

    gcloud container clusters update CLUSTER_NAME \
      --project=PROJECT_ID \
      --update-addons=HttpLoadBalancing=ENABLED
    
  3. De forma predeterminada, el archivo iap-operator.yaml tiene el puerto 31223 configurado como puerto de estado y el puerto 31224 configurado como puerto HTTP. Si el puerto 31223 ya está en uso en tu clúster, ejecuta lo siguiente para definir otro puerto de estado:

    kpt cfg set asm gcloud.container.cluster.ingress.statusPort STATUS_PORT
    

    Si el puerto 31224 ya está en uso en tu clúster, ejecuta el siguiente comando para definir otro puerto http:

    kpt cfg set asm gcloud.container.cluster.ingress.httpPort HTTP_PORT
    
  4. Sigue los pasos que se indican en el artículo Instalar las funciones predeterminadas y Mesh CA para usar una secuencia de comandos proporcionada por Google e instalar Cloud Service Mesh. Cuando ejecutes la secuencia de comandos, incluye la siguiente opción:

    --option iap-operator
    

    Por ejemplo:

    ./asmcli install \
      --project_id "PROJECT_ID" \
      --cluster_name "CLUSTER_NAME" \
      --cluster_location "CLUSTER_LOCATION" \
      --fleet_id FLEET_PROJECT_ID \
      --output_dir DIR_PATH \
      --enable_all \
      --option iap-operator
    

    Cuando instalas Cloud Service Mesh, el archivo iap-operator.yaml define el campo type del servicio istio-ingressgateway como NodePort, lo que configura la puerta de enlace para que abra un puerto específico en la malla de servicios. De esta forma, puedes configurar un balanceador de carga que dirija el tráfico enviado a tu nombre de dominio a este puerto.

  5. Si vas a instalar Cloud Service Mesh gestionado, sigue también estos pasos:

    1. Añade la etiqueta de revisión al espacio de nombres istio-system.

    2. Descarga la especificación del servicio de pasarela de entrada de Istio para IAP y llámala iap_operator.yaml.

    3. Instala el ingreso como un servicio NodePort. Para obtener más información, consulta el artículo Migrar desde IstioOperator.

      asmcli experimental mcp-migrate-check -f iap_operator.yaml
      
      istioctl install -f /asm-generated-configs/gateways-istiooperator/"GATEWAY_NAME".yaml
      

Después de instalar Cloud Service Mesh, vuelve a esta guía y continúa con la sección siguiente para configurar la integración con IAP.

Actualizaciones

En esta sección se tratan los siguientes casos prácticos de actualización:

  • Ya has configurado la integración de IAP y vas a actualizar Cloud Service Mesh. En este caso, ya has habilitado iap.googleapis.com en tu proyecto y el complemento HttpLoadBalancing en tu clúster. Ve al paso 3 para descargar el paquete asm y actualizar Cloud Service Mesh.

  • Estás actualizando Cloud Service Mesh y quieres configurar la integración con IAP por primera vez. En este caso, debes completar todos los pasos siguientes, actualizar Cloud Service Mesh y volver a esta guía después de la actualización para completar la integración.

  1. Habilita iap.googleapis.com. En el siguiente comando, sustituye PROJECT_ID por el proyecto en el que instalarás Cloud Service Mesh.

    gcloud services enable \
      --project=PROJECT_ID \
      iap.googleapis.com
    
  2. El clúster que estés actualizando debe tener la opción --addons=HttpLoadBalancing definida. El complemento HttpLoadBalancing habilita un controlador de balanceo de carga HTTP (L7) para el clúster. Ejecuta el siguiente comando para actualizar el clúster con las opciones que requiere Cloud Service Mesh. A menos que hayas definido una zona o una región predeterminadas, debes indicar la región (--region=REGION) o la zona (--zone=ZONE) en el comando.

    gcloud container clusters update CLUSTER_NAME \
      --project=PROJECT_ID
      --update-addons=HttpLoadBalancing=ENABLED
    
  3. Si vas a actualizar un balanceador de carga HTTP de Cloud que ya funciona, ejecuta el siguiente comando para conservar los puertos HTTP y de estado:

    kpt cfg set asm gcloud.container.cluster.ingress.httpPort $(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
    
    kpt cfg set asm gcloud.container.cluster.ingress.statusPort $(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="status-port")].nodePort}')
    
  4. Sigue los pasos que se indican en Actualizar Cloud Service Mesh para usar una secuencia de comandos proporcionada por Google y actualizar Cloud Service Mesh.

    Cuando actualizas Cloud Service Mesh, el archivo iap-operator.yaml define el campo type del servicio istio-ingressgateway en NodePort, lo que configura la puerta de enlace para que abra un puerto específico en la malla de servicios. De esta forma, puedes configurar un balanceador de carga que dirija el tráfico enviado a tu nombre de dominio a este puerto.

    De forma predeterminada, el archivo iap-operator.yaml tiene el puerto 31223 configurado como puerto de estado y el puerto 31224 configurado como puerto HTTP.

    Cuando ejecutes la secuencia de comandos, incluye la siguiente opción:

    --option iap-operator
    

    Por ejemplo:

    ./asmcli install \
      --project_id "PROJECT_ID" \
      --cluster_name "CLUSTER_NAME" \
      --cluster_location "CLUSTER_LOCATION" \
      --fleet_id FLEET_PROJECT_ID \
      --output_dir DIR_PATH \
      --enable_all \
      --option iap-operator
    
  5. Completa la actualización activando la inyección automática del proxy sidecar en tus cargas de trabajo. Para obtener más información, consulta Implementar y volver a implementar cargas de trabajo.

    Una vez que hayas completado la actualización, vuelve a esta guía y continúa con la siguiente sección para configurar la integración con las compras en la aplicación.

Reservar una dirección IP estática y configurar DNS

Para integrar Identity-Aware Proxy con Cloud Service Mesh, debes configurar unGoogle Cloud balanceador de carga HTTP(S), lo que requiere un nombre de dominio que apunte a una dirección IP estática. Puedes reservar una dirección IP externa estática, que asigna la dirección a tu proyecto indefinidamente hasta que la liberes explícitamente.

  1. Reserva una dirección IP externa estática:

    gcloud compute addresses create example-static-ip --global
    
  2. Obtén la dirección IP estática:

    gcloud compute addresses describe example-static-ip --global
    
  3. En tu registrador de nombres de dominio, configura un nombre de dominio completo (FQDN) con la dirección IP estática. Normalmente, se añade un registro A a la configuración de DNS. Los pasos de configuración y la terminología para añadir un A registro de un FQDN varían en función del registrador de nombres de dominio.

    El ajuste de DNS puede tardar entre 24 y 48 horas en propagarse. Puedes seguir configurando todo lo que se indica en esta guía, pero no podrás probar la configuración hasta que se propaguen los ajustes de DNS.

Desplegar una aplicación de ejemplo

Antes de habilitar las compras en la aplicación, necesitas una aplicación que se ejecute en tu clúster de GKE para verificar que todas las solicitudes tengan una identidad. En esta guía se usa la muestra Bookinfo para mostrar cómo configurar el balanceador de carga HTTP(S) y habilitar IAP.

Sigue los pasos para desplegar Bookinfo. Hasta que despliegues el balanceador de carga, no se podrá acceder a la aplicación Bookinfo fuera del clúster de GKE (por ejemplo, desde un navegador).

Solicitudes externas

El recurso de pasarela de Bookinfo (definido en samples/bookinfo/networking/bookinfo-gateway.yaml) usa el istio-ingressgateway preconfigurado. Recuerda que, cuando desplegaste Cloud Service Mesh, especificaste NodePort para el istio-ingressgateway, lo que abre un puerto específico en la malla de servicios. Aunque los nodos de tu clúster tienen direcciones IP externas, las solicitudes procedentes de fuera del clúster están bloqueadas por Google Cloud reglas de firewall. Con las compras en aplicaciones, la forma correcta de exponer las aplicaciones a Internet es mediante un balanceador de carga. No expongas las direcciones de los nodos mediante reglas de cortafuegos, ya que se saltaría IAP.

Para enrutar las solicitudes a Bookinfo, debes configurar un balanceador de carga HTTP(S) en tuGoogle Cloud proyecto. Como el balanceador de carga está en tu proyecto, se encuentra dentro del firewall y puede acceder a los nodos de tu clúster. Una vez que hayas configurado el balanceador de carga con la dirección IP estática y el nombre de dominio, podrás enviar solicitudes al nombre de dominio. El balanceador de carga reenviará las solicitudes a los nodos del clúster.

Habilitar IAP

En los siguientes pasos se describe cómo habilitar IAP.

  1. Comprueba si ya tienes una marca con el comando list. Solo puedes tener una marca por proyecto.

    gcloud iap oauth-brands list

    A continuación, se muestra un ejemplo de respuesta de gcloud si la marca existe:

    name: projects/[PROJECT_NUMBER]/brands/[BRAND_ID]
    applicationTitle: [APPLICATION_TITLE]
    supportEmail: [SUPPORT_EMAIL]
    orgInternalOnly: true
    
  2. Si no hay ninguna marca, usa el comando create:

    gcloud iap oauth-brands create --application_title=APPLICATION_TITLE --support_email=SUPPORT_EMAIL

    Los campos anteriores son obligatorios al llamar a esta API:

    • supportEmail: el correo de asistencia que se muestra en la pantalla de consentimiento de OAuth. Esta dirección de correo puede ser la de un usuario o un alias de Grupos de Google. Aunque las cuentas de servicio también tienen una dirección de correo, no son direcciones de correo válidas y no se pueden usar para crear una marca. Sin embargo, una cuenta de servicio puede ser la propietaria de un grupo de Google. Crea un grupo de Google o configura uno que ya tengas y asigna la cuenta de servicio que quieras como propietario del grupo.

    • applicationTitle: Nombre de la aplicación que se muestra en la pantalla de consentimiento de OAuth.

    La respuesta contiene los siguientes campos:

    name: projects/[PROJECT_NUMBER]/brands/[BRAND_ID]
    applicationTitle: [APPLICATION_TITLE]
    supportEmail: [SUPPORT_EMAIL]
    orgInternalOnly: true
    

Crear un cliente de OAuth de IAP

  1. Usa el comando create para crear un cliente. Usa la marca name del paso anterior.

    gcloud iap oauth-clients create projects/PROJECT_NUMBER/brands/BRAND-ID --display_name=NAME

    La respuesta contiene los siguientes campos:

    name: projects/[PROJECT_NUMBER]/brands/[BRAND_NAME]/identityAwareProxyClients/[CLIENT_ID]
    secret: [CLIENT_SECRET]
    displayName: [NAME]
    
  2. Usa el ID de cliente (CLIENT_ID en el paso anterior) y CLIENT_SECRET para habilitar las compras en la aplicación. Crea un secreto de Kubernetes con los materiales de tu cliente de OAuth:

    kubectl create secret generic -n istio-system my-secret --from-literal=client_id=CLIENT_ID \
    --from-literal=client_secret=CLIENT_SECRET

Desplegar el balanceador de carga

Puedes usar un recurso Ingress para crear un balanceador de carga HTTP(S) con certificados SSL configurados automáticamente. Los certificados SSL gestionados se aprovisionan, renuevan y gestionan para tu dominio.

  1. Crea un recurso ManagedCertificate. Este recurso especifica el dominio del certificado SSL. La lista spec.domains solo debe contener un dominio. No se admiten dominios comodín. En el siguiente archivo YAML, sustituye DOMAIN_NAME por el nombre de dominio que has configurado para la dirección IP estática externa.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.gke.io/v1
    kind: ManagedCertificate
    metadata:
      name: example-certificate
      namespace: istio-system
    spec:
      domains:
        - DOMAIN_NAME
    EOF
  2. Crea un recurso BackendConfig. Este recurso indica a GCLB cómo realizar comprobaciones de estado en la puerta de enlace de entrada, así como configurar Identity-Aware Proxy. Primero, recoge algunos valores de la puerta de enlace Ingress sobre las comprobaciones de estado:

    • Puerto de entrada de comprobación del estado: es el puerto de comprobación del estado de istio-ingress.

      export HC_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="status-port")].nodePort}')

    • Ruta de comprobación del estado de entrada: es la ruta de comprobación del estado de istio-ingress.

      export HC_INGRESS_PATH=$(kubectl get pods -n istio-system -l app=istio-ingressgateway -o jsonpath='{.items[0].spec.containers[?(@.name=="istio-proxy")].readinessProbe.httpGet.path}')

    cat <<EOF | kubectl apply -n istio-system -f -
    apiVersion: cloud.google.com/v1
    kind: BackendConfig
    metadata:
      name: http-hc-config
    spec:
      healthCheck:
        checkIntervalSec: 2
        timeoutSec: 1
        healthyThreshold: 1
        unhealthyThreshold: 10
        port: ${HC_INGRESS_PORT}
        type: HTTP
        requestPath: ${HC_INGRESS_PATH}
      iap:
        enabled: true
        oauthclientCredentials:
          secretName: my-secret
    EOF
  3. Anota el servicio de entrada con tu BackendConfig.

        kubectl annotate -n istio-system service/istio-ingressgateway --overwrite \
          cloud.google.com/backend-config='{"default": "http-hc-config"}' \
          cloud.google.com/neg='{"ingress":false}'
    
  4. Crea el balanceador de carga definiendo el recurso Ingress.

    • Define la anotación networking.gke.io/managed-certificates con el nombre del certificado que has creado en el paso anterior, example-certificate.

    • Asigna la anotación kubernetes.io/ingress.global-static-ip-name al nombre de la dirección IP estática que has reservado, example-static-ip.

    • Asigna el valor istio-ingressgateway a serviceName, que se usa en el recurso Gateway del ejemplo Bookinfo.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: example-ingress
      namespace: istio-system
      annotations:
        kubernetes.io/ingress.global-static-ip-name: example-static-ip
        networking.gke.io/managed-certificates: example-certificate
    spec:
      defaultBackend:
        service:
          name: istio-ingressgateway
          port:
            number: 80
    EOF
  5. En la Google Cloud consola, ve a la página Kubernetes Engine > Servicios y entradas.

    Ir a la página Servicios e Ingress

    Debería ver el mensaje "Creando entrada" en la columna Estado. Espera a que GKE aprovisione completamente el Ingress antes de continuar. Actualiza la página cada pocos minutos para obtener el estado más reciente de la entrada. Una vez que se haya aprovisionado el Ingress, puede que veas el estado "Ok" o el error "All backend services are in UNHEALTHY state" (Todos los servicios backend están en mal estado). Uno de los recursos que aprovisiona GKE es una comprobación del estado predeterminada. Si ves el mensaje de error, significa que el Ingress se ha aprovisionado y que se ha ejecutado la comprobación de estado predeterminada. Cuando veas el estado "Ok" o el error, ve a la siguiente sección para configurar las comprobaciones de estado del balanceador de carga.

Configurar la lista de acceso de IAP

Añade un usuario a la política de acceso de IAP:

gcloud beta iap web add-iam-policy-binding \
    --member=user:EMAIL_ADDRESS \
    --role=roles/iap.httpsResourceAccessor

donde EMAIL_ADDRESS es la dirección de correo completa del usuario, como alice@example.com.

  1. Prueba el balanceador de carga. Dirige tu navegador a:

    http://DOMAIN_NAME/productpage

    donde DOMAIN_NAME es el nombre de dominio que has configurado con la dirección IP estática externa.

    Deberías ver el productpage de la aplicación Bookinfo. Si actualiza la página varias veces, debería ver diferentes versiones de las reseñas, presentadas de forma rotatoria: estrellas rojas, estrellas negras y sin estrellas.

    También debes probar el acceso https a Bookinfo.

Habilitar la compatibilidad con RCToken en la malla de servicios

De forma predeterminada, IAP genera un JSON Web Token (JWT) cuyo ámbito es el cliente de OAuth. En Cloud Service Mesh, puedes configurar IAP para generar un RequestContextToken (RCToken), que es un JWT, pero con una audiencia configurable. RCToken te permite configurar la audiencia del JWT en una cadena arbitraria, que se puede usar en las políticas de Cloud Service Mesh para una autorización pormenorizada.

Para configurar el RCToken, sigue estos pasos:

  1. Crea una variable de entorno para la audiencia de RCToken. Puede ser cualquier cadena que quieras.

    export RCTOKEN_AUD="your-rctoken-aud"
    
  2. Opcional: Para completar el siguiente paso, necesitas la BACKEND_SERVICE_ID. Si necesitas saber el BACKEND_SERVICE_ID, ejecuta el siguiente comando:

    kubectl -n istio-system get Ingress example-ingress -o json | jq \
     '.metadata.annotations."ingress.kubernetes.io/backends"'
    

    El resultado esperado es similar al de "{\"BACKEND_SERVICE_ID\":\"HEALTHY\"}". Por ejemplo, "ingress.kubernetes.io/backends": "{\"k8s-be-31224--51f3b55cd1457fb6\":\"HEALTHY\"}". El BACKEND_SERVICE_ID de este ejemplo es k8s-be-31224--51f3b55cd1457fb6.

  3. Obtiene los ajustes de compra en la aplicación.

    gcloud iap settings get --format json \
    --project=${PROJECT_ID} --resource-type=compute --service=BACKEND_SERVICE_ID > iapSettings.json
    
  4. Actualiza IapSettings con la audiencia de RCToken.

    cat iapSettings.json | jq --arg RCTOKEN_AUD_STR $RCTOKEN_AUD \
    '. + {applicationSettings: {csmSettings: {rctokenAud: $RCTOKEN_AUD_STR}}}' \
    > updatedIapSettings.json
    
    gcloud iap settings set updatedIapSettings.json --format json \
    --project=${PROJECT_ID} --resource-type=compute --service=BACKEND_SERVICE_ID
    
  5. Habilita la autenticación RCToken en la puerta de enlace de entrada de Istio.

    cat <<EOF | kubectl apply -f -
    apiVersion: "security.istio.io/v1beta1"
    kind: "RequestAuthentication"
    metadata:
      name: "ingressgateway-jwt-policy"
      namespace: "istio-system"
    spec:
      selector:
        matchLabels:
          app: istio-ingressgateway
      jwtRules:
      - issuer: "https://cloud.google.com/iap"
        jwksUri: "https://www.gstatic.com/iap/verify/public_key-jwk"
        audiences:
        - $RCTOKEN_AUD
        fromHeaders:
        - name: ingress-authorization
          prefix: "Istio "
        outputPayloadToHeader: "verified-jwt"
        forwardOriginalToken: true
    EOF
    
  6. Opcional: Asegúrate de que se rechacen las solicitudes que no tengan JWTs válidos:

    .

      cat <<EOF | kubectl apply -f -
      apiVersion: security.istio.io/v1beta1
      kind: AuthorizationPolicy
      metadata:
        name: iap-gateway-require-jwt
        namespace: istio-system
      spec:
        selector:
          matchLabels:
            app: istio-iap-ingressgateway
        action: DENY
        rules:
          - from:
              - source:
                  notRequestPrincipals: ["*"]
      EOF
      

  7. Comprueba que las solicitudes a Bookinfo productpage se sigan realizando correctamente:

    http://DOMAIN_NAME/productpage

Para probar la política, sigue estos pasos:

  1. Crea un objeto de solicitud IapSettings, pero asigna a rctokenAud una cadena diferente:

    cat iapSettings.json | jq --arg RCTOKEN_AUD_STR wrong-rctoken-aud \
    '. + {applicationSettings: {csmSettings: {rctokenAud: $RCTOKEN_AUD_STR}}}' \
    > wrongIapSettings.json
    
  2. Llama a la API IapSettings para definir la audiencia de RCToken.

    gcloud beta iap settings set wrongIapSettings.json --project=PROJECT_ID --resource-type=compute --service=BACKEND_SERVICE
  3. Haz una solicitud a Bookinfo productpage y debería fallar:

    http://DOMAIN_NAME/productpage

Eliminar los recursos utilizados

Después de completar este tutorial, elimina los siguientes recursos para evitar que se apliquen cargos no deseados a tu cuenta:

  1. Elimina el certificado gestionado:

    kubectl delete managedcertificates example-certificate
  2. Elimina el Ingress, lo que desasigna los recursos de balanceo de carga:

    kubectl -n istio-system delete ingress example-ingress

  3. Elimina la dirección IP estática:

    gcloud compute addresses delete example-static-ip --global

    Si lo haces, asegúrate de eliminar la dirección IP del registrador de tu dominio.

  4. Elimina el clúster, lo que eliminará los recursos que lo componen, como las instancias de computación, los discos y los recursos de red:

    gcloud container clusters delete CLUSTER_NAME