Configura la seguridad del servicio de Traffic Director con gRPC sin proxy

En esta guía, se muestra cómo configurar un servicio de seguridad para una malla de servicios de gRPC sin proxy. Puedes ver implementaciones de ejemplo con grpc-java en github.

Requisitos

Antes de configurar la seguridad del servicio para la malla de servicios sin proxy de gRPC, asegúrate de cumplir con los siguientes requisitos.

Preparando para la configuración

La seguridad de PSM agrega seguridad a la malla de servicios configurada para el balanceo de cargas en función de la documentación de servicios de gRPC sin proxy. Por ejemplo, el cliente usa el esquema xds: en el URI para acceder al servicio, lo que habilita el balanceo de cargas PSM y las funciones de descubrimiento de extremos.

Actualiza los clientes y servidores de gRPC a la versión correcta

Compila o vuelve a compilar tus aplicaciones con la versión mínima de gRPC compatible con tu lenguaje.

Actualiza el archivo de arranque

Las aplicaciones de gRPC usan un solo archivo de arranque, el cual debe tener todos los campos que requiere el código del cliente y el servidor de gRPC. Un generador de arranque genera de forma automática el archivo de arranque para incluir las marcas y los valores que necesita la seguridad de PSM. Para obtener más información, consulta la sección Archivo de arranque, que incluye un archivo de arranque de muestra.

Descripción general de la configuración

Este proceso de configuración es una extensión de la configuración de Traffic Director con GKE y servicios de gRPC sin proxy. Se hace referencia a los pasos existentes sin modificar de esa configuración cada vez que se aplican.

Las principales mejoras en la configuración de Traffic Director con GKE son las siguientes:

  1. Configuración de CA Service.
  2. Crear un clúster de GKE y habilitar los certificados de carga de trabajo de GKE en el clúster
  3. Configurar la emisión de certificados de carga de trabajo en el clúster
  4. Crear las cuentas de servicio de cliente y servidor
  5. Configurar el servidor de ejemplo que usa las API de xDS y las credenciales del servidor de xDS para obtener la configuración de seguridad de Traffic Director
  6. Configurar el cliente de ejemplo que usa credenciales xDS
  7. Actualizar la configuración de Traffic Director para incluir la configuración de seguridad
  8. Definir una variable de entorno adicional que el código de seguridad de gRPC sin proxy necesita:
    • GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT: Configúralo como true para habilitar el código de vista previa de seguridad de PSM.

Puedes ver una configuración de ejemplo para gRPC para Java en github.

Actualizar la herramienta de línea de comandos de gcloud

Para actualizar la herramienta de línea de comandos de gcloud, ejecuta el siguiente comando:

gcloud components update

Configura variables de entorno

En esta guía, usas los comandos de Cloud Shell, y la información repetida en los comandos se representa con múltiples variables de entorno. Establece tus valores específicos según las siguientes variables de entorno en el entorno de shell antes de ejecutar los comandos. Cada línea de comentario indica el significado de la variable de entorno asociada.

# Your project ID
PROJECT_ID=YOUR_PROJECT_ID

# GKE cluster name and zone for this example.
CLUSTER_NAME="secure-psm-cluster"
ZONE="us-east1-d"

# GKE cluster URL derived from the above
GKE_CLUSTER_URL="https://container.googleapis.com/v1/projects/${PROJECT_ID}/locations/${ZONE}/clusters/${CLUSTER_NAME}"

# Workload pool to be used with the GKE cluster
WORKLOAD_POOL="${PROJECT_ID}.svc.id.goog"

# Kubernetes namespace to run client and server demo.
K8S_NAMESPACE='default'
DEMO_BACKEND_SERVICE_NAME='grpc-gke-helloworld-service'

# Compute other values
# Project number for your project
PROJNUM=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")

# VERSION is the GKE cluster version. Install and use the most recent version
# from the rapid release channel and substitute its version for
# CLUSTER_VERSION, for example:
# VERSION=latest available version
# Note that the minimum required cluster version is 1.20.6-gke.1000.
VERSION="CLUSTER_VERSION"
CLUSTER_LOCATION="us-east1"
SA_GKE=service-${PROJNUM}@container-engine-robot.iam.gserviceaccount.com

Habilita el acceso a las API necesarias

En esta sección, se explica cómo habilitar el acceso a las API necesarias.

  1. Ejecuta el siguiente comando a fin de habilitar Traffic Director y otras API necesarias para la seguridad de la malla de servicios de gRPC sin proxy.

    gcloud services enable \
        container.googleapis.com \
        cloudresourcemanager.googleapis.com \
        compute.googleapis.com \
        trafficdirector.googleapis.com \
        networkservices.googleapis.com \
        networksecurity.googleapis.com \
        privateca.googleapis.com
    
  2. Ejecuta el siguiente comando para permitir que la cuenta de servicio predeterminada acceda a la API de seguridad de Traffic Director.

    GSA_EMAIL=$(gcloud iam service-accounts list --format='value(email)' --filter='displayName:Compute Engine       default service account')
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member serviceAccount:${GSA_EMAIL} \
       --role roles/trafficdirector.client
    

Crea o actualiza un clúster de GKE

La seguridad de PSM depende de la integración del servicio de CA con el componente de GKE. El clúster de GKE debe cumplir con los siguientes requisitos, además de los requisitos de la malla de servicios de gRPC sin proxy:

  • Usa la versión más reciente del clúster de GKE del canal rápido. Se requiere una versión mínima del clúster de 1.20.6-gke.1000.
  • El clúster de GKE debe estar habilitado y configurado con certificados de carga de trabajo, como se describe en Crea autoridades certificadoras para emitir certificados.

Antes de crear el clúster, lee las Notas de la versión del canal rápido y asegúrate de tener instalada la versión más reciente. De lo contrario, en las notas de la versión, haz clic en el vínculo a la versión más reciente y, luego, instala esa versión.

  1. Crea un clúster nuevo que use Workload Identity. Si actualizas un clúster existente, avanza al siguiente paso.

    gcloud beta container clusters create ${CLUSTER_NAME} \
      --release-channel rapid \
      --scopes=cloud-platform \
      --image-type=cos_containerd \
      --zone=${ZONE} \
      --workload-pool ${PROJECT_ID}.svc.id.goog \
      --enable-workload-certificates \
      --cluster-version ${VERSION} \
      --tags=allow-health-checks \
      --enable-ip-alias \
      --workload-metadata=GKE_METADATA
    

    Reemplaza lo siguiente:

    • ${CLUSTER_NAME}: el nombre de tu clúster nuevo.
    • ${CLUSTER_LOCATION}: la región del clúster nuevo. Para crear un clúster zonal, usa --zone en su lugar.
    • ${PROJECT_ID}: El ID del proyecto de Google Cloud
    • ${VERSION}: Es la versión de tu clúster, que debe ser de 1.20.6-gke.1000 como mínimo. Te recomendamos que uses la versión más reciente del clúster de GKE.
  2. Si usas un clúster existente, puedes activar Workload Identity y los certificados de carga de trabajo de GKE. Asegúrate de que el clúster se haya creado con la marca --enable-ip-alias, que no se puede usar con el comando update.

    • CLUSTER_NAME: Es el nombre del clúster existente.
    • PROJECT_ID: El ID del proyecto de Google Cloud.
    gcloud beta container clusters update ${CLUSTER_NAME} \
      --workload-pool ${PROJECT_ID}.svc.id.goog \
      --enable-workload-certificates
    
  3. Obtén credenciales para el clúster y reemplaza ${CLUSTER_LOCATION} por la región de tu clúster:

    gcloud container clusters get-credentials ${CLUSTER_NAME} \
      --zone=${ZONE}
    

Registra clústeres creados recientemente con GKE Hub

Registra el clúster que creaste en Crea un clúster de GKE con una flota, también conocida como GKE Hub. El registro del clúster facilita la configuración de clústeres en varios proyectos.

Ten en cuenta que estos pasos pueden demorar hasta diez minutos en completarse.

  1. Habilita la API de GKE para tu proyecto:

    gcloud services enable \
        gkehub.googleapis.com
    
  2. Registra tu clúster:

    MANIFEST_FILE_NAME="/tmp/manifest"
    gcloud beta container hub memberships register ${CLUSTER_NAME} \
      --gke-cluster=${ZONE}/${CLUSTER_NAME} \
      --enable-workload-identity \
       --manifest-output-file=${MANIFEST_FILE_NAME}
    

    Reemplaza las variables de la siguiente manera:

    • ${CLUSTER_NAME}: el nombre del clúster
    • ${CLUSTER_LOCATION}: La zona de tu clúster.
    • ${MANIFEST_FILE_NAME}: La ruta de acceso del archivo en la que estos comandos generan el manifiesto para el registro.

    Cuando el proceso de registro se realice de forma correcta, verás un mensaje como el siguiente:

    Finished registering the cluster ${CLUSTER_NAME} with the Hub.
  3. Aplica el archivo de manifiesto generado a tu clúster:

    kubectl apply -f ${MANIFEST_FILE_NAME}
    

    Cuando el proceso de la aplicación se realice de forma correcta, verás mensajes como los siguientes:

    namespace/gke-connect created
    serviceaccount/connect-agent-sa created
    podsecuritypolicy.policy/gkeconnect-psp created
    role.rbac.authorization.k8s.io/gkeconnect-psp:role created
    rolebinding.rbac.authorization.k8s.io/gkeconnect-psp:rolebinding created
    role.rbac.authorization.k8s.io/agent-updater created
    rolebinding.rbac.authorization.k8s.io/agent-updater created
    role.rbac.authorization.k8s.io/gke-connect-agent-20210416-01-00 created
    clusterrole.rbac.authorization.k8s.io/gke-connect-impersonation-20210416-01-00 created
    clusterrolebinding.rbac.authorization.k8s.io/gke-connect-impersonation-20210416-01-00 created
    clusterrolebinding.rbac.authorization.k8s.io/gke-connect-feature-authorizer-20210416-01-00 created
    rolebinding.rbac.authorization.k8s.io/gke-connect-agent-20210416-01-00 created
    role.rbac.authorization.k8s.io/gke-connect-namespace-getter created
    rolebinding.rbac.authorization.k8s.io/gke-connect-namespace-getter created
    secret/http-proxy created
    deployment.apps/gke-connect-agent-20210416-01-00 created
    service/gke-connect-monitoring created
    secret/creds-gcp created
    
  4. Obtén el recurso de la membresía del clúster:

    kubectl get memberships membership -oyaml
    

    El resultado debe incluir el grupo de Workorkad Identity asignado por el centro, en el que ${PROJECT_ID} es el ID de tu proyecto:

    workload_identity_pool: ${PROJECT_ID}.svc.id.goog
    

    Esto significa que el clúster se registró correctamente.

Crea autoridades certificadoras para emitir certificados

Para emitir certificados a tus pods, crea las siguientes autoridades certificadoras (CA) con el servicio de CA:

  • CA raíz. Esta es la raíz de confianza de todos los certificados de carga de trabajo emitidos. Puedes usar una CA raíz existente si tienes una. Crea la CA raíz en el nivel enterprise, que está destinado a la emisión de certificados de larga duración y bajo volumen.
  • CA subordinada. Esta CA emite certificados para cargas de trabajo. Crea la CA subordinada en la región en la que se implementa el clúster. Crea la CA subordinada en el nivel devops, que está destinado a la emisión de certificados de corta duración y gran volumen.

Crear una CA subordinada es opcional, pero te recomendamos crear una en lugar de usar tu CA raíz para emitir certificados de carga de trabajo de GKE.

La CA subordinada puede estar en una región diferente de tu clúster, pero recomendamos crearla en la misma región que tu clúster para optimizar el rendimiento. Sin embargo, puedes crear las CA subordinadas y raíz en regiones diferentes sin ningún impacto en el rendimiento o la disponibilidad.

Estas regiones son compatibles durante la vista previa pública para el servicio de CA:

Nombre de la región Descripción de la región
asia-southeast1 Singapur
europe-west1 Bélgica
europe-west4 Netherlands
us-central1 Iowa
us-east1 Carolina del Sur
us-west1 Oregón

La lista de ubicaciones compatibles también se puede verificar ejecutando el siguiente comando:

gcloud beta privateca locations list
  1. Crea una CA raíz:

    gcloud beta privateca roots create ${ROOT_CA_NAME} \
      --subject "CN=${ROOT_CA_NAME}, O=${ROOT_CA_ORGANIZATION}" \
      --max-chain-length=1 \
      --location ${ROOT_CA_LOCATION} \
      --tier enterprise
    

    En esta configuración de demostración, usa los siguientes valores para las variables:

    • ROOT_CA_NAME=pkcs2-ca
    • ROOT_CA_LOCATION=us-east1
    • ROOT_CA_ORGANIZATION="TestCorpLLC"
  2. Crea una CA subordinada en la misma región que tu clúster:

    gcloud beta privateca subordinates create ${SUBORDINATE_CA_NAME} \
      --issuer ${ROOT_CA_NAME} \
      --issuer-location ${ROOT_CA_LOCATION} \
      --subject "CN=${SUBORDINATE_CA_NAME}, O=${SUBORDINATE_CA_ORGANIZATION}" \
      --location ${SUBORDINATE_CA_LOCATION} \
      --tier devops
    

    En esta configuración de demostración, usa los siguientes valores para las variables:

    • SUBORDINATE_CA_NAME="td-ca"
    • SUBORDINATE_CA_ORGANIZATION="TestCorpLLC"
    • SUBORDINATE_CA_LOCATION=us-east1
  3. Otorga la función privateca.admin en el servicio de CA a las personas que necesiten modificar las políticas de IAM. En este caso, MEMBER es una persona que necesita este acceso, en particular, cualquier persona que realice los siguientes pasos que otorgan las funciones privateca.auditor y privateca.certificateManager:

    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member=MEMBER \
      --role=roles/privateca.admin
    
  4. Otorga la función privateca.auditor de IAM a la CA raíz para permitir el acceso desde la cuenta de servicio de GKE:

    gcloud beta privateca roots add-iam-policy-binding ${ROOT_CA_NAME} \
      --location "${ROOT_CA_LOCATION}" \
      --role roles/privateca.auditor \
      --member="serviceAccount:${SA_GKE}"
    
  5. Otorga la función privateca.certificateManager de IAM para que la CA subordinada permita el acceso desde la cuenta de servicio de GKE:

    gcloud beta privateca roots add-iam-policy-binding "${SUBORDINATE_CA_NAME}" \
      --location "${SUBORDINATE_CA_LOCATION}" \
      --role roles/privateca.certificateManager \
      --member="serviceAccount:${SA_GKE}"
    
  6. Guarda la siguiente configuración de YAML WorkloadCertificateConfig para indicarle a tu clúster cómo emitir certificados de carga de trabajo:

    ISSUING_CA_URI="//privateca.googleapis.com/projects/${PROJECT_ID}/locations/${SUBORDINATE_CA_LOCATION}/certificateAuthorities/${SUBORDINATE_CA_NAME}"
    
    cat << EOF > WorkloadCertificateConfig.yaml
    apiVersion: security.cloud.google.com/v1alpha1
    kind: WorkloadCertificateConfig
    metadata:
      name: default
    spec:
      # Required. The SPIFFE trust domain. This must match your cluster's
      # Workload Identity pool.
      trustDomain: ${PROJECT_ID}.svc.id.goog
    
      # Required. The CA service that issues your certificates.
      certificateAuthorityConfig:
        certificateAuthorityServiceConfig:
          endpointURI: ${ISSUING_CA_URI}
    
      # Required. The key algorithm to use. Choice of RSA or ECDSA.
      #
      # To maximize compatibility with various TLS stacks, your workloads
      # should use keys of the same family as your root and subordinate CAs.
      #
      # To use RSA, specify configuration such as:
      #   keyAlgorithm:
      #     rsa:
      #       modulusSize: 4096
      #
      # Currently, the supported ECDSA curves are "P256" and "P384", and the
      # supported RSA modulus sizes are 2048, 3072 and 4096.
      keyAlgorithm:
        rsa:
          modulusSize: 4096
    
      # Optional. Validity duration of issued certificates, in seconds.
      #
      # Defaults to 86400 (1 day) if not specified.
      validityDurationSeconds: 86400
    
      # Optional. Try to start rotating the certificate once this
      # percentage of validityDurationSeconds is remaining.
      #
      # Defaults to 50 if not specified.
      rotationWindowPercentage: 50
    EOF
    

    Reemplaza lo siguiente:

    • ${PROJECT_ID}: El ID del proyecto en el que se ejecuta tu clúster.
    • ${ISSUING_CA_URI: El URI completamente calificado de la CA que emite los certificados de carga de trabajo. Puede ser tu CA subordinada (recomendado) o la CA raíz. El formato es //privateca.googleapis.com/projects/PROJECT_ID/locations/ISSUING_CA_LOCATION/certificateAuthorities/ISSUING_CA_NAME.
  7. Guarda la siguiente configuración de YAML TrustConfig para indicarle a tu clúster cómo confiar en los certificados emitidos:

    ROOT_CA_URI="//privateca.googleapis.com/projects/${PROJECT_ID}/locations/${ROOT_CA_LOCATION}/certificateAuthorities/${ROOT_CA_NAME}"
    
    cat << EOF > TrustConfig.yaml
    apiVersion: security.cloud.google.com/v1alpha1
    kind: TrustConfig
    metadata:
      name: default
    spec:
      # You must include a trustStores entry for the trust domain that
      # your cluster is enrolled in.
      trustStores:
      - trustDomain: ${PROJECT_ID}.svc.id.goog
        # Trust identities in this trustDomain if they appear in a certificate
        # that chains up to this root CA.
        trustAnchors:
        - certificateAuthorityServiceURI: ${ROOT_CA_URI}
    EOF
    

    Reemplaza lo siguiente:

    • ${PROJECT_ID}: Es el ID del proyecto de tu clúster.
    • ${ROOT_CA_URI}: El URI completamente calificado de la CA raíz. El formato es //privateca.googleapis.com/projects/PROJECT_ID/locations/ROOT_CA_LOCATION/certificateAuthorities/ROOT_CA_NAME.
  8. Aplica las configuraciones a tu clúster.

    kubectl apply -f WorkloadCertificateConfig.yaml
    kubectl apply -f TrustConfig.yaml
    

Crea un servicio de gRPC sin proxy con NEG

Para la seguridad de PSM, necesitas un servidor gRPC sin proxy capaz de usar xDS a fin de adquirir la configuración de seguridad de Traffic Director. Este paso es similar al de Configura servicios de GKE con NEG de la guía de configuración del balanceo de cargas de PSM, pero debes usar el servidor helloworld con xDS habilitado del ejemplo de xDS en el repositorio grpc-java en lugar de la imagen de java-example-hostname.

Debes compilar y ejecutar este servidor en un contenedor compilado a partir de una imagen de openjdk:8-jdk. También usarás la función de NEG con nombre, que te permite especificar un nombre para el NEG. Esto simplifica los pasos posteriores porque la implementación conoce el nombre del NEG sin tener que buscarlo.

El siguiente es un ejemplo completo de la especificación de Kubernetes del servidor de gRPC. Ten en cuenta lo siguiente:

  • La especificación crea una cuenta de servicio de Kubernetes, example-grpc-server, que usa el Pod del servidor de gRPC.
  • En la especificación, se usa el campo name en la anotación cloud.google.com/neg del servicio para especificar el nombre de NEG example-grpc-server.
  • La variable ${PROJNUM} representa el número de tu proyecto.
  • En la especificación, se usa la sección initContainers para ejecutar un generador de arranque a fin de propagar el archivo de arranque que necesita la biblioteca de gRPC sin proxy. Este archivo de arranque reside en /tmp/grpc-xds/td-grpc-bootstrap.json en el contenedor del servidor de gRPC llamado example-grpc-server.

Agrega la siguiente anotación a las especificaciones del Pod:

 annotations:
   security.cloud.google.com/use-workload-certificates: ""

Puedes ver la ubicación correcta en la especificación completa que sigue.

Cuando se crea, cada Pod obtiene un volumen en /var/run/secrets/workload-spiffe-credentials. Este volumen contiene lo siguiente:

  • private_key.pem es una clave privada generada de forma automática.
  • certificates.pem es un conjunto de certificados con formato PEM que se puede presentar en otro Pod como la cadena de certificados de cliente o usarse como una cadena de certificados de servidor.
  • ca_certificates.pem es un conjunto de certificados con formato PEM que se usa como anclajes de confianza cuando se valida la cadena de certificados de cliente que presenta otro Pod o la cadena de certificados de servidor que se recibe cuando se conecta a otro Pod.

Ten en cuenta que ca_certificates.pem contiene certificados para el dominio de confianza local de las cargas de trabajo, que es el grupo de cargas de trabajo del clúster.

El certificado de hoja en certificates.pem contiene la siguiente aserción de identidad de SPIFFE de texto sin formato:

spiffe://WORKLOAD_POOL/ns/NAMESPACE/sa/KUBERNETES_SERVICE_ACCOUNT

En esta aserción, sucede lo siguiente:

  • WORKLOAD_POOL es el nombre del grupo de cargas de trabajo del clúster.
  • NAMESPACE es el espacio de nombres de tu cuenta de servicio de Kubernetes.
  • KUBERNETES_SERVICE_ACCOUNT es el nombre de tu cuenta de servicio de Kubernetes.

Esta es la especificación que se debe utilizar en este ejemplo, en el archivo example-grpc-server.yaml:

Java

cat << EOF > example-grpc-server.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: example-grpc-server
  namespace: default
  annotations:
    iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
---
apiVersion: v1
kind: Service
metadata:
  name: example-grpc-server
  namespace: default
  labels:
    k8s-app: example-grpc-server
  annotations:
    cloud.google.com/neg: '{"exposed_ports":{"8080":{"name": "example-grpc-server"}}}'
spec:
  ports:
  - name: helloworld
    port: 8080
    protocol: TCP
    targetPort: 50051
  selector:
    k8s-app: example-grpc-server
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-grpc-server
  namespace: default
  labels:
    k8s-app: example-grpc-server
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: example-grpc-server
  strategy: {}
  template:
    metadata:
      annotations:
         security.cloud.google.com/use-workload-certificates: ""
      labels:
        k8s-app: example-grpc-server
    spec:
      containers:
      - image: openjdk:8-jdk
        imagePullPolicy: IfNotPresent
        name: example-grpc-server
        command:
        - /bin/sleep
        - inf
        env:
        - name: GRPC_XDS_BOOTSTRAP
          value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
        - name: GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT
          value: "true"
        ports:
        - protocol: TCP
          containerPort: 50051
        resources:
          limits:
            cpu: 800m
            memory: 512Mi
          requests:
            cpu: 100m
            memory: 512Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/grpc-xds/
      initContainers:
      - name: grpc-td-init
        image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.12.0-rc1
        imagePullPolicy: Always
        args:
        - --output
        - "/tmp/bootstrap/td-grpc-bootstrap.json"
        - --include-psm-security-experimental
        resources:
          limits:
            cpu: 100m
            memory: 100Mi
          requests:
            cpu: 10m
            memory: 100Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/bootstrap/
      serviceAccountName: example-grpc-server
      volumes:
      - name: grpc-td-conf
        emptyDir:
          medium: Memory
EOF

C++

cat << EOF > example-grpc-server.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: example-grpc-server
  namespace: default
  annotations:
    iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
---
apiVersion: v1
kind: Service
metadata:
  name: example-grpc-server
  namespace: default
  labels:
    k8s-app: example-grpc-server
  annotations:
    cloud.google.com/neg: '{"exposed_ports":{"8080":{"name": "example-grpc-server"}}}'
spec:
  ports:
  - name: helloworld
    port: 8080
    protocol: TCP
    targetPort: 50051
  selector:
    k8s-app: example-grpc-server
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-grpc-server
  namespace: default
  labels:
    k8s-app: example-grpc-server
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: example-grpc-server
  strategy: {}
  template:
    metadata:
      annotations:
         security.cloud.google.com/use-workload-certificates: ""
      labels:
        k8s-app: example-grpc-server
    spec:
      containers:
      - image: phusion/baseimage:18.04-1.0.0
        imagePullPolicy: IfNotPresent
        name: example-grpc-server
        command:
        - /bin/sleep
        - inf
        env:
        - name: GRPC_XDS_BOOTSTRAP
          value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
        - name: GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT
          value: "true"
        ports:
        - protocol: TCP
          containerPort: 50051
        resources:
          limits:
            cpu: 8
            memory: 8Gi
          requests:
            cpu: 300m
            memory: 512Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/grpc-xds/
      initContainers:
      - name: grpc-td-init
        image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.12.0-rc1
        imagePullPolicy: Always
        args:
        - --output
        - "/tmp/bootstrap/td-grpc-bootstrap.json"
        - --include-psm-security-experimental
        resources:
          limits:
            cpu: 100m
            memory: 100Mi
          requests:
            cpu: 10m
            memory: 100Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/bootstrap/
      serviceAccountName: example-grpc-server
      volumes:
      - name: grpc-td-conf
        emptyDir:
          medium: Memory
EOF

Python

cat << EOF > example-grpc-server.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: example-grpc-server
  namespace: default
  annotations:
    iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
---
apiVersion: v1
kind: Service
metadata:
  name: example-grpc-server
  namespace: default
  labels:
    k8s-app: example-grpc-server
  annotations:
    cloud.google.com/neg: '{"exposed_ports":{"8080":{"name": "example-grpc-server"}}}'
spec:
  ports:
  - name: helloworld
    port: 8080
    protocol: TCP
    targetPort: 50051
  selector:
    k8s-app: example-grpc-server
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-grpc-server
  namespace: default
  labels:
    k8s-app: example-grpc-server
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: example-grpc-server
  strategy: {}
  template:
    metadata:
      annotations:
         security.cloud.google.com/use-workload-certificates: ""
      labels:
        k8s-app: example-grpc-server
    spec:
      containers:
      - image: phusion/baseimage:18.04-1.0.0
        imagePullPolicy: IfNotPresent
        name: example-grpc-server
        command:
        - /bin/sleep
        - inf
        env:
        - name: GRPC_XDS_BOOTSTRAP
          value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
        - name: GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT
          value: "true"
        ports:
        - protocol: TCP
          containerPort: 50051
        resources:
          limits:
            cpu: 8
            memory: 8Gi
          requests:
            cpu: 300m
            memory: 512Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/grpc-xds/
      initContainers:
      - name: grpc-td-init
        image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.12.0-rc1
        imagePullPolicy: Always
        args:
        - --output
        - "/tmp/bootstrap/td-grpc-bootstrap.json"
        - --include-psm-security-experimental
        resources:
          limits:
            cpu: 100m
            memory: 100Mi
          requests:
            cpu: 10m
            memory: 100Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/bootstrap/
      serviceAccountName: example-grpc-server
      volumes:
      - name: grpc-td-conf
        emptyDir:
          medium: Memory
EOF

Go

cat << EOF > example-grpc-server.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: example-grpc-server
  namespace: default
  annotations:
    iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
---
apiVersion: v1
kind: Service
metadata:
  name: example-grpc-server
  namespace: default
  labels:
    k8s-app: example-grpc-server
  annotations:
    cloud.google.com/neg: '{"exposed_ports":{"8080":{"name": "example-grpc-server"}}}'
spec:
  ports:
  - name: helloworld
    port: 8080
    protocol: TCP
    targetPort: 50051
  selector:
    k8s-app: example-grpc-server
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-grpc-server
  namespace: default
  labels:
    k8s-app: example-grpc-server
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: example-grpc-server
  strategy: {}
  template:
    metadata:
      annotations:
         security.cloud.google.com/use-workload-certificates: ""
      labels:
        k8s-app: example-grpc-server
    spec:
      containers:
      - image: golang:1.16-alpine
        imagePullPolicy: IfNotPresent
        name: example-grpc-server
        command:
        - /bin/sleep
        - inf
        env:
        - name: GRPC_XDS_BOOTSTRAP
          value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
        - name: GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT
          value: "true"
        ports:
        - protocol: TCP
          containerPort: 50051
        resources:
          limits:
            cpu: 8
            memory: 8Gi
          requests:
            cpu: 300m
            memory: 512Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/grpc-xds/
      initContainers:
      - name: grpc-td-init
        image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.12.0-rc1
        imagePullPolicy: Always
        args:
        - --output
        - "/tmp/bootstrap/td-grpc-bootstrap.json"
        - --include-psm-security-experimental
        resources:
          limits:
            cpu: 100m
            memory: 100Mi
          requests:
            cpu: 10m
            memory: 100Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/bootstrap/
      serviceAccountName: example-grpc-server
      volumes:
      - name: grpc-td-conf
        emptyDir:
          medium: Memory
EOF
  1. Aplica la especificación:

    kubectl apply -f example-grpc-server.yaml
    
  2. Otorga las funciones correctas a la cuenta de servicio:

    gcloud iam service-accounts add-iam-policy-binding \
      --role roles/iam.workloadIdentityUser \
      --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/example-grpc-server]" \
      ${PROJNUM}-compute@developer.gserviceaccount.com
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/example-grpc-server]" \
      --role roles/trafficdirector.client
    
  3. Usa las instrucciones en Configura servicios de GKE con NEG para verificar que el servicio y el Pod se creen de forma correcta.

  4. Verifica que el nombre de NEG sea correcto:

    gcloud compute network-endpoint-groups list | \
       grep example-grpc-server | awk '{print $1}'
    

    Este comando debe mostrar el nombre de NEG example-grpc-server.

Configura Traffic Director con componentes del balanceo de cargas de Google Cloud

Los pasos de esta sección son similares a los de Configura Traffic Director con componentes del balanceo de cargas, pero hay algunos cambios, como se describe a continuación.

Crea la verificación de estado, la regla de firewall y el servicio de backend

Cuando el servidor de gRPC está configurado para usar mTLS, las verificaciones de estado de gRPC no funcionan porque el cliente de verificación de estado no puede presentar un certificado de cliente válido a los servidores. Para abordar esto, puedes usar uno de dos métodos.

En el primer método, el servidor debe crear un puerto de entrega adicional que se designe como puerto de verificación de estado. Esto se conecta a un servicio de verificación de estado especial, como texto sin formato o TLS para ese puerto.

El servidor de ejemplo helloworld de xDS usa PORT_NUMBER + 1 como el puerto de verificación de estado de texto simple. En el ejemplo, se usa 50052 como puerto de verificación de estado porque 50051 es el puerto del servidor de aplicaciones de gRPC.

En el segundo método, debes configurar la verificación de estado para comprobar solo la conectividad TCP con el puerto de entrega de la aplicación. Esto solo verifica la conectividad y genera tráfico innecesario al servidor cuando hay protocolos de enlace TLS fallidos. Por este motivo, te recomendamos que uses el primer método.

  1. Crea la verificación de estado.

    • Si creas un puerto de entrega designado para la verificación de estado, que es el enfoque que recomendamos, usa este comando:

      gcloud compute health-checks create grpc grpc-gke-helloworld-hc \
       --enable-logging --port 50052
      
    • Si creas una verificación de estado de TCP, la cual no recomendamos, usa este comando:

      gcloud compute health-checks create tcp grpc-gke-helloworld-hc \
      --use-serving-port
      
  2. Crea el firewall:

    gcloud compute firewall-rules create grpc-gke-allow-health-checks \
      --network default --action allow --direction INGRESS \
      --source-ranges 35.191.0.0/16,130.211.0.0/22 \
      --target-tags allow-health-checks \
      --rules tcp:50051-50052
    
  3. Crea el servicio de backend:

    gcloud compute backend-services create grpc-gke-helloworld-service \
       --global \
       --load-balancing-scheme=INTERNAL_SELF_MANAGED \
       --protocol=GRPC \
       --health-checks grpc-gke-helloworld-hc
    
  4. Vincula el NEG al servicio de backend:

    gcloud compute backend-services add-backend grpc-gke-helloworld-service \
       --global \
       --network-endpoint-group example-grpc-server \
       --network-endpoint-group-zone ${ZONE} \
       --balancing-mode RATE \
       --max-rate-per-endpoint 5
    

Crea el mapa de reglas de enrutamiento

Esto es similar a la manera en que se crea un mapa de reglas de enrutamiento en la configuración de Traffic Director con Google Kubernetes Engine y los servicios de gRPC sin proxy.

  1. Crea el mapa de URL:

    gcloud compute url-maps create grpc-gke-url-map \
       --default-service grpc-gke-helloworld-service
    
  2. Agrega el comparador de rutas de acceso al mapa de URL:

    gcloud compute url-maps add-path-matcher grpc-gke-url-map \
       --default-service grpc-gke-helloworld-service \
       --path-matcher-name grpc-gke-path-matcher \
       --new-hosts helloworld-gke:8000
    
  3. Crea el proxy de gRPC de destino.

    gcloud compute target-grpc-proxies create grpc-gke-proxy \
       --url-map grpc-gke-url-map --validate-for-proxyless
    
  4. Crea la regla de reenvío:

    gcloud compute forwarding-rules create grpc-gke-forwarding-rule \
      --global \
      --load-balancing-scheme=INTERNAL_SELF_MANAGED \
      --address=0.0.0.0 \
      --target-grpc-proxy=grpc-gke-proxy \
      --ports 8000 \
      --network default
    

Configura Traffic Director con seguridad de gRPC sin proxy

En este ejemplo, se muestra cómo configurar mTLS en el lado del cliente y del servidor.

Configura mTLS del lado del servidor

Primero, crea una política de TLS para el servidor. La política solicita al gRPC del lado del servidor que use la configuración del complemento certificateProvicerInstance identificada por el nombre google_cloud_private_spiffe para el certificado de identidad, que es parte de serverCertificate. En la sección mtlsPolicy, se indica la seguridad mTLS y se usa el mismo google_cloud_private_spiffe que la configuración del complemento para clientValidationCa, que es la especificación del certificado raíz (validación).

A continuación, crea una política de extremos. Esto especifica que un backend, por ejemplo, un servidor de gRPC, que usa el puerto 50051 con cualquier etiqueta de metadatos o ninguna etiqueta, recibe la política de TLS del servidor adjunta llamada server_mtls_policy. Debes especificar etiquetas de metadatos mediante MATCH_ANY y un array vacío []. Crea la política de extremos con un archivo temporal ep-mtls-psms.yaml que contenga los valores del recurso de la política de extremos con la política que ya definiste.

  1. Crea un archivo temporal server-mtls-policy.yaml en el directorio actual con los valores del recurso de la política de TLS del servidor:

    name: "server_mtls_policy"
    serverCertificate:
      certificateProviderInstance:
        pluginInstance: google_cloud_private_spiffe
    mtlsPolicy:
      clientValidationCa:
      - certificateProviderInstance:
          pluginInstance: google_cloud_private_spiffe
    
  2. Crea un recurso de política de TLS de servidor llamado server_mtls_policy mediante la importación del archivo temporal server-mtls-policy.yaml:

    gcloud beta network-security server-tls-policies import server_mtls_policy \
      --source=server-mtls-policy.yaml --location=global
    
  3. Crea la política de extremos mediante el archivo temporal ep-mtls-psms.yaml:

    name: "ep_mtls_psms"
    type: "GRPC_SERVER"
    serverTlsPolicy: "server_mtls_policy"
    trafficPortSelector:
      ports:
      - "50051"
    endpointMatcher:
      metadataLabelMatcher:
        metadataLabelMatchCriteria: "MATCH_ANY"
        metadataLabels: []
    
  4. Para crear el recurso, importa el archivo ep-mtls-psms.yaml:

    gcloud beta network-services endpoint-policies import ep_mtls_psms \
      --source=ep-mtls-psms.yaml --location=global
    

Configura mTLS del lado del cliente

La política de seguridad del cliente está vinculada con el servicio de backend. Cuando un cliente accede a un backend (el servidor de gRPC) a través del servicio de backend, la política de seguridad adjunta del cliente se envía al cliente.

  1. Crea el contenido de los recursos de la política de TLS del cliente en un archivo temporal llamado client-mtls-policy.yaml en el directorio actual:

    name: "client_mtls_policy"
    clientCertificate:
      certificateProviderInstance:
        pluginInstance: google_cloud_private_spiffe
    serverValidationCa:
    - certificateProviderInstance:
        pluginInstance: google_cloud_private_spiffe
    
  2. Crea el recurso de política de TLS de cliente llamado client_mtls_policy mediante la importación del archivo temporal client-mtls-policy.yaml:

    gcloud beta network-security client-tls-policies import client_mtls_policy \
      --source=client-mtls-policy.yaml --location=global
    
  3. Crea un fragmento en un archivo temporal para hacer referencia a esta política y agrega detalles de subjectAltNames en el mensaje SecuritySettings, como en el siguiente ejemplo. Reemplaza ${PROJECT_ID} por el valor de ID del proyecto, que es el valor de la variable de entorno ${PROJECT_ID} que se describió antes. Ten en cuenta que example-grpc-server en subjectAltNames es el nombre de la cuenta de servicio de Kubernetes que se usa para el Pod del servidor de gRPC en la especificación de implementación.

    cat << EOF > client-security-settings.yaml
    securitySettings:
      clientTlsPolicy: projects/${PROJECT_ID}/locations/global/clientTlsPolicies/client_mtls_policy
      subjectAltNames:
        - "spiffe://${PROJECT_ID}.svc.id.goog/ns/default/sa/example-grpc-server"
    EOF
    
  4. Agrega el mensaje securitySettings al servicio de backend que ya creaste. Con estos pasos se exporta el contenido del servicio de backend actual, se agrega el mensaje de securitySetting del cliente y se vuelve a importar el contenido nuevo para actualizar el servicio de backend.

    gcloud beta compute backend-services export grpc-gke-helloworld-service --global \
      --destination=/tmp/grpc-gke-helloworld-service.yaml
    
    cat /tmp/grpc-gke-helloworld-service.yaml client-security-settings.yaml \
      >/tmp/grpc-gke-helloworld-service1.yaml
    
    gcloud beta compute backend-services import grpc-gke-helloworld-service --global \
      --source=/tmp/grpc-gke-helloworld-service1.yaml -q
    

Verifica la configuración

Se completó la configuración de Traffic Director, incluida la seguridad del servidor y del cliente. A continuación, prepara y ejecuta las cargas de trabajo del servidor y del cliente. Esto completa el ejemplo.

Crea un cliente de gRPC sin proxy

Este paso es similar al paso anterior Crea un servicio de gRPC sin proxy. Usa el cliente helloworld habilitado para xDS desde el directorio de ejemplo de xDS en el repositorio grpc-java. Compila y ejecuta el cliente en un contenedor compilado a partir de una imagen openjdk:8-jdk. La especificación de Kubernetes para el cliente de gRPC hace lo siguiente.

  • Crea una cuenta de servicio de Kubernetes example-grpc-client que usa el Pod de cliente de gRPC.
  • ${PROJNUM} representa el número de proyecto y se debe reemplazar por el número real.

Agrega la siguiente anotación a las especificaciones del Pod:

  annotations:
    security.cloud.google.com/use-workload-certificates: ""

Cuando se crea, cada Pod obtiene un volumen en /var/run/secrets/workload-spiffe-credentials. Este volumen contiene lo siguiente:

  • private_key.pem es una clave privada generada de forma automática.
  • certificates.pem es un conjunto de certificados con formato PEM que se puede presentar en otro Pod como la cadena de certificados de cliente o usarse como una cadena de certificados de servidor.
  • ca_certificates.pem es un conjunto de certificados con formato PEM que se usa como anclajes de confianza cuando se valida la cadena de certificados de cliente que presenta otro Pod o la cadena de certificados de servidor que se recibe cuando se conecta a otro Pod.

Ten en cuenta que ca_certificates.pem contiene certificados para el dominio de confianza local de las cargas de trabajo, que es el grupo de cargas de trabajo del clúster.

El certificado de hoja en certificates.pem contiene la siguiente aserción de identidad de SPIFFE de texto sin formato:

spiffe://WORKLOAD_POOL/ns/NAMESPACE/sa/KUBERNETES_SERVICE_ACCOUNT

En esta aserción, sucede lo siguiente:

  • WORKLOAD_POOL es el nombre del grupo de cargas de trabajo del clúster.
  • NAMESPACE es el nombre de tu cuenta de servicio de Kubernetes.
  • KUBERNETES_SERVICE_ACCOUNT es el espacio de nombres de tu cuenta de servicio de Kubernetes.

Esta es la especificación que debes usar en este ejemplo:

Java

cat << EOF > example-grpc-client.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: example-grpc-client
  namespace: default
  annotations:
    iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-grpc-client
  namespace: default
  labels:
    k8s-app: example-grpc-client
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: example-grpc-client
  strategy: {}
  template:
    metadata:
      annotations:
        security.cloud.google.com/use-workload-certificates: ""
      labels:
        k8s-app: example-grpc-client
    spec:
      containers:
      - image: openjdk:8-jdk
        imagePullPolicy: IfNotPresent
        name: example-grpc-client
        command:
        - /bin/sleep
        - inf
        env:
        - name: GRPC_XDS_BOOTSTRAP
          value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
        - name: GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT
          value: "true"
        resources:
          limits:
            cpu: 800m
            memory: 512Mi
          requests:
            cpu: 100m
            memory: 512Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/grpc-xds/
      initContainers:
      - name: grpc-td-init
        image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.12.0-rc1
        imagePullPolicy: Always
        args:
        - --output
        - "/tmp/bootstrap/td-grpc-bootstrap.json"
        - --include-psm-security-experimental
        resources:
          limits:
            cpu: 100m
            memory: 100Mi
          requests:
            cpu: 10m
            memory: 100Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/bootstrap/
      serviceAccountName: example-grpc-client
      volumes:
      - name: grpc-td-conf
        emptyDir:
          medium: Memory
EOF

C++

cat << EOF > example-grpc-client.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: example-grpc-client
  namespace: default
  annotations:
    iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-grpc-client
  namespace: default
  labels:
    k8s-app: example-grpc-client
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: example-grpc-client
  strategy: {}
  template:
    metadata:
      annotations:
        security.cloud.google.com/use-workload-certificates: ""
      labels:
        k8s-app: example-grpc-client
    spec:
      containers:
      - image: phusion/baseimage:18.04-1.0.0
        imagePullPolicy: IfNotPresent
        name: example-grpc-client
        command:
        - /bin/sleep
        - inf
        env:
        - name: GRPC_XDS_BOOTSTRAP
          value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
        - name: GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT
          value: "true"
        resources:
          limits:
            cpu: 8
            memory: 8Gi
          requests:
            cpu: 300m
            memory: 512Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/grpc-xds/
      initContainers:
      - name: grpc-td-init
        image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.12.0-rc1
        imagePullPolicy: Always
        args:
        - --output
        - "/tmp/bootstrap/td-grpc-bootstrap.json"
        - --include-psm-security-experimental
        resources:
          limits:
            cpu: 100m
            memory: 100Mi
          requests:
            cpu: 10m
            memory: 100Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/bootstrap/
      serviceAccountName: example-grpc-client
      volumes:
      - name: grpc-td-conf
        emptyDir:
          medium: Memory
EOF

Python

cat << EOF > example-grpc-client.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: example-grpc-client
  namespace: default
  annotations:
    iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-grpc-client
  namespace: default
  labels:
    k8s-app: example-grpc-client
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: example-grpc-client
  strategy: {}
  template:
    metadata:
      annotations:
        security.cloud.google.com/use-workload-certificates: ""
      labels:
        k8s-app: example-grpc-client
    spec:
      containers:
      - image: phusion/baseimage:18.04-1.0.0
        imagePullPolicy: IfNotPresent
        name: example-grpc-client
        command:
        - /bin/sleep
        - inf
        env:
        - name: GRPC_XDS_BOOTSTRAP
          value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
        - name: GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT
          value: "true"
        resources:
          limits:
            cpu: 8
            memory: 8Gi
          requests:
            cpu: 300m
            memory: 512Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/grpc-xds/
      initContainers:
      - name: grpc-td-init
        image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.12.0-rc1
        imagePullPolicy: Always
        args:
        - --output
        - "/tmp/bootstrap/td-grpc-bootstrap.json"
        - --include-psm-security-experimental
        resources:
          limits:
            cpu: 100m
            memory: 100Mi
          requests:
            cpu: 10m
            memory: 100Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/bootstrap/
      serviceAccountName: example-grpc-client
      volumes:
      - name: grpc-td-conf
        emptyDir:
          medium: Memory
EOF

Go

cat << EOF > example-grpc-client.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: example-grpc-client
  namespace: default
  annotations:
    iam.gke.io/gcp-service-account: ${PROJNUM}-compute@developer.gserviceaccount.com
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-grpc-client
  namespace: default
  labels:
    k8s-app: example-grpc-client
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: example-grpc-client
  strategy: {}
  template:
    metadata:
      annotations:
        security.cloud.google.com/use-workload-certificates: ""
      labels:
        k8s-app: example-grpc-client
    spec:
      containers:
      - image: golang:1.16-alpine
        imagePullPolicy: IfNotPresent
        name: example-grpc-client
        command:
        - /bin/sleep
        - inf
        env:
        - name: GRPC_XDS_BOOTSTRAP
          value: "/tmp/grpc-xds/td-grpc-bootstrap.json"
        - name: GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT
          value: "true"
        resources:
          limits:
            cpu: 8
            memory: 8Gi
          requests:
            cpu: 300m
            memory: 512Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/grpc-xds/
      initContainers:
      - name: grpc-td-init
        image: gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.12.0-rc1
        imagePullPolicy: Always
        args:
        - --output
        - "/tmp/bootstrap/td-grpc-bootstrap.json"
        - --include-psm-security-experimental
        resources:
          limits:
            cpu: 100m
            memory: 100Mi
          requests:
            cpu: 10m
            memory: 100Mi
        volumeMounts:
        - name: grpc-td-conf
          mountPath: /tmp/bootstrap/
      serviceAccountName: example-grpc-client
      volumes:
      - name: grpc-td-conf
        emptyDir:
          medium: Memory
EOF
  1. Aplica la especificación:

    kubectl apply -f example-grpc-client.yaml
    
  2. Otorga las funciones correctas a la cuenta de servicio:

    gcloud iam service-accounts add-iam-policy-binding \
      --role roles/iam.workloadIdentityUser \
      --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/example-grpc-client]" \
      ${PROJNUM}-compute@developer.gserviceaccount.com
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/example-grpc-client]" \
      --role roles/trafficdirector.client
    
  3. Verifica que el Pod se esté ejecutando.

    kubectl get pods
    

    El comando muestra información similar a la siguiente:

    NAMESPACE   NAME                                    READY   STATUS    RESTARTS   AGE
    default     example-grpc-client-7c969bb997-9fzjv    1/1     Running   0          104s
    [..skip..]
    

Ejecuta el servidor

Compila y ejecuta el servidor helloworld con xDS habilitado en el Pod del servidor que creaste antes.

Java

  1. Obtén el nombre del Pod creado para el servicio example-grpc-server:

    kubectl get pods | grep example-grpc-server
    

    Verás comentarios como los siguientes:

    default    example-grpc-server-77548868d-l9hmf     1/1    Running   0     105s
    
  2. Abre una shell al pod servidor:

    kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/bash
    
  3. En la shell, verifica que el archivo de arranque en /tmp/grpc-xds/td-grpc-bootstrap.json coincida con el esquema descrito en la sección Archivo de arranque.

  4. Descarga la versión 1.38.1 de gRPC para Java y compila la aplicación del servidor xds-hello-world.

    curl -L https://github.com/grpc/grpc-java/archive/v1.38.1.tar.gz | tar -xz
    
    cd grpc-java-1.38.1/examples/example-xds
    
    ../gradlew --no-daemon installDist
    
  5. Ejecuta el servidor con la marca --xds-creds para indicar la seguridad habilitada para xDS, con 50051 como el puerto de escucha y xds-server como el nombre de identificación del servidor:

    ./build/install/example-xds/bin/xds-hello-world-server --xds-creds 50051 xds-server
    
  6. Después de que el servidor obtenga la configuración necesaria de Traffic Director, verás el siguiente resultado:

    Listening on port 50051
    Plaintext health service listening on port 50052
    

C++

  1. Obtén el nombre del Pod creado para el servicio example-grpc-server:

    kubectl get pods | grep example-grpc-server
    

    Verás comentarios como los siguientes:

    default    example-grpc-server-77548868d-l9hmf     1/1    Running   0     105s
    
  2. Abre una shell al pod servidor:

    kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/bash
    
  3. En la shell, verifica que el archivo de arranque en /tmp/grpc-xds/td-grpc-bootstrap.json coincida con el esquema descrito en la sección Archivo de arranque.

  4. Descarga gRPC para C++ y compila la aplicación del servidor xds-hello-world.

    apt-get update -y && \
            apt-get install -y \
                build-essential \
                clang \
                python3 \
                python3-dev
    
    curl -L https://github.com/grpc/grpc/archive/master.tar.gz | tar -xz
    
    cd grpc-master
    
    tools/bazel build examples/cpp/helloworld:xds_greeter_server
    
  5. Ejecuta el servidor mediante 50051 como el puerto de escucha y xds_greeter_server como el nombre de identificación del servidor:

    bazel-bin/examples/cpp/helloworld/xds_greeter_server --port=50051 --maintenance_port=50052 --secure
    

    Para ejecutar el servidor sin credenciales, puedes especificar lo siguiente:

    bazel-bin/examples/cpp/helloworld/xds_greeter_server --nosecure
    
  6. Después de que el servidor obtenga la configuración necesaria de Traffic Director, verás el siguiente resultado:

    Listening on port 50051
    Plaintext health service listening on port 50052
    

Python

  1. Obtén el nombre del Pod creado para el servicio example-grpc-server:

    kubectl get pods | grep example-grpc-server
    

    Verás comentarios como los siguientes:

    default    example-grpc-server-77548868d-l9hmf     1/1    Running   0     105s
    
  2. Abre una shell al pod servidor:

    kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/bash
    
  3. En la shell, verifica que el archivo de arranque en /tmp/grpc-xds/td-grpc-bootstrap.json coincida con el esquema descrito en la sección Archivo de arranque.

  4. Descarga la versión 1.37.0 de gRPC para Python y compila la aplicación de ejemplo.

    apt-get update -y
    apt-get install -y python3 python3-pip
    python3 -m pip install virtualenv
    curl -L https://github.com/grpc/grpc/archive/v1.37.x.tar.gz | tar -xz
    cd grpc-1.37.x/examples/python/xds/
    python3 -m virtualenv venv
    source venv/bin/activate
    python3 -m pip install -r requirements.txt
    
  5. Ejecuta el servidor con la marca --xds-creds para indicar la seguridad habilitada para xDS, con 50051 como el puerto de escucha.

    python3 server.py 50051 --xds-creds
    
  6. Después de que el servidor obtenga la configuración necesaria de Traffic Director, verás el siguiente resultado:

    2021-05-06 16:10:34,042: INFO     Running with xDS Server credentials
    2021-05-06 16:10:34,043: INFO     Greeter server listening on port 50051
    2021-05-06 16:10:34,046: INFO     Maintenance server listening on port 50052
    

Go

  1. Obtén el nombre del Pod creado para el servicio example-grpc-server:

    kubectl get pods | grep example-grpc-server
    

    Verás comentarios como los siguientes:

    default    example-grpc-server-77548868d-l9hmf     1/1    Running   0     105s
    
  2. Abre una shell al pod servidor:

    kubectl exec -it example-grpc-server-77548868d-l9hmf -- /bin/sh
    
  3. En la shell, verifica que el archivo de arranque en /tmp/grpc-xds/td-grpc-bootstrap.json coincida con el esquema descrito en la sección Archivo de arranque.

  4. Descarga la versión 1.38.0 de gRPC para Go y navega al directorio que contiene la aplicación del servidor xds-hello-world.

    apk add curl
    
    curl -L https://github.com/grpc/grpc-go/archive/v1.38.0.tar.gz | tar -xz
    
    cd grpc-go-1.38.0/examples/features/xds/server
    
    
  5. Compila y ejecuta el servidor con la marca --xds_creds para indicar la seguridad habilitada para xDS, con 50051 como el puerto de escucha:

    GRPC_GO_LOG_VERBOSITY_LEVEL=2 GRPC_GO_LOG_SEVERITY="info" \
      go run main.go \
      -xds_creds \
      -port 50051
    
  6. Después de que el servidor obtenga la configuración necesaria de Traffic Director, verás el siguiente resultado:

    Using xDS credentials...
    Serving GreeterService on 0.0.0.0:50051 and HealthService on 0.0.0.0:50052
    

Ejecuta el cliente y verifica la configuración

Compila y ejecuta el cliente helloworld habilitado para xDS en el Pod de cliente que creaste antes.

Java

  1. Obtén el nombre del pod cliente:

    kubectl get pods | grep example-grpc-client
    

    Verás comentarios como los siguientes:

    default    example-grpc-client-7c969bb997-9fzjv     1/1    Running   0     105s
    
  2. Abre una shell al pod de cliente:

    kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
    
  3. Una vez que estés dentro de la shell, descarga la versión 1.38.1 de gRPC para Java y compila la aplicación cliente xds-hello-world.

    curl -L https://github.com/grpc/grpc-java/archive/v1.38.1.tar.gz | tar -xz
    
    cd grpc-java-1.38.1/examples/example-xds
    
    ../gradlew --no-daemon installDist
    
  4. Ejecuta el cliente con la marca --xds-creds para indicar la seguridad habilitada para xDS, el nombre del cliente y la string de conexión de destino:

    ./build/install/example-xds/bin/xds-hello-world-client --xds-creds xds-client \
          xds:///helloworld-gke:8000
    

    Deberías ver un resultado similar a este:

    Greeting: Hello xds-client, from xds-server
    

C++

  1. Obtén el nombre del pod cliente:

    kubectl get pods | grep example-grpc-client
    

    Verás comentarios como los siguientes:

    default    example-grpc-client-7c969bb997-9fzjv     1/1    Running   0     105s
    
  2. Abre una shell al pod de cliente:

    kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
    
  3. Una vez que estés dentro de la shell, descarga gRPC para C++ y compila la aplicación cliente xds-hello-world.

    apt-get update -y && \
            apt-get install -y \
                build-essential \
                clang \
                python3 \
                python3-dev
    
    curl -L https://github.com/grpc/grpc/archive/master.tar.gz | tar -xz
    
    cd grpc-master
    
    tools/bazel build examples/cpp/helloworld:xds_greeter_client
    
  4. Ejecuta el cliente con la marca --xds-creds para indicar la seguridad habilitada para xDS, el nombre del cliente y la string de conexión de destino:

    bazel-bin/examples/cpp/helloworld/xds_greeter_client --target=xds:///helloworld-gke:8000
    

    Para ejecutar el cliente sin credenciales, usa lo siguiente:

    bazel-bin/examples/cpp/helloworld/xds_greeter_client --target=xds:///helloworld-gke:8000 --nosecure
    

    Deberías ver un resultado similar a este:

    Greeter received: Hello world
    

Python

  1. Obtén el nombre del pod cliente:

    kubectl get pods | grep example-grpc-client
    

    Verás comentarios como los siguientes:

    default    example-grpc-client-7c969bb997-9fzjv     1/1    Running   0     105s
    
  2. Abre una shell al pod de cliente:

    kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/bash
    
  3. Una vez que estés dentro de la shell, descarga la versión 1.37.0 de gRPC para Python y compila la aplicación cliente de ejemplo.

    apt-get update -y
    apt-get install -y python3 python3-pip
    python3 -m pip install virtualenv
    curl -L https://github.com/grpc/grpc/archive/v1.37.x.tar.gz | tar -xz
    cd grpc-1.37.x/examples/python/xds/
    python3 -m virtualenv venv
    source venv/bin/activate
    python3 -m pip install -r requirements.txt
    
  4. Ejecuta el cliente con la marca --xds-creds para indicar la seguridad habilitada para xDS, el nombre del cliente y la string de conexión de destino:

    python3 client.py xds:///helloworld-gke:8000 --xds-creds
    

    Deberías ver un resultado similar a este:

    Greeter client received: Hello you from example-host!
    

Go

  1. Obtén el nombre del pod cliente:

    kubectl get pods | grep example-grpc-client
    

    Verás comentarios como los siguientes:

    default    example-grpc-client-7c969bb997-9fzjv     1/1    Running   0     105s
    
  2. Abre una shell al pod de cliente:

    kubectl exec -it example-grpc-client-7c969bb997-9fzjv -- /bin/sh
    
  3. Una vez que estés dentro de la shell, descarga la versión 1.38.0 de gRPC para Go y navega al directorio que contiene la aplicación cliente xds-hello-world.

    apk add curl
    
    curl -L https://github.com/grpc/grpc-go/archive/v1.38.0.tar.gz | tar -xz
    
    cd grpc-go-1.38.0/examples/features/xds/client
    
  4. Compila y ejecuta el cliente con la marca --xds_creds para indicar la seguridad habilitada para xDS, el nombre del cliente y la string de conexión de destino:

    GRPC_GO_LOG_VERBOSITY_LEVEL=2 GRPC_GO_LOG_SEVERITY="info" \
      go run main.go \
      -xds_creds \
      -name xds-client \
      -target xds:///helloworld-gke:8000
    

    Deberías ver un resultado similar a este:

    Greeting: Hello xds-client, from xds-server
    

Usa TLS en lugar de mTLS

En este ejemplo, el uso de TLS solo requiere un pequeño cambio.

  1. En ServerTlsPolicy, descarta el mtlsPolicy:

    cat << EOF > server-tls-policy.yaml
    name: "server_tls_policy"
    serverCertificate:
      certificateProviderInstance:
        pluginInstance: google_cloud_private_spiffe
    EOF
    
  2. En su lugar, usa esta política en EndpointPolicy:

    cat << EOF > ep-tls-psms.yaml
    name: "ep_mtls_psms"
    type: "GRPC_SERVER"
    serverTlsPolicy: "server_tls_policy"
    trafficPortSelector:
      ports:
      - "50051"
    endpointMatcher:
      metadataLabelMatcher:
        metadataLabelMatchCriteria: "MATCH_ALL"
        metadataLabels: []
    EOF
    
  3. ClientTlsPolicy para mTLS también funciona en el caso de TLS, pero la sección clientCertificate de la política se puede descartar, ya que no es necesaria para TLS:

    cat << EOF > client-tls-policy.yaml
    name: "client_tls_policy"
    serverValidationCa:
    - certificateProviderInstance:
        pluginInstance: google_cloud_private_spiffe
    EOF
    

Archivo de arranque

En el proceso de configuración de esta guía, se usa un generador de arranque para generar el archivo de arranque requerido. En esta sección, se proporciona información de referencia sobre el archivo de arranque.

El archivo de arranque contiene información de configuración que requiere el código de gRPC sin proxy, incluida la información de conexión para el servidor xDS. El archivo de arranque contiene la configuración de seguridad que requiere la función de seguridad de gRPC sin proxy. El servidor de gRPC requiere un campo adicional, como se describe a continuación. Un archivo de arranque de muestra se ve de la siguiente manera:

{
  "xds_servers": [
    {
      "server_uri": "trafficdirector.googleapis.com:443",
      "channel_creds": [
        {
          "type": "google_default"
        }
      ],
      "server_features": [
        "xds_v3"
      ]
    }
  ],
  "node": {
    "cluster": "cluster",
    "id": "projects/9876012345/networks/default/nodes/client1",
    "metadata": {
      "TRAFFICDIRECTOR_GCP_PROJECT_NUMBER": "9876012345",
      "TRAFFICDIRECTOR_NETWORK_NAME": "default",
      "INSTANCE_IP": "10.0.0.3"
    },
    "locality": {
      "zone": "us-central1-a"
    }
  },
  "server_listener_resource_name_template": "grpc/server?xds.resource.listening_address=%s",
  "certificate_providers": {
    "google_cloud_private_spiffe": {
      "plugin_name": "file_watcher",
      "config": {
        "certificate_file": "/var/run/secrets/workload-spiffe-credentials/certificates.pem",
        "private_key_file": "/var/run/secrets/workload-spiffe-credentials/private_key.pem",
        "ca_certificate_file": "/var/run/secrets/workload-spiffe-credentials/ca_certificates.pem",
        "refresh_interval": "600s"
      }
    }
  }
}

Actualizaciones del archivo de arranque para el servicio de seguridad

Los siguientes campos reflejan las modificaciones relacionadas con la seguridad y el uso de xDS v3:

El campo id dentro de node proporciona una identidad única al cliente de gRPC para Traffic Director. Debes proporcionar el número del proyecto de Google Cloud y el nombre de red con el ID del nodo en este formato:

projects/{project number}/networks/{network name}/nodes/[UNIQUE_ID]

El siguiente es un ejemplo para el proyecto 1234 y la red predeterminada:

projects/1234/networks/default/nodes/client1

El campo INSTANCE_IP es la dirección IP del pod o 0.0.0.0 para indicar INADDR_ANY. El servidor de gRPC usa este campo a fin de recuperar el recurso de objeto de escucha desde Traffic Director para la seguridad del servidor.

Campos de configuración de seguridad en el archivo de arranque

Clave JSON Tipo Valor Notas
server_listener_resource_name_template String grpc/server?xds.resource.listening_address=%s Obligatorio para los servidores gRPC. gRPC usa este valor a fin de redactar el nombre del recurso para recuperar el recurso "Listener" de Traffic Director, que lo usará para la seguridad del servidor y otras opciones de configuración. gRPC usa esto para formar la string del nombre del recurso.
certificate_providers Struct de JSON google_cloud_private_spiffe Obligatorio. El valor es un struct de JSON que representa una asignación de nombres para instancias de proveedor de certificados. Se usa una instancia de proveedor de certificados para recuperar identidades y certificados raíz. El archivo de arranque de ejemplo contiene un nombre, google_cloud_private_spiffe, con el struct de JSON de la instancia del proveedor del certificado como valor. Cada struct de JSON de instancia del proveedor de certificados tiene dos campos:
  • plugin_name: valor obligatorio que identifica el complemento del proveedor de certificados que se usará según lo requiera la arquitectura de complementos de gRPC para proveedores de certificados. gRPC tiene compatibilidad integrada con el complemento de observación de archivos que se usa en esta configuración. El plugin_name es file_watcher.
  • config: valor obligatorio que identifica el blog de configuración JSON para el complemento file_watcher. El esquema y el contenido dependen del complemento.

El contenido de la estructura JSON config para el complemento file_watcher es el siguiente:

  • certificate_file: string obligatoria. Este valor es la ubicación del certificado de identidad.
  • private_key_file: string obligatoria. El valor es la ubicación del archivo de clave privada, que debe coincidir con el certificado de identidad.
  • ca_certificate_file: string obligatoria. El valor es la ubicación del certificado raíz, que también se conoce como el paquete de confianza.
  • refresh_interval: string opcional. El valor indica el intervalo de actualización, especificado con la representación de string de una asignación JSON de Duration. El valor predeterminado es “600s”, una duración de 10 minutos.

Generador de arranque

La imagen del contenedor del generador de arranque está disponible en gcr.io/trafficdirector-prod/td-grpc-bootstrap:0.12.0-rc1. Su código fuente está disponible en https://github.com/GoogleCloudPlatform/traffic-director-grpc-bootstrap. Las opciones de línea de comandos más usadas son las siguientes:

  • --output: Usa esta opción para especificar dónde se escribe el archivo de arranque de salida, por ejemplo, el comando de arranque --output /tmp/bootstrap/td-grpc-bootstrap.json genera el archivo de arranque en /tmp/bootstrap/td-grpc-bootstrap.json en el archivo de sistema del pod.
  • --include-psm-security-experimental: Usa esta marca obligatoria para indicarle al generador de arranque que genere la configuración de gRPC sin proxy relacionada con la seguridad en el archivo de arranque.
  • --node-metadata-experimental: Usa esta marca para propagar los metadatos del nodo en el archivo de arranque. Se requiere cuando usas comparadores de etiquetas de metadatos en EndpointPolicy, en los que {td_name_short}} usa los datos de etiquetas proporcionados en la sección de metadatos de nodo del archivo de arranque. El argumento se proporciona en el formato clave=valor, por ejemplo: --node-metadata-experimental version=prod --node-metadata-experimental type=grpc.

En el ejemplo anterior, se agrega lo siguiente en la sección de metadatos de nodo del archivo de arranque:

{
  "node": {
...
    "metadata": {
      "version": "prod",
      "type": "grpc",
...
    },
...
  },
...
}

Soluciona problemas

Usa estas instrucciones para resolver problemas con la implementación de seguridad.

Las cargas de trabajo no pueden obtener una configuración de Traffic Director

Podría aparecer un error similar a este:

PERMISSION_DENIED: Request had insufficient authentication scopes.

Asegúrate de que se den las siguientes condiciones:

  • Creaste tu clúster de GKE con el argumento --scopes=cloud-platform.
  • Asignaste el roles/trafficdirector.client a tus cuentas de servicio de Kubernetes.
  • Asignaste roles/trafficdirector.client a tu cuenta de servicio de GCP predeterminada (la cuenta ${GSA_EMAIL} anterior).
  • Habilitaste el servicio trafficdirector.googleapis.com (API).

Tu servidor de gRPC no usa TLS/mTLS incluso con la configuración correcta de Traffic Director

Asegúrate de especificar GRPC_SERVER en la configuración de las políticas de extremos. Si especificaste SIDECAR_PROXY, gRPC ignora la configuración.

No puedes crear el clúster de GKE con la versión de clúster solicitada

El comando de creación del clúster de GKE podría fallar con un error similar al siguiente:

Node version "1.20.5-gke.2000" is unsupported.

Asegúrate de usar el argumento --release-channel rapid en el comando de creación de clústeres. Debes usar el canal de versiones rápido para obtener la versión correcta de esta versión preliminar pública.

Si aparece el error No usable endpoint

Si un cliente no puede comunicarse con el servidor debido a un error No usable endpoint, es posible que el verificador de estado haya marcado los backends del servidor como en mal estado. Para verificar el estado de los backends, ejecuta este comando gcloud:

gcloud compute backend-services get-health grpc-gke-helloworld-service --global

Si el comando muestra que el backend está en mal estado, puede deberse a uno de estos motivos:

  • El firewall no se creó o no contiene el rango de IP de origen correcto.
  • Las etiquetas de destino de tu firewall no coinciden con las etiquetas del clúster que creaste.

Las cargas de trabajo no pueden comunicarse en la configuración de seguridad

Si las cargas de trabajo no pueden comunicarse después de configurar la seguridad de la malla de servicios sin proxy, sigue estas instrucciones para determinar la causa.

  1. Inhabilita la seguridad sin proxy y elimina los problemas en los casos de uso de balanceo de cargas de la malla de servicios sin proxy. Para inhabilitar la seguridad en la malla, realiza una de las siguientes acciones:
    1. Usa credenciales de texto simple en el cliente y el servidor O
    2. No configures la seguridad para el servicio de backend ni la política de extremos en la configuración de Traffic Director.

Sigue los pasos que se indican en Solución de problemas de implementaciones sin proxy de Traffic Director, ya que no hay una configuración de seguridad en la implementación.

  1. Modifica las cargas de trabajo para usar credenciales de xDS con texto simple o credenciales no seguras como credenciales de resguardo. Mantén la configuración de Traffic Director con la seguridad inhabilitada, como se mencionó anteriormente. En este caso, aunque gRPC permite que Traffic Director configure la seguridad, Traffic Director no envía información de seguridad, en cuyo caso gRPC debe recurrir a credenciales de texto simple (o no seguras) que deberían funcionar de manera similar al primer caso anterior. Si este caso no funciona, haz lo siguiente:

    1. Aumenta el nivel de registro en el cliente y el servidor para que puedas ver los mensajes xDS intercambiados entre gRPC y Traffic Director.
    2. Asegúrate de que Traffic Director no tenga seguridad habilitada en las respuestas de CDS y LDS que se envían a las cargas de trabajo.
    3. Asegúrate de que las cargas de trabajo no usen los modos TLS o mTLS en sus canales. Si ves mensajes de registro relacionados con los protocolos de enlace TLS, verifica el código fuente de tu aplicación y asegúrate de usar texto no seguro o simple como credenciales de resguardo. Si el código fuente de la aplicación es correcto, es posible que sea un error en la biblioteca de gRPC.
  2. Verifica que la integración del servicio de CA con GKE funcione de forma correcta para tu clúster de GKE mediante los pasos para solucionar problemas de esa guía del usuario. Asegúrate de que los certificados y claves que proporciona esa característica estén disponibles en el directorio especificado, /var/run/secrets/workload-spiffe-credentials/.

  3. Habilita TLS (en lugar de mTLS) en la malla, como se describió antes, y reinicia las cargas de trabajo del cliente y del servidor.

    1. Aumenta el nivel de registro en el cliente y el servidor para poder ver los mensajes xDS intercambiados entre gRPC y Traffic Director.
    2. Asegúrate de que Traffic Director tenga habilitada la seguridad en las respuestas de CDS y LDS que se envían a las cargas de trabajo.

El cliente falla con un CertificateException y un mensaje Peer certificate SAN check failed

Esto indica un problema con los valores de subjectAltNames en el mensaje SecuritySettings. Ten en cuenta que estos valores se basan en los servicios de Kubernetes que creaste para tu servicio de backend. Para cada servicio de Kubernetes que creaste, hay un ID de SPIFFE asociado, en este formato:

spiffe://${WORKLOAD_POOL}/ns/${K8S_NAMESPACE}/sa/${SERVICE_ACCOUNT}

Estos valores son los siguientes:

  • WORKLOAD_POOL: el grupo de cargas de trabajo del clúster, que es ${PROJECT_ID}.svc.id.goog
  • K8S_NAMESPACE: el espacio de nombres de Kubernetes que usaste en la implementación del servicio
  • SERVICE_ACCOUNT: la cuenta de servicio de Kubernetes que usaste en la implementación del servicio

Para cada servicio de Kubernetes que adjuntaste al servicio de backend como un grupo de extremos de red, asegúrate de haber calculado de forma correcta el ID de SPIFFE y haber agregado ese ID de SPIFFE al campo subjectAltNames en el mensaje SecuritySettings.

Las aplicaciones no pueden usar los certificados mTLS con tu biblioteca de gRPC

Si tus aplicaciones no pueden usar los certificados mTLS con tu biblioteca de gRPC, haz lo siguiente:

  1. Verifica que la especificación del pod contenga la anotación security.cloud.google.com/use-workload-certificates que se describe en Crea un servicio de gRPC sin proxy con NEG.

  2. Verifica que se pueda acceder a los archivos que contienen la cadena de certificados junto con el certificado de hoja, la clave privada y los certificados de CA de confianza en las siguientes rutas desde el pod:

    1. Cadena de certificados junto con certificado de hoja: "/var/run/secrets/workload-spiffe-credentials/certificates.pem"
    2. Clave privada: “/var/run/secrets/workload-spiffe-credentials/private_key.pem”
    3. Paquete de CA: "/var/run/secrets/workload-spiffe-credentials/ca_certificates.pem"
  3. Si los certificados del paso anterior no están disponibles, haz lo siguiente:

      # The following command should return the status of the CA Service instance
      # returned from previous command.
      # Change 'subordinates' to 'roots' if the issuing CA is a root CA.
      gcloud beta privateca subordinates describe ${SUBORDINATE_CA_NAME} 
    --location=LOCATION

    1. Verifica que el plano de control de GKE tenga la vinculación de función de IAM correcta y otórgale acceso al servicio de CA:

      # Get the IAM policy for the CA
      gcloud beta privateca roots get-iam-policy {GKE CA Name}
      
      # Verify that there is an IAM binding granting access in the following format
      - members:
      - serviceAccount:service-projnumber@container-engine-robot.iam.gserviceaccount.com
      role: roles/privateca.certificateManager
      
      # Where projnumber is the project number (e.g. 2915810291) for the GKE cluster.
      
    2. Verifica que el certificado no haya vencido. Esta es la cadena de certificados y el certificado de hoja en /var/run/secrets/workload-spiffe-credentials/certificates.pem. Como comprobación, ejecuta este comando:

      cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Not After"
      

    3. Ejecuta el siguiente comando para verificar que el tipo de clave sea compatible con tu aplicación:

      cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Public Key Algorithm" -A 3
      

    4. Verifica que tu aplicación de Java para gRPC tenga el siguiente keyAlgorithm en el archivo YAML WorkloadCertificateConfig:

      keyAlgorithm:
        rsa:
          modulusSize: 4096
    
  4. Verifica que la CA use la misma familia de claves que la clave del certificado.

El cliente, el servidor o el par rechazaron el certificado de una aplicación

  1. Verifica que la aplicación par use el mismo paquete de confianza para verificar el certificado.
  2. Verifica que el certificado en uso no esté vencido (cadena de certificados junto con certificado de hoja: “/var/run/secrets/workload-spiffe-credentials/certificates.pem”).

No se pudo crear el clúster con la marca --enable-workload-certificates

Asegúrate de estar ejecutando la última versión de gcloud:

gcloud components update

Los pods no se inician

Es posible que los pods que usan certificados de carga de trabajo de GKE no se inicien si falla el aprovisionamiento de certificados. Esto puede suceder en situaciones como las siguientes:

  • WorkloadCertificateConfig o TrustConfig no están bien configurados o no se encuentran.
  • No se aprueban las CSR.

Para verificar si el aprovisionamiento de certificados falla, revisa los eventos del pod.

  1. Verifica el estado del Pod:

    kubectl get pod -n POD_NAMESPACE POD_NAME
    

    Reemplaza lo siguiente:

    • POD_NAMESPACE: es el espacio de nombres del Pod.
    • POD_NAME: es el nombre del Pod.
  2. Revisa los eventos recientes de tu pod:

    kubectl describe pod -n POD_NAMESPACE POD_NAME
    
  3. Si falla el aprovisionamiento de certificados, verás un evento con Type=Warning, Reason=FailedMount, From=kubelet y un campo Message que comienza con MountVolume.SetUp failed for volume "gke-workload-certificates". El campo Message contiene información para solucionar problemas.

    Events:
      Type     Reason       Age                From       Message
      ----     ------       ----               ----       -------
      Warning  FailedMount  13s (x7 over 46s)  kubelet    MountVolume.SetUp failed for volume "gke-workload-certificates" : rpc error: code = Internal desc = unable to mount volume: store.CreateVolume, err: unable to create volume "csi-4d540ed59ef937fbb41a9bf5380a5a534edb3eedf037fe64be36bab0abf45c9c": caPEM is nil (check active WorkloadCertificateConfig)
    
  4. Consulta los siguientes pasos de solución de problemas si el motivo por el que tus pods no se inician es porque hay objetos mal configurados o CSR rechazadas.

La configuración de WorkloadCertificateConfig o TrustConfig es incorrecta

Asegúrate de haber creado los objetos WorkloadCertificateConfig y TrustConfig de forma correcta. Puedes diagnosticar una configuración incorrecta en cualquiera de estos objetos mediante kubectl.

  1. Recupera el estado actual.

    Para WorkloadCertificateConfig:

    kubectl get WorkloadCertificateConfig default -o yaml
    

    Para TrustConfig:

    kubectl get TrustConfig default -o yaml
    
  2. Inspecciona el resultado del estado. Un objeto válido tendrá una condición con type: Ready y status: "True".

    status:
      conditions:
      - lastTransitionTime: "2021-03-04T22:24:11Z"
        message: WorkloadCertificateConfig is ready
        observedGeneration: 1
        reason: ConfigReady
        status: "True"
        type: Ready
    

    En el caso de los objetos no válidos, aparece status: "False" en su lugar. Los campos reason y message contienen detalles adicionales sobre la solución de problemas.

Las CSR no están aprobadas.

Si algo sale mal durante el proceso de aprobación de la CSR, puedes verificar los detalles del error en las condiciones de type: Approved y type: Issued de la CSR.

  1. Enumera las CSR relevantes mediante kubectl:

    kubectl get csr \
      --field-selector='spec.signerName=spiffe.gke.io/spiffe-leaf-signer'
    
  2. Elige una CSR que sea Approved y no Issued, o Approved.

  3. Obtén detalles de la CSR seleccionada con kubectl:

    kubectl get csr CSR_NAME -o yaml
    

    Reemplaza CSR_NAME por el nombre de la CSR que elegiste.

Una CSR válida tiene una condición con type: Approved y status: "True", y un certificado válido en el campo status.certificate:

status:
  certificate: <base64-encoded data>
  conditions:
  - lastTransitionTime: "2021-03-04T21:58:46Z"
    lastUpdateTime: "2021-03-04T21:58:46Z"
    message: Approved CSR because it is a valid SPIFFE SVID for the correct identity.
    reason: AutoApproved
    status: "True"
    type: Approved

La información de solución de problemas para CSR no válidas aparece en los campos message y reason.

Faltan certificados en los pods

  1. Obtén las especificaciones de pod para tu pod:

    kubectl get pod -n POD_NAMESPACE POD_NAME -o yaml
    

    Reemplaza lo siguiente:

    • POD_NAMESPACE: es el espacio de nombres del Pod.
    • POD_NAME: es el nombre del Pod.
  2. Verifica que las especificaciones del pod contengan la anotación security.cloud.google.com/use-workload-certificates que se describe en Configura pods para recibir credenciales de mTLS.

  3. Verifica que el controlador de admisión de certificados de carga de trabajo de GKE inserte de forma correcta un volumen de controlador CSI de tipo workloadcertificates.security.cloud.google.com en tu especificación de pods:

    volumes:
    ...
    -csi:
      driver: workloadcertificates.security.cloud.google.com
      name: gke-workload-certificates
    ...
    
  4. Comprueba la presencia de una activación de volumen en cada uno de los contenedores:

    containers:
    - name: ...
      ...
      volumeMounts:
      - mountPath: /var/run/secrets/workload-spiffe-credentials
        name: gke-workload-certificates
        readOnly: true
      ...
    
  5. Verifica que los siguientes paquetes de certificados y la clave privada estén disponibles en las siguientes ubicaciones del Pod:

    • Paquete de cadena de certificados: /var/run/secrets/workload-spiffe-credentials/certificates.pem
    • Clave privada: /var/run/secrets/workload-spiffe-credentials/private_key.pem
    • Paquete de ancla de confianza de CA: /var/run/secrets/workload-spiffe-credentials/ca_certificates.pem
  6. Si los archivos no están disponibles, realiza los siguientes pasos:

    1. Recupera la instancia del servicio de CA (vista previa) del clúster:

      kubectl get workloadcertificateconfigs default -o jsonpath '{.spec.certificateAuthorityConfig.certificateAuthorityServiceConfig.endpointURI}'
      
    2. Recupera el estado de la instancia del servicio de CA (vista previa):

      gcloud beta privateca ISSUING_CA_TYPE describe ISSUING_CA_NAME \
        --location ISSUING_CA_LOCATION
      

      Reemplaza lo siguiente:

      • ISSUING_CA_TYPE: el tipo de CA de la emisión, que debe ser subordinates o roots.
      • ISSUING_CA_NAME: el nombre de la CA emisora.
      • ISSUING_CA_LOCATION: la región de la CA emisora.
    3. Obtén la política de IAM para la CA raíz:

      gcloud beta privateca roots get-iam-policy ROOT_CA_NAME
      

      Reemplaza ROOT_CA_NAME por el nombre de tu CA raíz.

    4. En la política de IAM, verifica que exista la vinculación de la política privateca.auditor:

      ...
      - members:
        - serviceAccount:service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com
        role: roles/privateca.auditor
      ...
      

      En este ejemplo, PROJECT_NUMBER es el número del proyecto de tu clúster.

    5. Obtén la política de IAM para la CA subordinada:

      gcloud beta privateca subordinates get-iam-policy SUBORDINATE_CA_NAME
      

      Reemplaza SUBORDINATE_CA_NAME por el nombre de la CA subordinada.

    6. En la política de IAM, verifica que exista la vinculación de la política privateca.certificateManager:

      ...
      - members:
        - serviceAccount: service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com
        role: roles/privateca.certificateManager
      ...
      

      En este ejemplo, PROJECT_NUMBER es el número del proyecto de tu clúster.

Las aplicaciones no pueden usar las credenciales de mTLS emitidas

  1. Verifica que el certificado no haya vencido:

    cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Not After"
    
  2. Comprueba que tu aplicación admita el tipo de clave que usaste.

    cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Public Key Algorithm" -A 3
    
  3. Comprueba que la CA emisora use la misma familia de claves que la clave del certificado.

    1. Obtén el estado de la instancia del servicio de CA (vista previa):

      gcloud beta privateca ISSUING_CA_TYPE describe ISSUING_CA_NAME \
        --location ISSUING_CA_LOCATION
      

      Reemplaza lo siguiente:

      • ISSUING_CA_TYPE: el tipo de CA de la emisión, que debe ser subordinates o roots.
      • ISSUING_CA_NAME: el nombre de la CA emisora.
      • ISSUING_CA_LOCATION: la región de la CA emisora.
    2. Comprueba que el keySpec.algorithm en el resultado sea el mismo algoritmo de clave que definiste en el manifiesto de YAML WorkloadCertificateConfig. El resultado se verá así:

      config:
        ...
        subjectConfig:
          commonName: td-sub-ca
          subject:
            organization: TestOrgLLC
          subjectAltName: {}
      createTime: '2021-05-04T05:37:58.329293525Z'
      issuingOptions:
        includeCaCertUrl: true
      keySpec:
        algorithm: RSA_PKCS1_2048_SHA256
       ...
      

Los certificados se rechazan

  1. Verifica que la aplicación par use el mismo paquete de confianza para verificar el certificado.
  2. Verifica que el certificado no haya vencido:

    cat /var/run/secrets/workload-spiffe-credentials/certificates.pem | openssl x509 -text -noout | grep "Not After"
    
  3. Verifica que el código de cliente, si no usas la API de recarga de credenciales de gRPC para Go, actualice las credenciales del sistema de archivos de forma periódica.

  4. Verifica que las cargas de trabajo estén en el mismo dominio de confianza que la CA. Los certificados de carga de trabajo de GKE admiten la comunicación entre cargas de trabajo en un solo dominio de confianza.

Limitaciones

Traffic Director no es compatible con situaciones en las que hay dos o más recursos de políticas de extremos que coinciden de manera equitativa con un extremo, por ejemplo, dos políticas con las mismas etiquetas y puertos, o dos o más políticas con diferentes etiquetas que coinciden de manera equitativa con las etiquetas de un extremo. Si deseas obtener más información sobre cómo las políticas de extremos se comparan con las etiquetas de un extremo, consulta cómo vincular a API para EndpointPolicy.EndpointMatcher.MetadataLabelMatcher. En tales casos, Traffic Director no genera una configuración de seguridad a partir de ninguna de las políticas en conflicto.