Usa varios certificados SSL en el balanceo de cargas de HTTP(S) con Ingress


En esta página, se muestra cómo configurar varios certificados SSL para recursos de Ingress en clústeres de Google Kubernetes Engine (GKE).

Descripción general

Si deseas aceptar solicitudes HTTPS de los clientes, el balanceador de cargas de aplicaciones debe contar con un certificado para que pueda demostrar su identidad a los clientes. También debe tener una clave privada para completar el protocolo de enlace HTTPS.

Cuando el balanceador de cargas acepta una solicitud HTTP(S) de un cliente, el tráfico entre el cliente y el balanceador de cargas se encripta mediante TLS. Sin embargo, el balanceador de cargas finaliza el cifrado TLS y reenvía la solicitud sin encriptación a la aplicación. Cuando configuras un balanceador de cargas de aplicaciones a través de Ingress, puedes configurarlo para que presente hasta diez certificados TLS al cliente.

El balanceador de cargas usa la indicación de nombre del servidor (SNI) para determinar qué certificado presentar al cliente, según el nombre de dominio en el protocolo de enlace TLS. Si el cliente no usa SNI o usa un nombre de dominio que no coincide con el nombre común (CN) en uno de los certificados, el balanceador de cargas usa el primer certificado que se encuentra en el Ingress.

En el siguiente diagrama, se muestra un balanceador de cargas que envía tráfico a backends diferentes, según el nombre de dominio que se usa en la solicitud:

Varios certificados SSL con el diagrama del sistema Ingress

Puedes proporcionar un balanceador de cargas de aplicaciones con certificados SSL con los siguientes métodos:

  • Certificado SSL de Google Cloud que administras. El certificado SSL usa un certificado ya compartido que subes al proyecto de Google Cloud.

  • Secretos de Kubernetes. El secreto contiene un certificado y una clave que tú mismo creas. Debes agregar el nombre del Secret al campo tls del manifiesto de Ingress.

Puedes usar más de un método en el mismo Ingress. Esto permite migraciones entre métodos sin tiempo de inactividad.

Panorama general

Esta es una descripción general de los pasos en este documento:

  1. Crea una implementación.

  2. Crea un servicio.

  3. Crea dos archivos de certificado y dos archivos de claves o dos objetos ManagedCertificate. Debes configurar estos certificados en el mismo proyecto y en el mismo espacio de nombres en el que se implementa el balanceador de cargas.

  4. Crea un Ingress que use Secretos o certificados ya compartidos. Cuando creas el Ingress, GKE crea y configura un balanceador de cargas de aplicaciones.

  5. Prueba el balanceador de cargas de aplicaciones.

Antes de comenzar

Antes de comenzar, asegúrate de haber realizado las siguientes tareas:

  • Habilita la API de Google Kubernetes Engine.
  • Habilitar la API de Google Kubernetes Engine
  • Si deseas usar Google Cloud CLI para esta tarea, instala y, luego, inicializa gcloud CLI. Si ya instalaste gcloud CLI, ejecuta gcloud components update para obtener la versión más reciente.
  • Debes tener dos nombres de dominio. Los nombres de dominio no deben tener más de 63 caracteres.

Limitaciones

  • Los certificados administrados por Google solo son compatibles con Ingress de GKE con el uso del balanceador de cargas de aplicaciones externo. Los certificados administrados por Google no son compatibles con controladores de Ingress de terceros.
  • Para los balanceadores de cargas de aplicaciones internos, debes inhabilitar HTTP en el manifiesto de Ingress. Esto no es necesario con el balanceador de cargas externo.
  • No debes cambiar ni actualizar manualmente la configuración del balanceador de cargas de la aplicación. Esto significa que no debes editar ninguno de los componentes del balanceador de cargas, incluidos los proxies de destino, los mapas de URL y los servicios de backend. GKE reemplazará todos los cambios que realices.

Crear una implementación

  1. Guarda el siguiente manifiesto como my-mc-deployment.yaml:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-mc-deployment
    spec:
      selector:
        matchLabels:
          app: products
          department: sales
      replicas: 3
      template:
        metadata:
          labels:
            app: products
            department: sales
        spec:
          containers:
          - name: hello
            image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0"
            env:
            - name: "PORT"
              value: "50001"
          - name: hello-again
            image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:2.0"
            env:
            - name: "PORT"
              value: "50002"
    

    En este manifiesto, se describe un Deployment con tres Pods. Cada Pod tiene dos contenedores. Un contenedor ejecuta hello-app:1.0 y escucha en el puerto TCP 50001. El otro contenedor ejecuta hello-app:2.0 y escucha en el puerto TCP 50002.

  2. Aplica el manifiesto al clúster:

    kubectl apply -f my-mc-deployment.yaml
    

Crea un Service

  1. Guarda el siguiente manifiesto como my-mc-service.yaml:

    apiVersion: v1
    kind: Service
    metadata:
      name: my-mc-service
    spec:
      type: NodePort
      selector:
        app: products
        department: sales
      ports:
      - name: my-first-port
        protocol: TCP
        port: 60001
        targetPort: 50001
      - name: my-second-port
        protocol: TCP
        port: 60002
        targetPort: 50002
    

    En este manifiesto, se describe un Service con las siguientes propiedades:

    • selector: Especifica que cualquier Pod que tenga las etiquetas app: products y department: sales es miembro de este servicio.
    • ports: Especifica que, cuando un cliente envía una solicitud al servicio en my-first-port, GKE reenvía la solicitud a uno de los Pods miembro en el puerto 50001. Cuando un cliente envía una solicitud al servicio en my-second-port, GKE reenvía la solicitud a uno de los pods miembro en el puerto 50002.
  2. Aplica el manifiesto al clúster:

    kubectl apply -f my-mc-service.yaml
    

Crea certificados y claves

Para hacer los ejercicios en esta página, necesitas dos certificados, cada uno con una clave correspondiente. Cada certificado debe tener un nombre común (CN, por sus siglas en inglés) que sea igual a un nombre de dominio de tu propiedad.

Puedes crear esos certificados de forma manual o usar certificados administrados por Google.

Si ya tienes dos archivos de certificado con los valores adecuados para el nombre común, puedes pasar a la siguiente sección.

Certificados administrados por el usuario

  1. Crea tu primera clave:

    openssl genrsa -out test-ingress-1.key 2048
    
  2. Crea tu primera solicitud de firma de certificado:

    openssl req -new -key test-ingress-1.key -out test-ingress-1.csr \
        -subj "/CN=FIRST_DOMAIN"
    

    Reemplaza FIRST_DOMAIN por un nombre de dominio de tu propiedad, como example.com.

  3. Crea el primer certificado:

    openssl x509 -req -days 365 -in test-ingress-1.csr -signkey test-ingress-1.key \
        -out test-ingress-1.crt
    
  4. Crea tu segunda clave:

    openssl genrsa -out test-ingress-2.key 2048
    
  5. Crea tu segunda solicitud de firma de certificado:

    openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \
        -subj "/CN=SECOND_DOMAIN"
    

    Reemplaza SECOND_DOMAIN por otro nombre de dominio de tu propiedad, como examplepetstore.com.

  6. Crea el segundo certificado:

    openssl x509 -req -days 365 -in test-ingress-2.csr -signkey test-ingress-2.key \
        -out test-ingress-2.crt
    

Para obtener más información sobre los certificados y las claves, consulta la Descripción general de certificados SSL.

Ahora tienes dos archivos de certificado y dos archivos de claves.

En las tareas restantes, se usan los siguientes marcadores de posición para hacer referencia a tus dominios, certificados y claves:

  • FIRST_CERT_FILE es la ruta de acceso a tu primer archivo de certificado.
  • FIRST_KEY_FILE es la ruta al archivo de claves que acompaña a tu primer certificado.
  • FIRST_DOMAIN es un nombre de dominio de tu propiedad.
  • FIRST_SECRET_NAME: El nombre del Secret que contiene tu primer certificado y clave.
  • SECOND_CERT_FILE es la ruta a tu segundo archivo de certificado.
  • SECOND_KEY_FILE es la ruta al archivo de claves que acompaña a tu segundo certificado.
  • SECOND_DOMAIN es un segundo nombre de dominio de tu propiedad.
  • SECOND_SECRET_NAME: El nombre del Secret que contiene tu segundo certificado y clave.

Certificados administrados por Google

Para crear certificados administrados por Google, debes agregar objetos ManagedCertificate al espacio de nombres de tu Ingress. Puedes usar la siguiente plantilla para definir certificados en tus dominios:

  apiVersion: networking.gke.io/v1
  kind: ManagedCertificate
  metadata:
    name: FIRST_CERT_NAME
  spec:
    domains:
      - FIRST_DOMAIN
  ---
  apiVersion: networking.gke.io/v1
  kind: ManagedCertificate
  metadata:
    name: SECOND_CERT_NAME
  spec:
    domains:
      - SECOND_DOMAIN

Reemplaza lo siguiente:

  • FIRST_CERT_NAME: Es el nombre de tu primer objeto ManagedCertificate.
  • FIRST_DOMAIN: Es el primer dominio que posees.
  • SECOND_CERT_NAME: Es el nombre del segundo objeto ManagedCertificate.
  • SECOND_DOMAIN: Es el segundo dominio que posees.

Los nombres de los objetos ManagedCertificate son diferentes de los nombres de los certificados reales que crean. Solo necesitas conocer los nombres de los objetos ManagedCertificate para usarlos en tu Ingress.

Especifica certificados para el Ingress

El paso siguiente es crear un objeto Ingress. En el manifiesto de Ingress, puedes usar uno de los siguientes métodos a fin de proporcionar certificados para el balanceador de cargas:

  • Secrets
  • Certificados ya compartidos
  • Certificados administrados por Google

Secrets

  1. Crea un Secreto que contenga tu primer certificado y tu primera clave:

    kubectl create secret tls FIRST_SECRET_NAME \
        --cert=FIRST_CERT_FILE \
        --key=FIRST_KEY_FILE
    
  2. Crea un secreto que contenga el segundo certificado y la segunda clave:

    kubectl create secret tls SECOND_SECRET_NAME \
        --cert=SECOND_CERT_FILE \
        --key=SECOND_KEY_FILE
    

Crea un Ingress

  1. Guarda el siguiente manifiesto como my-mc-ingress.yaml:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: my-mc-ingress
    spec:
      tls:
      - secretName: FIRST_SECRET_NAME
      - secretName: SECOND_SECRET_NAME
      rules:
      - host: FIRST_DOMAIN
        http:
          paths:
          - pathType: ImplementationSpecific
            backend:
              service:
                name: my-mc-service
                port:
                  number: 60001
      - host: SECOND_DOMAIN
        http:
          paths:
          - pathType: ImplementationSpecific
            backend:
              service:
                name: my-mc-service
                port:
                  number: 60002
    

    Reemplaza FIRST_DOMAIN y SECOND_DOMAIN por nombres de dominio de tu propiedad, como example.com y examplepetstore.com.

  2. Aplica el manifiesto al clúster:

    kubectl apply -f my-mc-ingress.yaml
    
  3. Describe tu Ingress:

    kubectl describe ingress my-mc-ingress
    

    El resultado es similar al siguiente:

    Name: my-mc-ingress
    Address: 203.0.113.1
    ...
    TLS:
      FIRST_SECRET_NAME terminates
      SECOND_SECRET_NAME terminates
    Rules:
      Host              Path  Backends
      ----              ----  --------
      FIRST_DOMAIN
                          my-mc-service:my-first-port (<none>)
      SECOND_DOMAIN
                          my-mc-service:my-second-port (<none>)
    Annotations:
    ...
    Events:
      Type    Reason  Age   From                     Message
      ----    ------  ----  ----                     -------
      Normal  ADD     3m    loadbalancer-controller  default/my-mc-ingress
      Normal  CREATE  2m    loadbalancer-controller  ip: 203.0.113.1
    

    El resultado muestra que dos Secretos están asociados con el Ingress. El resultado también muestra la dirección IP externa del balanceador de cargas. Si no se configuró la dirección IP externa, espera unos minutos y vuelve a ejecutar el comando.

Certificados ya compartidos

  1. Crea un certificado:

    gcloud compute ssl-certificates create FIRST_CERT_NAME \
        --certificate=FIRST_CERT_FILE \
        --private-key=FIRST_KEY_FILE
    

    Reemplaza lo siguiente:

    • FIRST_CERT_NAME es el nombre de tu primer certificado.
    • FIRST_CERT_FILE: Es el primer archivo de certificado.
    • FIRST_KEY_FILE: Es el primer archivo de claves.
  2. Crea un segundo certificado:

    gcloud compute ssl-certificates create SECOND_CERT_NAME \
        --certificate=SECOND_CERT_FILE \
        --private-key=SECOND_KEY_FILE
    

    Reemplaza lo siguiente:

    • SECOND_CERT_NAME es el nombre de tu segundo certificado.
    • SECOND_CERT_FILE: El segundo archivo de certificado.
    • SECOND_KEY_FILE: Es el segundo archivo de claves.
  3. Observa tus recursos de certificado:

    gcloud compute ssl-certificates list
    

    El resultado es similar al siguiente:

    NAME                   CREATION_TIMESTAMP
    FIRST_CERT_NAME      2018-11-03T12:08:47.751-07:00
    SECOND_CERT_NAME     2018-11-03T12:09:25.359-07:00
    

Crea un Ingress

  1. Guarda el siguiente manifiesto como my-psc-ingress.yaml:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: my-psc-ingress
      annotations:
        ingress.gcp.kubernetes.io/pre-shared-cert: "FIRST_CERT_NAME,SECOND_CERT_NAME"
    spec:
      rules:
      - host: FIRST_DOMAIN
        http:
          paths:
          - pathType: ImplementationSpecific
            backend:
              service:
                name: my-mc-service
                port:
                  number: 60001
      - host: SECOND_DOMAIN
        http:
          paths:
          - pathType: ImplementationSpecific
            backend:
              service:
                name: my-mc-service
                port:
                  number: 60002
    

    Reemplaza FIRST_DOMAIN y SECOND_DOMAIN por tus nombres de dominio.

    En este manifiesto, se describe un Ingress que enumera recursos de certificados ya compartidos en una anotación.

  2. Aplica el manifiesto al clúster:

    kubectl apply -f my-psc-ingress.yaml
    
  3. Describe tu Ingress:

    kubectl describe ingress my-psc-ingress
    

    El resultado es similar al siguiente:

    Name:             my-psc-ingress
    Address:          203.0.113.2
    ...
    Rules:
      Host              Path  Backends
      ----              ----  --------
      FIRST_DOMAIN
                          my-mc-service:my-first-port (<none>)
      SECOND_DOMAIN
                          my-mc-service:my-second-port (<none>)
    Annotations:
      ...
      ingress.gcp.kubernetes.io/pre-shared-cert:    FIRST_CERT_NAME,SECOND_CERT_NAME
      ...
      ingress.kubernetes.io/ssl-cert:               FIRST_CERT_NAME,SECOND_CERT_NAME
    Events:
      Type    Reason  Age   From                     Message
      ----    ------  ----  ----                     -------
      Normal  ADD     2m    loadbalancer-controller  default/my-psc-ingress
      Normal  CREATE  1m    loadbalancer-controller  ip: 203.0.113.2
    

    En el resultado se muestra que el Ingress está asociado con certificados ya compartidos llamados FIRST_CERT_NAME y SECOND_CERT_NAME. El resultado también muestra la dirección IP externa del balanceador de cargas. Si no se configuró la dirección IP externa, espera unos minutos y vuelve a intentar el comando.

Certificados administrados por Google

Crea un Ingress

  1. Guarda el siguiente manifiesto como my-gmc-ingress.yaml:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: my-gmc-ingress
      annotations:
        networking.gke.io/managed-certificates: "FIRST_CERT_NAME,SECOND_CERT_NAME"
    spec:
      rules:
      - host: FIRST_DOMAIN
        http:
          paths:
          - pathType: ImplementationSpecific
            backend:
              service:
                name: my-mc-service
                port:
                  number: 60001
      - host: SECOND_DOMAIN
        http:
          paths:
          - pathType: ImplementationSpecific
            backend:
              service:
                name: my-mc-service
                port:
                  number: 60002
    

    Reemplaza FIRST_DOMAIN y SECOND_DOMAIN por tus nombres de dominio.

    En este manifiesto, se describe un Ingress que enumera recursos de certificados ya compartidos en una anotación.

  2. Aplica el manifiesto al clúster:

    kubectl apply -f my-gmc-ingress.yaml
    
  3. Describe tu Ingress:

    kubectl describe ingress my-gmc-ingress
    

    El resultado es similar al siguiente:

    Name:             my-gmc-ingress
    Address:          203.0.113.2
    ...
    Rules:
      Host              Path  Backends
      ----              ----  --------
      FIRST_DOMAIN
                          my-mc-service:my-first-port (<none>)
      SECOND_DOMAIN
                          my-mc-service:my-second-port (<none>)
    Annotations:
      ...
      ingress.gcp.kubernetes.io/pre-shared-cert:    mcrt-a6e41ce4-2b39-4334-84ce-867ff543c424,mcrt-bbff4116-f014-4800-a43a-4095bffeb4f4
      ...
      ingress.kubernetes.io/ssl-cert:               mcrt-a6e41ce4-2b39-4334-84ce-867ff543c424,mcrt-bbff4116-f014-4800-a43a-4095bffeb4f4
      networking.gke.io/managed-certificates:       FIRST_CERT_NAME,SECOND_CERT_NAME
    Events:
      Type    Reason  Age   From                     Message
      ----    ------  ----  ----                     -------
      Normal  ADD     2m    loadbalancer-controller  default/my-gmc-ingress
      Normal  CREATE  1m    loadbalancer-controller  ip: 203.0.113.2
    

    En el resultado, se muestra que el Ingress está asociado con certificados administrados llamados FIRST_CERT_NAME y SECOND_CERT_NAME. GKE propaga automáticamente las anotaciones ingress.gcp.kubernetes.io/pre-shared-cert y ingress.kubernetes.io/ssl-cert con los certificados administrados por Google que creaste con los objetos ManagedCertificate. El resultado también muestra la dirección IP externa del balanceador de cargas. Si no se configuró la dirección IP externa, espera unos minutos y vuelve a intentar el comando.

Prueba el balanceador de cargas

Espera unos cinco minutos para que GKE termine de configurar el balanceador de cargas.

Si usaste los certificados administrados por Google, es posible que lleve mucho más tiempo en completar la configuración, ya que el sistema necesita aprovisionar los certificados y verificar la configuración de DNS para los dominios determinados.

Para probar el balanceador de cargas, debes tener dos nombres de dominio y ambos nombres deben resolver la dirección IP externa del balanceador de cargas de la aplicación externo.

  1. Envía una solicitud al balanceador de cargas mediante tu primer nombre de dominio:

    curl -v https://FIRST_DOMAIN
    

    Es posible que debas usar la opción curl -k para realizar una transferencia SSL no segura, de modo que curl aceptará certificados autofirmados.

    El resultado es similar al siguiente:

    ...
    *   Trying 203.0.113.1...
    ...
    * Connected to FIRST_DOMAIN (203.0.113.1) port 443 (#0)
    ...
    * TLSv1.2 (IN), TLS handshake, Certificate (11):
    ...
    * Server certificate:
    *  subject: CN=FIRST_DOMAIN
    ...
    > Host: FIRST_DOMAIN.com
    ...
    Hello, world!
    Version: 1.0.0
    ...
    

    El resultado muestra que el primer certificado se usó en el protocolo de enlace TLS.

  2. Envía una solicitud al balanceador de cargas mediante el segundo nombre de dominio:

    curl -v https://SECOND_DOMAIN
    

    El resultado es similar al siguiente:

    ...
    *   Trying 203.0.113.1...
    ...
    * Connected to SECOND_DOMAIN (203.0.113.1) port 443 (#0)
    ...
    * Server certificate:
    *  subject: CN=SECOND_DOMAIN
    ...
    > Host: SECOND_DOMAIN
    ...
    Hello, world!
    Version: 2.0.0
    

    Esta salida muestra que el segundo certificado se usó en el protocolo de enlace TLS.

El campo hosts de un objeto Ingress

Un IngressSpec tiene un campo tls que es un arreglo de objetos IngressTLS. Cada objeto IngressTLS tiene un campo hosts y un campo SecretName. En GKE, el campo hosts no se usa. GKE lee el nombre común (CN) del certificado en el secreto. Si el nombre común coincide con el nombre de dominio en una solicitud del cliente, entonces el balanceador de cargas presenta al cliente el certificado coincidente.

¿Qué certificado se presenta?

El balanceador de cargas elige un certificado de acuerdo con las siguientes reglas:

  • Si los Secretos y los certificados ya compartidos aparecen en el Ingress, los certificados ya compartidos tienen prioridad sobre los Secretos. En otras palabras, los Secretos siguen incluidos, pero los certificados ya compartidos se presentan primero.

  • Si ningún certificado tiene un nombre común (CN) que coincida con el nombre de dominio en la solicitud del cliente, el balanceador de cargas presenta el certificado principal.

  • Para los secretos enumerados en el bloque tls, el certificado principal se encuentra en el primer secreto de la lista.

  • Para los certificados ya compartidos que se enumeran en la anotación, el certificado principal es el primer certificado en la lista.

Prácticas recomendadas para la rotación de certificados

Si deseas rotar el contenido de tu Secret o un certificado ya compartido, sigue estas prácticas recomendadas:

  • Crea un nuevo Secreto o certificado ya compartido con un nombre diferente que contenga los datos del certificado nuevo. Conecta este recurso (junto con el existente) a tu Ingress siguiente las instrucciones anteriores. Una vez que los cambios sean de tu agrado, puedes quitar el certificado anterior del Ingress.
  • Si no te molesta interrumpir el tráfico, puedes quitar el recurso anterior del Ingress, aprovisionar un recurso nuevo con el mismo nombre, pero de contenido diferente y, luego, volver a conectarlo al Ingress.
Para evitar llevar a cabo la administración de la rotación de certificados, consulta Usa certificados SSL administrados por Google.

Soluciona problemas

Especificar los Secretos no válidos o que no existen genera un error de evento de Kubernetes. Puedes verificar los eventos de Kubernetes para un Ingress de la siguiente manera:

kubectl describe ingress

El resultado es similar al siguiente:

Name:             my-ingress
Namespace:        default
Address:          203.0.113.3
Default backend:  hello-server:8080 (10.8.0.3:8080)
TLS:
  my-faulty-Secret terminates
Rules:
  Host  Path  Backends
  ----  ----  --------
  *     *     my-service:443 (10.8.0.3:443)
Events:
   Error during sync: cannot get certs for Ingress default/my-ingress:
 Secret "my-faulty-ingress" has no 'tls.crt'

¿Qué sigue?

  • Si tienes una aplicación que se ejecuta en varios clústeres de GKE en regiones diferentes, configura Ingress de varios clústeres para enrutar el tráfico a un clúster en la región más cercana al usuario.