Usar varios certificados SSL en el balanceo de carga HTTPS con Ingress

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

Información general

Si quieres aceptar solicitudes HTTPS de tus clientes, el balanceador de carga de aplicaciones debe tener un certificado para poder demostrar su identidad a tus clientes. El balanceador de carga también debe tener una clave privada para completar el handshake de HTTPS.

Cuando el balanceador de carga acepta una solicitud HTTPS de un cliente, el tráfico entre el cliente y el balanceador de carga se cifra mediante TLS. Sin embargo, el balanceador de carga finaliza el cifrado TLS y reenvía la solicitud sin cifrar a la aplicación. Cuando configuras un balanceador de carga de aplicaciones a través de Ingress, puedes configurar el balanceador de carga para que presente hasta 15 certificados TLS al cliente.

El balanceador de carga usa la indicación del nombre de servidor (SNI) para determinar qué certificado debe presentar al cliente en función del nombre de dominio del handshake de TLS. Si el cliente no usa SNI o si usa un nombre de dominio que no coincide con el nombre común (CN) de uno de los certificados, el balanceador de carga usará el primer certificado que aparezca en el recurso Ingress.

En el siguiente diagrama se muestra el balanceador de carga que envía tráfico a diferentes backends en función del nombre de dominio utilizado en la solicitud:

Diagrama del sistema de varios certificados SSL con Ingress

Puedes proporcionar a un balanceador de carga de aplicaciones certificados SSL mediante los siguientes métodos:

  • Certificados SSL gestionados por Google Consulta la página de certificados gestionados para obtener información sobre cómo usarlos.

  • Trusted Cloud by S3NS Certificado SSL que gestionas tú. El certificado SSL usa un certificado precompartido que subes a tu Trusted Cloud by S3NS proyecto.

  • Secretos de Kubernetes. El secreto contiene un certificado y una clave que creas tú mismo. Añade el nombre del secreto al campo tls del archivo de manifiesto de Ingress.

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

Perspectiva general

A continuación, se muestra un resumen de los pasos que se describen en este documento:

  1. Crea un despliegue.

  2. Crea un servicio.

  3. Crea dos archivos de certificado y dos archivos de clave, o dos objetos ManagedCertificate. Debes configurar estos certificados en el mismo proyecto y en el mismo espacio de nombres en el que se haya implementado el balanceador de carga.

  4. Crea un Ingress que use secretos o certificados precompartidos. Cuando creas el Ingress, GKE crea y configura un balanceador de carga de aplicaciones.

  5. Prueba el balanceador de carga de aplicación.

Antes de empezar

Antes de empezar, asegúrate de que has realizado las siguientes tareas:

  • Habilita la API de Google Kubernetes Engine.
  • Habilitar la API de Google Kubernetes Engine
  • Si quieres usar Google Cloud CLI para esta tarea, instálala y, a continuación, inicialízala. Si ya has instalado la gcloud CLI, obtén la versión más reciente ejecutando gcloud components update.
  • Debes tener dos nombres de dominio. Los nombres de dominio no pueden tener más de 63 caracteres.
  • Asegúrate de que tienes un clúster Autopilot o Standard. Para crear un clúster, consulta Crear un clúster de Autopilot.

Limitaciones

  • Los certificados gestionados por Google solo se admiten con Ingress de GKE mediante el balanceador de carga de aplicaciones externo. Los certificados gestionados por Google no admiten controladores Ingress de terceros.

  • En el caso de los balanceadores de carga de aplicaciones internos, debes inhabilitar HTTP en el manifiesto de Ingress. No es necesario para el balanceador de carga externo.

  • No debes cambiar ni actualizar manualmente la configuración del balanceador de carga de aplicaciones. Esto significa que no debe editar ninguno de los componentes del balanceador de carga, incluidos los proxies de destino, los mapas de URLs y los servicios de backend. GKE sobrescribe los cambios que hagas.

Crear un despliegue

  1. Guarda el siguiente archivo de 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"
    

    Este manifiesto 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 a tu clúster:

    kubectl apply -f my-mc-deployment.yaml
    

Crea un servicio

  1. Guarda el siguiente archivo de 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
    

    Este manifiesto describe un servicio con los siguientes campos:

    • selector: especifica que cualquier pod que tenga la etiqueta app: products y la etiqueta 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 miembros 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 miembros en el puerto 50002.
  2. Aplica el manifiesto a tu clúster:

    kubectl apply -f my-mc-service.yaml
    

Crear certificados y claves

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

Puedes crear esos certificados manualmente o usar certificados gestionados por Google.

Si ya tiene dos archivos de certificado con los valores adecuados para Nombre común, puede pasar a la siguiente sección.

Certificados gestionados 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"
    

    Sustituye FIRST_DOMAIN por un nombre de dominio que te pertenezca, como example.com.

  3. Crea tu 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"
    

    Sustituye SECOND_DOMAIN por otro nombre de dominio que te pertenezca, como examplepetstore.com.

  6. Crea tu 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 el artículo Información general sobre los certificados SSL.

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

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

  • FIRST_CERT_FILE: la ruta a tu primer archivo de certificado.
  • FIRST_KEY_FILE: la ruta al archivo de clave que va con tu primer certificado.
  • FIRST_DOMAIN: un nombre de dominio que sea de tu propiedad.
  • FIRST_SECRET_NAME: el nombre del secreto que contiene tu primer certificado y tu primera clave.
  • SECOND_CERT_FILE: la ruta a tu segundo archivo de certificado.
  • SECOND_KEY_FILE: la ruta al archivo de clave que corresponde a tu segundo certificado.
  • SECOND_DOMAIN: un segundo nombre de dominio que sea de tu propiedad.
  • SECOND_SECRET_NAME: el nombre del secreto que contiene tu segundo certificado y clave.

Certificados gestionados por Google

Para crear certificados gestionados por Google, debes añadir objetos ManagedCertificate al espacio de nombres de tu Ingress. Puedes usar la siguiente plantilla para definir los certificados de 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

Haz los cambios siguientes:

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

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

Especificar certificados para tu Ingress

El siguiente paso es crear un objeto Ingress. En el manifiesto de Ingress, puede usar uno de los siguientes métodos para proporcionar certificados al balanceador de carga:

  • Secretos
  • Certificados precompartidos
  • Certificados gestionados por Google

Secretos

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

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

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

Crear un Ingress

  1. Guarda el siguiente archivo de 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
    

    Sustituye FIRST_DOMAIN y SECOND_DOMAIN por nombres de dominio que te pertenezcan, como example.com y examplepetstore.com.

  2. Aplica el manifiesto a tu clúster:

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

    kubectl describe ingress my-mc-ingress
    

    El resultado debería ser 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 hay dos secretos asociados al Ingress. El resultado también muestra la dirección IP externa del balanceador de carga. Si la dirección IP externa no está definida, espera unos minutos y vuelve a probar el comando.

Certificados precompartidos

  1. Crea un certificado:

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

    Haz los cambios siguientes:

    • FIRST_CERT_NAME: el nombre de tu primer certificado.
    • FIRST_CERT_FILE: tu primer archivo de certificado.
    • FIRST_KEY_FILE: tu 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
    

    Haz los cambios siguientes:

    • SECOND_CERT_NAME: el nombre de tu segundo certificado.
    • SECOND_CERT_FILE: tu segundo archivo de certificado.
    • SECOND_KEY_FILE: tu segundo archivo de claves.
  3. Consulta los recursos de tu certificado:

    gcloud compute ssl-certificates list
    

    El resultado debería ser 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
    

Crear un Ingress

  1. Guarda el siguiente archivo de 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
    

    Sustituye FIRST_DOMAIN y SECOND_DOMAIN por los nombres de tus dominios.

    Este manifiesto describe un Ingress que enumera los recursos de certificados precompartidos en una anotación.

  2. Aplica el manifiesto a tu clúster:

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

    kubectl describe ingress my-psc-ingress
    

    El resultado debería ser 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
    

    El resultado muestra que el Ingress está asociado a certificados precompartidos llamados FIRST_CERT_NAME y SECOND_CERT_NAME. La salida también muestra la dirección IP externa del balanceador de carga. Si la dirección IP externa no está definida, espera unos minutos y vuelve a probar el comando.

Certificados gestionados por Google

Crear un Ingress

  1. Guarda el siguiente archivo de 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
    

    Sustituye FIRST_DOMAIN y SECOND_DOMAIN por los nombres de tus dominios.

    Este manifiesto describe un Ingress que enumera los recursos de certificados precompartidos en una anotación.

  2. Aplica el manifiesto a tu clúster:

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

    kubectl describe ingress my-gmc-ingress
    

    El resultado debería ser 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
    

    La salida muestra que el Ingress está asociado a certificados gestionados llamados FIRST_CERT_NAME y SECOND_CERT_NAME. GKE rellena automáticamente las anotaciones ingress.gcp.kubernetes.io/pre-shared-cert y ingress.kubernetes.io/ssl-cert con los certificados gestionados por Google que has creado con los objetos ManagedCertificate. La salida también muestra la dirección IP externa del balanceador de carga. Si no se ha definido la dirección IP externa, espera unos minutos y vuelve a probar el comando.

Probar el balanceador de carga

Espera unos cinco minutos a que GKE termine de configurar el balanceador de carga.

Si has usado certificados gestionados por Google, puede que la configuración tarde bastante más en completarse, ya que el sistema tiene que aprovisionar los certificados y verificar la configuración de DNS de los dominios en cuestión.

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

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

    curl -v https://FIRST_DOMAIN
    

    Es posible que tengas que usar la opción curl -k para realizar una transferencia SSL no segura, de forma que curl acepte certificados autofirmados.

    El resultado debería ser 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
    ...
    

    En este resultado se muestra que tu primer certificado se ha usado en el handshake TLS.

  2. Envía una solicitud al balanceador de carga mediante tu segundo nombre de dominio:

    curl -v https://SECOND_DOMAIN
    

    El resultado debería ser 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 tu segundo certificado se ha usado en el handshake de TLS.

El campo hosts de un objeto Ingress

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

¿Qué certificado se presenta?

El balanceador de carga elige un certificado según estas reglas:

  • Si tanto los secretos como los certificados precompartidos se incluyen en el Ingress, los certificados precompartidos tienen prioridad sobre los secretos. Es decir, los secretos se siguen incluyendo, pero los certificados precompartidos se presentan primero.

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

  • En el caso de los secretos que aparecen en el bloque tls, el certificado principal es el primer secreto de la lista.

  • En el caso de los certificados precompartidos que se indican en la anotación ingress.gcp.kubernetes.io/pre-shared-cert, el orden en el que se enumeran los certificados determina el certificado principal. El certificado principal, que se presenta cuando ningún otro certificado coincide con la solicitud del cliente, es el primer certificado que aparece en la anotación.

  • Cuando usas certificados gestionados por Google, todos los certificados que se indican en la anotación networking.gke.io/managed-certificates se ordenan automáticamente por nombre en orden alfabético. El certificado principal es el que aparece en primer lugar en esta lista alfabética. Para definir un certificado específico como principal, debes asignar nombres a tus objetos ManagedCertificate de forma que se controle el orden de clasificación. Por ejemplo, para que my-default-cert sea el principal en lugar de another-cert, puedes llamarlos 0-my-default-cert y 1-another-cert.

Prácticas recomendadas para la rotación de certificados

Si quieres rotar el contenido de tu secreto o certificado precompartido, te recomendamos que sigas estas prácticas:

  • Crea un secreto o un certificado precompartido con otro nombre que contenga los nuevos datos del certificado. Adjunta este recurso (junto con el que ya tienes) a tu Ingress siguiendo las instrucciones que se han proporcionado anteriormente. Cuando estés conforme con los cambios, puedes quitar el certificado antiguo del Ingress.
  • Si no te importa interrumpir el tráfico, puedes quitar el recurso antiguo del Ingress, aprovisionar un recurso nuevo con el mismo nombre, pero con contenido diferente, y, a continuación, volver a adjuntarlo al Ingress.

Para no tener que gestionar la rotación de certificados, consulta el artículo Usar certificados SSL gestionados por Google.

Solución de problemas

Si se especifican secretos no válidos o que no existen, se produce un error de evento de Kubernetes. Para consultar los eventos de Kubernetes de un Ingress, sigue estos pasos:

kubectl describe ingress

El resultado debería ser 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'

Siguientes pasos