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

En esta página, se muestra cómo usar varios certificados SSL para Ingress con balanceo de cargas interno y externo.

Descripción general

Si deseas aceptar solicitudes HTTPS de tus clientes, el balanceador de cargas de HTTP(S) interno o externo debe tener un certificado para 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 la encriptación TLS y reenvía la solicitud sin encriptación a la aplicación. Cuando configuras un balanceador de cargas de HTTP(S) 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 aparece 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 de Ingress

Para proporcionar a un balanceador de cargas de HTTP(S) un certificado SSL de Google Cloud administrado por ti, usa un certificado ya compartido que hayas subido al proyecto de Google Cloud. Para proporcionar a un balanceador de cargas de HTTP(S) un certificado y una clave que hayas creado, genera un secreto. El secreto contiene el certificado y la clave. Para usar un secreto, agrega su nombre en el 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.

Versión mínima de GKE

Debes tener la versión 1.10.2 o una posterior de GKE para usar certificados ya compartidos o especificar varios certificados en un Ingress.

Panorama general

A continuación, hay una descripción general de los pasos en este tema:

  1. Crea una implementación

  2. Crea un servicio

  3. Crea dos archivos de certificado y dos archivos de claves.

  4. Crea un Ingress que use secretos o certificados ya compartidos. Como resultado de la creación de un Ingress, GKE crea y configura un balanceador de cargas de HTTP(S).

  5. Prueba el balanceador de cargas de HTTP(S).

Antes de comenzar

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

Establece la configuración de gcloud predeterminada mediante uno de los siguientes métodos:

  • Usa gcloud init si quieres aprender acerca de los valores predeterminados de la configuración.
  • Usa gcloud config para establecer el ID, la zona y la región del proyecto de manera individual.

Usa gcloud init

  1. Ejecuta gcloud init y sigue estas instrucciones:

    gcloud init

    Si usas SSH en un servidor remoto, usa la marca --console-only para evitar que el comando inicie un navegador:

    gcloud init --console-only
  2. Sigue las instrucciones a fin de autorizar a gcloud para que use tu cuenta de Google Cloud.
  3. Crea una configuración nueva o selecciona una existente.
  4. Elige un proyecto de Google Cloud.
  5. Elige una zona predeterminada de Compute Engine.

Usa la configuración de gcloud

  • Establece tu ID del proyecto predeterminado:
    gcloud config set project project-id
  • Si trabajas con clústeres zonales, establece tu zona de procesamiento predeterminada:
    gcloud config set compute/zone compute-zone
  • Si trabajas con clústeres regionales, establece tu región de procesamiento predeterminada:
    gcloud config set compute/region compute-region
  • Descarga la versión más reciente de gcloud:
    gcloud components update

Crea una Implementación

A continuación, se muestra un manifiesto para una implementación:

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: "gcr.io/google-samples/hello-app:2.0"
            env:
            - name: "PORT"
              value: "50001"
          - name: hello-again
            image: "gcr.io/google-samples/node-hello:1.0"
            env:
            - name: "PORT"
              value: "50002"
    

La Implementación tiene tres pods, y cada pod tiene dos contenedores. Un contenedor ejecuta hello-app y escucha en el puerto TCP 50001. El otro contenedor ejecuta node-hello y escucha en el puerto TCP 50002.

Copia el manifiesto en un archivo llamado my-mc-deployment.yaml y crea la Implementación con el siguiente comando:

    kubectl apply -f my-mc-deployment.yaml
    

Crea un servicio

A continuación, se detalla un manifiesto para un servicio:

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
    

El campo selector en el manifiesto del servicio indica que cualquier pod con las etiquetas app: products y department: sales es miembro de este servicio. Por lo tanto, los pods de la Implementación que creaste en el paso anterior son miembros del servicio.

El campo ports del manifiesto del servicio es un arreglo de objetos ServicePort. Cuando un cliente envía una solicitud al servicio en my-first-port, esta se reenvía a uno de los pods miembro en el puerto 50001. Cuando un cliente envía una solicitud al servicio en my-second-port, esta se reenvía a uno de los pods miembro en el puerto 50002.

Copia el manifiesto en un archivo llamado my-mc-service.yaml y crea el servicio:

    kubectl apply -f my-mc-service.yaml
    

Crea 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 que poseas. Si ya tienes dos archivos de certificado con los valores adecuados para el nombre común, puedes pasar a la siguiente sección.

Crea tu primera clave:

    openssl genrsa -out test-ingress-1.key 2048
    

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"
    

En el ejemplo anterior, first-domain es un nombre de dominio de tu propiedad o uno falso.

Por ejemplo, supongamos que deseas que el balanceador de cargas entregue solicitudes desde el dominio your-store.example. La solicitud de firma de certificado debería verse así:

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

Crea el primer certificado:

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

Crea tu segunda clave:

    openssl genrsa -out test-ingress-2.key 2048
    

Crea la segunda solicitud de firma de certificado:

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

En el ejemplo anterior, second-domain es otro nombre de dominio de tu propiedad o uno falso.

Por ejemplo, supongamos que deseas que el balanceador de cargas entregue solicitudes desde el dominio your-experimental-store.example. La solicitud de firma de certificado debería verse así:

    openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \
        -subj "/CN=your-experimental-store.example"
    

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 la creación de certificados y claves, consulta Obtén una clave privada y un certificado firmado.

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 al archivo del primer certificado.
  • first-key-file es la ruta al archivo de claves que acompaña al primer certificado.
  • first-domain es un nombre de dominio de tu propiedad o uno falso.
  • first-secret-name es el nombre del secreto que contiene el primer certificado y la primera clave.
  • second-cert-file es la ruta al archivo del segundo certificado.
  • second-key-file es la ruta al archivo de claves que acompaña al segundo certificado.
  • second-domain es un segundo nombre de dominio de tu propiedad o uno falso.
  • second-secret-name es el nombre del secreto que contiene el segundo certificado y la segunda clave.

Especifica certificados para el Ingress

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

  • Secretos
  • Certificados ya compartidos

Selecciona la pestaña SECRETS o PRE-SHARED CERTS para elegir uno de los métodos:

Secretos

Crea secretos

Crea un secreto que contenga el primer certificado y la primera clave:

    kubectl create secret tls first-secret-name \
      --cert first-cert-file --key first-key-file
    

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

A continuación, se detalla un manifiesto para un Ingress:

apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: my-mc-ingress
    spec:
      tls:
      - secretName: first-secret-name
      - secretName: second-secret-name
      rules:
      - host: first-domain
        http:
          paths:
          - backend:
              serviceName: my-mc-service
              servicePort: my-first-port
      - host: second-domain
        http:
          paths:
          - backend:
              serviceName: my-mc-service
              servicePort: my-second-port
    

Copia el manifiesto en un archivo llamado my-mc-ingress.yaml. Reemplaza first-domain y second-domain por nombres de dominio de tu propiedad o falsos.

Crea el Ingress con el siguiente comando:

    kubectl apply -f my-mc-ingress.yaml
    

Cuando creas un Ingress, el controlador de entrada de GKE crea un balanceador de cargas de HTTP(S). Espera un minuto para que GKE asigne una dirección IP externa al balanceador de cargas.

Describe el Ingress con el siguiente comando:

    kubectl describe ingress my-mc-ingress
    

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.

Name: my-mc-ingress
    Address: 203.0.113.1
    ...
    TLS:
      first-secret-name terminates
      second-secret-name terminates
    Rules:
      Host              Path  Backends
      ----              ----  --------
      your-store.example
                         my-mc-service:my-first-port (<none>)
      your-experimental-store.example
                         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
    

Certificados ya compartidos

Usa certificados ya compartidos

Crea un recurso de certificado en tu proyecto de Google Cloud:

    gcloud compute ssl-certificates create test-ingress-1 \
    --certificate first-cert-file --private-key first-key-file
    

En el ejemplo anterior, se ilustra lo siguiente:

Crea un segundo recurso de certificado en tu proyecto de Google Cloud:

    gcloud compute ssl-certificates create test-ingress-2 \
    --certificate second-cert-file --private-key second-key-file
    

En el ejemplo anterior, se ilustra lo siguiente:

  • second-cert-file es el segundo archivo de certificado.
  • second-key-file es el segundo archivo de claves.

Observa los recursos de certificado:

    gcloud compute ssl-certificates list
    

El resultado muestra que tienes recursos de certificado llamados test-ingres-1 y test-ingress-2:

NAME                CREATION_TIMESTAMP
    test-ingress-1      2018-11-03T12:08:47.751-07:00
    test-ingress-2      2018-11-03T12:09:25.359-07:00
    

A continuación, se muestra un manifiesto para un Ingress en el que se enumeran recursos de certificados ya compartidos en una anotación:

apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: my-psc-ingress
      annotations:
        ingress.gcp.kubernetes.io/pre-shared-cert: "test-ingress-1,test-ingress-2"
    spec:
      rules:
      - host: first-domain
        http:
          paths:
          - backend:
              serviceName: my-mc-service
              servicePort: my-first-port
      - host: second-domain
        http:
          paths:
          - backend:
              serviceName: my-mc-service
              servicePort: my-second-port
    

Copia el manifiesto en un archivo llamado my-psc-ingress.yaml. Reemplaza first-domain y second-domain por tus nombres de dominio o nombres de dominio falsos.

Crea el Ingress con el siguiente comando:

    kubectl apply -f my-psc-ingress.yaml
    

Espera un minuto para que GKE asigne una dirección IP externa al balanceador de cargas.

Describe el Ingress con el siguiente comando:

    kubectl describe ingress my-psc-ingress
    

El resultado muestra que el Ingress está asociado con certificados ya compartidos llamados test-ingress-1 y test-ingress-2. El resultado también muestra la dirección IP externa del balanceador de cargas:

Name:             my-psc-ingress
    Address:          203.0.113.2
    ...
    Rules:
      Host              Path  Backends
      ----              ----  --------
      your-store.example
                         my-mc-service:my-first-port (<none>)
      your-experimental-store.example
                         my-mc-service:my-second-port (<none>)
    Annotations:
      ...
      ingress.gcp.kubernetes.io/pre-shared-cert:    test-ingress-1,test-ingress-2
      ...
      ingress.kubernetes.io/ssl-cert:               test-ingress-1,test-ingress-2
    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
    

Prueba el balanceador de cargas

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

Para realizar este paso, debes tener dos nombres de dominio, y ambos deben resolver la dirección IP externa del balanceador de cargas de HTTP(S).

Envía una solicitud al balanceador de cargas mediante el primer nombre de dominio:

    curl -v https://first-domain
    

El resultado muestra que el primer certificado se usó en el protocolo de enlace TLS. Si el primer dominio es your-store.example, el resultado es similar a este:

...
    *   Trying 203.0.113.1...
    ...
    * Connected to your-store.example (203.0.113.1) port 443 (#0)
    ...
    * TLSv1.2 (IN), TLS handshake, Certificate (11):
    ...
    * Server certificate:
    *  subject: CN=your-store.example
    ...
    > Host: your-store.example
    ...
    &lt;
    Hello, world!
    Version: 2.0.0
    ...
    

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

    curl -v https://second-domain
    

El resultado muestra que el segundo certificado se usó en el protocolo de enlace TLS. Si el segundo dominio es your-experimental-store.example, el resultado es similar a este:

...
    *   Trying 203.0.113.1...
    ...
    * Connected to your-experimental-store.example (203.0.113.1) port 443 (#0)
    ...
    * Server certificate:
    *  subject: CN=your-experimental-store.example
    ...
    > Host: your-experimental-store.example
    ...
    Hello Kubernetes!
    

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, 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 se enumeran en el Ingress, el balanceador de cargas ignora los secretos y usa la lista de certificados ya compartidos.

  • 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.

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

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 ejemplo:

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'
    

Próximos pasos