Utiliser plusieurs certificats SSL dans l'équilibrage de charge HTTPS avec un objet Entrée

Cette page vous explique comment utiliser plusieurs certificats SSL pour un objet Ingress avec des équilibrages de charge interne et externe.

Présentation

Si vous souhaitez accepter les requêtes HTTPS émanant de vos clients, l'équilibreur de charge HTTP(S) interne ou externe doit disposer d'un certificat lui permettant de leur prouver son identité. L'équilibreur de charge doit également disposer d'une clé privée pour effectuer le handshake HTTPS.

Lorsque l'équilibreur de charge accepte une requête HTTPS d'un client, le trafic entre le client et l'équilibreur de charge est chiffré via TLS. Cependant, l'équilibreur de charge interrompt ce chiffrement TLS et transfère la requête sans chiffrement à l'application. Lorsque vous configurez un équilibreur de charge HTTP(S) via Ingress, vous pouvez le configurer pour qu'il présente au maximum dix certificats TLS au client.

L'équilibreur de charge utilise l'indication du nom du serveur SNI (Server Name Indication) pour déterminer le certificat à présenter au client, en fonction du nom de domaine figurant dans le handshake TLS. Si le client n'utilise pas SNI ou utilise un nom de domaine qui ne correspond pas au nom commun (CN) de l'un des certificats, l'équilibreur de charge utilise le premier certificat répertorié dans l'objet Entrée. Le diagramme suivant illustre l’équilibrage de charge qui envoie le trafic à différents backends, en fonction du nom de domaine utilisé dans la requête :

Certificats SSL multidomaines avec diagramme du système Ingress

Pour fournir un équilibreur de charge HTTPS avec des certificats SSL, vous pouvez utiliser l'une des trois méthodes suivantes :

  • Certificats SSL gérés par Google. Pour plus d'informations sur leur utilisation, reportez-vous à la page des certificats gérés.

  • Certificat SSL Google Cloud que vous gérez vous-même. Il utilise un certificat prépartagé que vous avez précédemment importé dans votre projet Google Cloud.

  • Secrets Kubernetes Le secret contient un certificat et une clé que vous créez vous-même. Pour utiliser un secret, ajoutez son nom dans le champ tls de votre fichier manifeste d'entrée.

Vous pouvez utiliser plusieurs méthodes dans la même entrée. Cela évite les temps d'arrêt entre les méthodes lors de la migration.

Version minimale de GKE

Vous devez utiliser GKE version 1.10.2 ou ultérieure pour utiliser des certificats prépartagés ou pour spécifier plusieurs certificats dans une entrée.

Prérequis

Pour réaliser les exercices de cette page, vous devez posséder deux noms de domaine. Vous pouvez utiliser Google Domains ou un autre bureau d'enregistrement.

L'essentiel

Voici une présentation du processus :

  1. Créez un déploiement.

  2. Créez un service.

  3. Créez deux fichiers de certificat et deux fichiers de clé.

  4. Créez une entrée qui utilise des Secrets ou des certificats prépartagés. Après avoir créé l'entrée, GKE crée et configure un équilibreur de charge HTTP(S).

  5. Testez l'équilibreur de charge HTTP(S).

Avant de commencer

Avant de commencer, effectuez les tâches suivantes :

Configurez les paramètres gcloud par défaut à l'aide de l'une des méthodes suivantes :

  • Utilisez gcloud init pour suivre les instructions permettant de définir les paramètres par défaut.
  • Utilisez gcloud config pour définir individuellement l'ID, la zone et la région de votre projet.

Utiliser gcloud init

Si le message d'erreur One of [--zone, --region] must be supplied: Please specify location s'affiche, effectuez les tâches ci-dessous.

  1. Exécutez gcloud init et suivez les instructions :

    gcloud init

    Si vous utilisez SSH sur un serveur distant, utilisez l'option --console-only pour empêcher la commande d'ouvrir un navigateur :

    gcloud init --console-only
  2. Suivez les instructions pour autoriser gcloud à utiliser votre compte Google Cloud.
  3. Créez ou sélectionnez une configuration.
  4. Choisissez un projet Google Cloud.
  5. Choisissez une zone Compute Engine par défaut pour les clusters zonaux ou une région pour les clusters régionaux ou Autopilot.

Utiliser gcloud config

  • Définissez votre ID de projet par défaut :
    gcloud config set project PROJECT_ID
  • Si vous utilisez des clusters zonaux, définissez votre zone de calcul par défaut :
    gcloud config set compute/zone COMPUTE_ZONE
  • Si vous utilisez des clusters Autopilot ou régionaux, définissez votre région de calcul par défaut :
    gcloud config set compute/region COMPUTE_REGION
  • Mettez à jour gcloud vers la dernière version :
    gcloud components update

Créer un déploiement

Voici le fichier manifeste d'un déploiement :

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"

Le déploiement comporte trois pods et chaque pod englobe deux conteneurs. Un conteneur exécute hello-app et écoute sur le port TCP 50001. L'autre conteneur exécute node-hello et écoute sur le port TCP 50002.

Copiez le fichier manifeste dans un fichier nommé my-mc-deployment.yaml, puis créez le déploiement :

kubectl apply -f my-mc-deployment.yaml

Créer un service

Voici un fichier manifeste de service :

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

Le champ selector du fichier manifeste de service indique que tous les pods comportant les étiquettes app: products et department: sales sont membres de ce service. Les pods du déploiement que vous avez créés à l'étape précédente sont donc membres du service.

Le champ ports du fichier manifeste de service est un tableau d'objets ServicePort. Lorsqu'un client envoie une requête au service sur my-first-port, la requête est transmise à l'un des pods membres sur le port 50001. Lorsqu'un client envoie une requête au service sur my-second-port, la requête est transmise à l'un des pods membres sur le port 50002.

Copiez le fichier manifeste dans un fichier nommé my-mc-service.yaml, puis créez le service :

kubectl apply -f my-mc-service.yaml

Créer des certificats et des clés

Pour faire les exercices de cette page, vous avez besoin de deux certificats, chacun avec une clé correspondante. Chaque certificat doit avoir un nom commun (CN) correspondant à un nom de domaine que vous possédez. Si vous avez déjà deux fichiers de certificat avec les valeurs appropriées pour le nom commun, vous pouvez passer à la section suivante.

Créez votre première clé :

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

Créez votre première requête de signature de certificat :

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

first-domain est un nom de domaine que vous possédez.

Par exemple, supposons que vous souhaitiez que l'équilibreur de charge réponde aux requêtes provenant du domaine example.com. Votre requête de signature de certificat devrait se présenter comme suit :

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

Créez votre premier certificat :

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

Créez votre deuxième clé :

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

Créez votre deuxième requête de signature de certificat :

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

second-domain est un autre nom de domaine que vous possédez.

Par exemple, supposons que vous souhaitiez que l'équilibreur de charge réponde aux requêtes provenant du domaine examplepetstore.com. Votre requête de signature de certificat devrait se présenter comme suit :

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

Créez votre deuxième certificat :

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

Pour en savoir plus sur les certificats et les clés, consultez la présentation des certificats SSL.

Vous possédez maintenant deux fichiers de certificat et deux fichiers de clé.

Les tâches restantes utilisent les espaces réservés suivants pour désigner vos domaines, certificats et clés :

  • first-cert-file est le chemin d'accès à votre premier fichier de certificat.
  • first-key-file est le chemin d'accès au fichier de clé qui accompagne votre premier certificat.
  • first-domain est un nom de domaine que vous possédez.
  • first-secret-name est le nom du secret contenant votre premier certificat et votre première clé.
  • second-cert-file est le chemin d'accès à votre deuxième fichier de certificat.
  • second-key-file est le chemin d'accès du fichier de clé qui accompagne votre deuxième certificat.
  • second-domain est un deuxième nom de domaine que vous possédez.
  • second-secret-name est le nom du secret contenant votre deuxième certificat et votre deuxième clé.

Spécifier des certificats pour votre objet Ingress

L'étape suivante consiste à créer un objet Entrée. Dans votre fichier manifeste Entrée, vous pouvez utiliser l'une des deux méthodes suivantes pour fournir des certificats à l'équilibreur de charge :

  • Secrets
  • Certificats prépartagés

Choisissez l'une des deux méthodes en sélectionnant l'onglet SECRETS ou l'onglet PRE-SHARED CERTS :

Secrets

Créer des Secrets

Créez un Secret contenant votre premier certificat et votre première clé :

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

Créez un Secret contenant votre deuxième certificat et votre deuxième clé :

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

Créer une Entrée

Voici un exemple de manifeste pour une entrée :

apiVersion: networking.k8s.io/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

Copiez le fichier manifeste dans un fichier nommé my-mc-ingress.yaml. Remplacez first-domain et second-domain par des noms de domaine que vous possédez, par exemple example.com et examplepetstore.com.

Créez l'entrée :

kubectl apply -f my-mc-ingress.yaml

Lorsque vous créez un objet Ingress, le contrôleur GKE Ingress crée un équilibreur de charge HTTP(S). Attendez une minute pour que GKE attribue une adresse IP externe à l'équilibreur de charge.

Décrivez votre entrée :

kubectl describe ingress my-mc-ingress

Le résultat montre que deux Secrets sont associés à l'entrée. Le résultat affiche également l'adresse IP externe de l'équilibreur de charge.

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

Certificats prépartagés

Utiliser des certificats prépartagés

Créez une ressource de certificat dans votre projet Google Cloud :

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

où :

Créez une deuxième ressource de certificat dans votre projet Google Cloud :

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

où :

  • second-cert-file est votre deuxième fichier de certificat.
  • second-key-file est votre deuxième fichier de clé.

Affichez vos ressources de certificat :

gcloud compute ssl-certificates list

Le résultat indique que vous disposez de ressources de certificat nommées test-ingres-1 et 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

Voici un fichier manifeste pour une entrée qui répertorie les ressources de certificat prépartagées dans une annotation :

apiVersion: networking.k8s.io/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

Copiez le fichier manifeste dans un fichier nommé my-psc-ingress.yaml. Remplacez first-domain et second-domain par vos noms de domaine.

Créez l'entrée :

kubectl apply -f my-psc-ingress.yaml

Attendez une minute pour que GKE attribue une adresse IP externe à l'équilibreur de charge.

Décrivez votre entrée :

kubectl describe ingress my-psc-ingress

Le résultat montre que l'entrée est associée à des certificats prépartagés nommés test-ingress-1 et test-ingress-2. La sortie affiche également l'adresse IP externe de l'équilibreur de charge.

Name:             my-psc-ingress
Address:          203.0.113.2
...
Rules:
  Host              Path  Backends
  ----              ----  --------
  example.com
                     my-mc-service:my-first-port (<none>)
  examplepetstore.com
                     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

Tester l'équilibreur de charge

Attendez environ cinq minutes que GKE ait fini de configurer l'équilibreur de charge.

Pour effectuer cette étape, vous devez posséder deux noms de domaine et vos deux noms de domaine doivent résoudre l'adresse IP externe de l'équilibreur de charge HTTP(S).

Envoyez une requête à l'équilibreur de charge en utilisant votre premier nom de domaine :

curl -v https://first-domain

La sortie indique que votre premier certificat a été utilisé lors de l'établissement du handshake TLS. Si votre premier domaine est example.com, le résultat ressemble à ceci :

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

Envoyez une requête à l'équilibreur de charge en utilisant votre deuxième nom de domaine :

curl -v https://second-domain

Le résultat indique que votre deuxième certificat a été utilisé lors de l'établissement du handshake TLS. Si votre deuxième domaine est examplepetstore.com, le résultat ressemble à ceci :

...
*   Trying 203.0.113.1...
...
* Connected to examplepetstore.com (203.0.113.1) port 443 (#0)
...
* Server certificate:
*  subject: CN=examplepetstore.com
...
> Host: examplepetstore.com
...
Hello Kubernetes!

Le champ "hosts" d'un objet Entrée

Un objet IngressSpec possède un champ tls qui est un tableau d'objets IngressTLS. Chaque objet IngressTLS possède un champ hosts et un champ SecretName. Dans GKE, le champ hosts n'est pas utilisé. GKE lit le nom commun (CN) du certificat dans le Secret. Si le nom commun correspond au nom de domaine dans une requête de client, l'équilibreur de charge présente le certificat correspondant au client.

Quel certificat est présenté ?

L'équilibreur de charge choisit un certificat en fonction des règles suivantes :

  • Si les secrets et les certificats prépartagés sont répertoriés dans l'objet Ingress (entrée), les certificats prépartagés sont prioritaires sur les secrets. En d'autres termes, les secrets sont toujours inclus, mais les certificats prépartagés sont présentés en premier.

  • Si aucun certificat ne possède de nom commun (CN) correspondant au nom de domaine dans une requête de client, l'équilibreur de charge présente le certificat principal.

  • Pour les secrets répertoriés dans le bloc tls, le certificat principal figure dans le premier secret de la liste.

  • Pour les certificats prépartagés répertoriés dans l'annotation, le certificat principal est le premier certificat de la liste.

Bonnes pratiques concernant la rotation des certificats

Si vous souhaitez alterner le contenu de votre certificat (secret ou prépartagé), voici quelques bonnes pratiques :

  • Créez un certificat secret ou prépartagé avec un autre nom, contenant les nouvelles données de certificat. Associez cette ressource (ainsi que la ressource existante) à votre entrée (objet Ingress) en suivant les instructions fournies précédemment. Une fois que vous êtes satisfait des modifications, vous pouvez supprimer l'ancien certificat de l'entrée.
  • Si cela ne vous dérange pas de perturber le trafic, vous pouvez supprimer l'ancienne ressource de l'entrée, provisionner une nouvelle ressource avec le même nom, mais avec un contenu différent, puis la réassocier à l'entrée.

Pour éviter de gérer vous-même la rotation des certificats, consultez la fonctionnalité Certificats SSL gérés par Google.

Dépannage

Si vous renseignez des résultats de certificats Secrets incorrects ou inexistants, une erreur d'événement Kubernetes est renvoyée. Vous pouvez consulter les événements Kubernetes pour un objet Entrée en procédant comme suit :

kubectl describe ingress

Le résultat ressemble à ceci :

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'

Étapes suivantes