Assurer la compatibilité des certificats de webhook avant la mise à niveau vers la version 1.23


À partir de la version 1.23, Kubernetes n'accepte plus la validation de l'identité du serveur à l'aide du champ X.509 Common Name (CN) des certificats. À la place, Kubernetes ne s'appuiera que sur les informations du champ X.509 Subject Alternative Name (SAN).

Pour éviter tout impact sur vos clusters, vous devez remplacer les certificats incompatibles sans SAN pour les backends de webhooks et de serveurs d'API agrégés avant de mettre à niveau vos clusters vers la version 1.23 de Kubernetes.

Pourquoi Kubernetes n'accepte plus les certificats de backend sans SAN ?

GKE utilise Kubernetes Open Source, qui utilise le composant kube-apiserver pour contacter votre webhook et les backends de serveur d'API agrégés à l'aide de TLS (Transport Layer Security). Le composant kube-apiserver est écrit dans le langage de programmation Go.

Avant la version 1.15 de Go, les clients TLS validaient l'identité des serveurs auxquels ils étaient connectés à l'aide d'un processus en deux étapes :

  1. Ils vérifiaient si le nom DNS (ou l'adresse IP) du serveur était présent en tant que l'un des SAN sur le certificat du serveur.
  2. Comme alternative, ils vérifiaient si le nom DNS (ou l'adresse IP) du serveur était égal au CN sur le certificat du serveur.

Dans le cadre de la norme RFC 6125, la validation d'identité du serveur basée sur le champ CN est devenue entièrement obsolète en 2011. Les navigateurs et autres applications stratégiques pour la sécurité n'utilisent plus le champ.

Pour respecter l'écosystème TLS plus large, l'étape 2 a été supprimée du processus de validation dans la version 1.15 de Go. Cependant, le commutateur de débogage (x509ignoreCN=0) a été conservé pour permettre à l'ancien comportement de faciliter le processus de migration. La version 1.19 de Kubernetes est la première version créée à l'aide de Go 1.15. Les clusters GKE sur les versions 1.19 à 1.22 ont activé le commutateur de débogage par défaut afin de donner aux clients plus de temps pour remplacer les certificats des backends de webhooks et de serveurs d'API agrégés concernés.

La version 1.23 de Kubernetes a été créée à l'aide de la version 1.17 de Go, qui supprime le commutateur de débogage. Une fois que GKE met à jour vos clusters vers la version 1.23, les appels ne parviennent pas à se connecter, depuis le plan de contrôle de votre cluster, aux webhooks ou aux services d'API agrégés qui ne fournissent pas de certificat X.509 valide avec le SAN approprié.

Identifier les clusters concernés

Pour les clusters exécutant au moins des versions de correctif 1.21.9 ou 1.22.3

Pour les clusters utilisant les versions de correctifs 1.21.9 et 1.22.3 (ou version ultérieure) avec Cloud Logging activé, GKE fournit un journal Cloud Audit Logs pour identifier les appels aux backends concernés de votre cluster. Vous pouvez utiliser le filtre suivant pour rechercher les journaux :

logName =~ "projects/.*/logs/cloudaudit.googleapis.com%2Factivity"
resource.type = "k8s_cluster"
operation.producer = "k8s.io"
"invalid-cert.webhook.gke.io"

Si vos clusters n'ont pas appelé de backends avec des certificats concernés, aucun journal ne s'affiche. Si un tel journal d'audit s'affiche, il inclut le nom d'hôte du backend concerné.

Voici un exemple d'entrée de journal, pour un backend de webhook hébergé par un service nommé example-webhook dans l'espace de noms default :

{
  ...
  resource {
    type: "k8s_cluster",
    "labels": {
      "location": "us-central1-c",
      "cluster_name": "example-cluster",
      "project_id": "example-project"
    }
  },
  labels: {
    invalid-cert.webhook.gke.io/example-webhook.default.svc: "No subjectAltNames returned from example-webhook.default.svc:8443",
    ...
  },
  logName: "projects/example-project/logs/cloudaudit.googleapis.com%2Factivity",
  operation: {
    ...
    producer: "k8s.io",
    ...
  },
  ...
}

Les noms d'hôte des services concernés (par exemple example-webhook.default.svc) sont inclus en tant que suffixes dans les noms de libellés commençant par invalid-cert.webhook.gke.io/. Vous pouvez également obtenir le nom du cluster qui a effectué l'appel à partir du libellé resource.labels.cluster_name, qui possède la valeur example-cluster dans cet exemple.

Insights sur les abandons

Les insights d'abandon vous permettent d'identifier les clusters qui utilisent des certificats incompatibles. Les insights sont disponibles pour les clusters exécutant la version 1.22.6-gke.1000 ou ultérieure.

Autres versions de cluster

Si vous disposez d'un cluster sur une version de correctif antérieure à 1.22.3 sur la version mineure 1.22 ou sur une version de correctif antérieure à 1.21.9, vous avez deux options pour déterminer si votre cluster est affecté par cet abandon :

Option 1 (recommandée) : mettez à niveau votre cluster vers une version de correctif permettant d'identifier les certificats concernés à l'aide de journaux. Assurez-vous que Cloud Logging est activé pour votre cluster. Une fois votre cluster mis à niveau, les journaux Cloud Audit Logs d'identification sont générés chaque fois que le cluster tente d'appeler un service qui ne fournit pas de certificat associé à un SAN approprié. Comme les journaux ne sont générés que lors d'une tentative d'appel, nous vous recommandons d'attendre 30 jours après une mise à niveau afin de laisser suffisamment de temps pour que tous les chemins d'appel soient appelés.

Il est recommandé d'utiliser des journaux pour identifier les services concernés, car cette approche réduit les efforts manuels en générant automatiquement des journaux pour afficher les services concernés.

Option 2 : inspectez tous les certificats utilisés par les webhooks ou les services d'API agrégés dans vos clusters pour déterminer s'ils sont affectés par l'absence de SAN :

  1. Obtenez la liste des webhooks et des serveurs d'API agrégés dans votre cluster, puis identifiez leurs backends (services ou URL).
  2. Inspectez les certificats utilisés par les services.

Compte tenu des efforts manuels nécessaires pour inspecter tous les certificats de cette manière, cette méthode ne doit être suivie que si vous devez évaluer l'impact des abandons dans la version 1.23 de Kubernetes avant de mettre à niveau votre cluster vers la version 1.21. Si vous pouvez mettre à niveau votre cluster vers la version 1.21, vous devez d'abord le mettre à niveau, puis suivre les instructions de l'option 1 pour éviter les efforts manuels.

Identifier les services de backend à inspecter

Pour identifier les backends susceptibles d'être concernés par l'abandon, obtenez la liste des webhooks et des services d'API agrégés, ainsi que les backends associés dans le cluster.

Pour répertorier tous les webhooks pertinents dans le cluster, utilisez les commandes kubectl suivantes :

kubectl get mutatingwebhookconfigurations -A   # mutating admission webhooks

kubectl get validatingwebhookconfigurations -A # validating admission webhooks

Vous pouvez obtenir un service de backend ou une URL associés pour un webhook donné en examinant le champ clientConfig.service ou le champ webhooks.clientConfig.url dans la configuration du webhook :

kubectl get mutatingwebhookconfigurations example-webhook -o yaml

La sortie de la commande ressemble à ceci :

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- admissionReviewVersions:
  clientConfig:
    service:
        name: example-service
        namespace: default
        port: 443

Notez que clientConfig peut spécifier son backend en tant que service Kubernetes (clientConfig.service) ou en tant qu'URL (clientConfig.url).

Pour répertorier tous les services d'API agrégés pertinents dans le cluster, utilisez la commande kubectl suivante :

kubectl get apiservices -A |grep -v Local      # aggregated API services

La sortie de la commande ressemble à ceci :

NAME                     SERVICE                      AVAILABLE   AGE
v1beta1.metrics.k8s.io   kube-system/metrics-server   True        237d

Cet exemple renvoie le service metric-server à partir de l'espace de noms kube-system.

Vous pouvez obtenir un service associé pour une API agrégée donnée en examinant le champ spec.service :

kubectl get apiservices v1beta1.metrics.k8s.io -o yaml

La sortie de la commande ressemble à ceci :

...
apiVersion: apiregistration.k8s.io/v1
kind: APIService
spec:
  service:
    name: metrics-server
    namespace: kube-system
    port: 443

Inspecter le certificat d'un service

Une fois que vous avez identifié les services de backend pertinents à inspecter, vous pouvez inspecter le certificat de chaque service, tel que example-service :

  1. Recherchez le sélecteur et le port cible du service :

    kubectl describe service example-service
    

    La sortie de la commande ressemble à ceci :

    Name: example-service
    Namespace: default
    Labels: run=nginx
    Selector: run=nginx
    Type: ClusterIP
    IP: 172.21.xxx.xxx
    Port: 443
    TargetPort: 444
    

    Dans cet exemple, example-service dispose du sélecteur run=nginx et du port cible 444.

  2. Recherchez un pod correspondant au sélecteur :

    kubectl get pods --selector=run=nginx
    

    La sortie de la commande ressemble à ceci :

    NAME          READY   STATUS    RESTARTS   AGE
    example-pod   1/1     Running   0          21m
    
  3. Configurez un transfert de port

    de votre localhost kubectl au pod.

    kubectl port-forward pods/example-pod LOCALHOST_PORT:TARGET_PORT # port forwarding in background
    

    Remplacez les éléments suivants dans la commande :

    • LOCALHOST_PORT : adresse à écouter.
    • TARGET_PORT : port cible (TargetPort) de l'étape 1.
  4. Utilisez openssl pour imprimer le certificat utilisé par le service :

    openssl s_client -connect localhost:LOCALHOST_PORT </dev/null | openssl x509 -noout -text
    

    Cet exemple de résultat montre un certificat valide (avec des entrées SAN) :

    Subject: CN = example-service.default.svc
    X509v3 extensions:
      X509v3 Subject Alternative Name:
        DNS:example-service.default.svc
    

    Cet exemple de résultat montre un certificat avec un SAN manquant :

    Subject: CN = example-service.default.svc
      X509v3 extensions:
          X509v3 Key Usage: critical
              Digital Signature, Key Encipherment
          X509v3 Extended Key Usage:
              TLS Web Server Authentication
          X509v3 Authority Key Identifier:
              keyid:1A:5F:29:D8:E9:3C:54:3C:35:CC:D8:AB:D1:21:FD:C3:56:25:C0:74
    
  5. Empêchez le transfert de port de s'exécuter en arrière-plan à l'aide des commandes suivantes :

    $ jobs
    [1]+  Running                 kubectl port-forward pods/example-pod 8888:444 &
    $ kill %1
    [1]+  Terminated              kubectl port-forward pods/example 8888:444
    

Inspecter le certificat d'un backend d'URL

Si le webhook utilise un backend url, connectez-vous directement au nom d'hôte spécifié dans l'URL. Par exemple, si l'URL est https://example.com:123/foo/bar, utilisez la commande openssl suivante pour imprimer le certificat utilisé par le backend :

  openssl s_client -connect example.com:123 </dev/null | openssl x509 -noout -text

Atténuer le risque de mise à niveau vers la version 1.23

Une fois que vous avez identifié les clusters et les services de backend associés qui utilisent des certificats sans SAN, vous devez mettre à jour les webhooks et les backends de serveurs d'API agrégés pour utiliser des certificats avec les SAN appropriés avant de mettre à niveau les clusters vers la version 1.23.

GKE ne met pas automatiquement à niveau les clusters sur les versions 1.22.6-gke.1000 ou ultérieures avec des backends utilisant des certificats incompatibles tant que vous ne remplacez pas les certificats ou tant que la version 1.22 n'est pas en fin d'assistance standard.

Si votre cluster utilise une version de GKE antérieure à 1.22.6-gke.1000, vous pouvez temporairement empêcher les mises à niveau automatiques en configurant une exclusion de maintenance pour éviter les mises à niveau mineures.

Ressources

Pour en savoir plus sur ce changement, consultez les ressources suivantes :

  • Notes de version de Kubernetes 1.23
    • Kubernetes est créé à l'aide de Go 1.17. Cette version de Go empêche d'utiliser un paramètre d'environnement GODEBUG=x509ignoreCN=0 pour réactiver l'ancien comportement obsolète consistant à traiter le CN des certificats de diffusion X.509 comme un nom d'hôte.
  • Notes de version de Kubernetes 1.19 et Kubernetes 1.20
    • L'ancien comportement obsolète consistant à traiter le champ CN des certificats de diffusion X.509 comme un nom d'hôte lorsqu'aucun SAN n'est présent est désormais désactivé par défaut.
  • Notes de version de Go 1.17
    • L'option temporaire GODEBUG=x509ignoreCN=0 a été supprimée.
  • Notes de version de Go 1.15
    • L'ancien comportement obsolète consistant à traiter le champ CN des certificats X.509 comme hôte lorsqu'aucun SAN n'est présent est désormais désactivé par défaut.
  • RFC 6125 (page 46)
    • Bien que l'utilisation de la valeur CN soit une pratique existante, elle est obsolète, et les autorités de certification sont encouragées à fournir des valeurs subjectAltName à la place.
  • Webhooks d'admission