Kompatibilität mit Webhook-Zertifikaten vor einem Upgrade auf Version 1.23 gewährleisten


Ab Version 1.23 unterstützt Kubernetes nicht mehr die Validierung der Serveridentität mithilfe des X.509-CN-Felds (Common Name) in Zertifikaten. Stattdessen verwendet Kubernetes nur Informationen in den X.509-SAN-Feldern (Subject Alternative Name).

Um Auswirkungen auf Ihre Cluster zu vermeiden, müssen Sie inkompatible Zertifikate ohne SANs für Back-Ends von Webhooks und aggregierten API-Servern ersetzen, bevor Sie ein Upgrade Ihrer Cluster auf Kubernetes Version 1.23 durchführen.

Warum Kubernetes Backend-Zertifikate ohne SANs nicht mehr unterstützt

GKE basiert auf dem Open-Source-Kubernetes-System, das die kube-apiserver-Komponente verwendet, um Ihren Webhook und aggregierte API-Server-Back-Ends über Transport Layer Security (TLS) zu kontaktieren. Die kube-apiserver-Komponente ist in der Programmiersprache Go geschrieben.

In Versionen vor Go 1.15 validierten TLS-Clients die Identität der Server, zu denen sie eine Verbindung herstellten, in zwei Schritten:

  1. Prüfen, ob der DNS-Name (oder die IP-Adresse) des Servers als einer der SANs auf dem Serverzertifikat vorhanden ist.
  2. Alternativ prüfen, ob der DNS-Name (oder die IP-Adresse) des Servers mit dem CN im Serverzertifikat übereinstimmt.

RFC 6125 hat die Validierung der Serveridentität gemäß dem CN-Feld 2011 vollständig eingestellt. Browser und andere sicherheitsrelevante Anwendungen verwenden dieses Feld nicht mehr.

Zur Anpassung an die gesamte TLS-Umgebung hat Go 1.15 Schritt 2 aus seinem Validierungsprozess entfernt, jedoch einen Debug-Switch (x509ignoreCN=0) übrig gelassen, um das alte Verhalten zu aktivieren und den Migrationsprozess zu vereinfachen. Kubernetes Version 1.19 war die erste Version, die mit Go 1.15 erstellt wurde. In GKE-Clustern für Versionen von 1.19 bis 1.22 wurde standardmäßig der Debug-Switch aktiviert, um Kunden mehr Zeit zum Ersetzen der Zertifikate für die betroffenen Webhooks und aggregierten API-Server-Back-Ends zu geben.

Die Kubernetes-Version 1.23 basiert auf Go 1.17, womit der Debugging-Switch entfernt wurde. Nachdem GKE Ihre Cluster auf Version 1.23 aktualisiert hat, können Aufrufe von der Steuerungsebene des Clusters nicht mit Webhooks oder aggregierten API-Diensten verbunden werden, die kein gültiges X.509-Zertifikat mit einem entsprechenden SAN bereitstellen.

Betroffene Cluster bestimmen

Für Cluster mit Patchversionen mindestens 1.21.9 oder 1.22.3

Für Cluster mit Patchversionen 1.21.9 und 1.22.3 oder höher mit Cloud LoggingAktiviert bietet GKE einen Log für Cloud-Audit-Logs, um Aufrufe von betroffenen Back-Ends aus Ihrem Cluster zu identifizieren. Mit dem folgenden Filter können Sie nach den Logs suchen:

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

Wenn Ihre Cluster keine Back-Ends mit betroffenen Zertifikaten aufgerufen haben, werden keine Logs angezeigt. Wenn Sie ein solches Audit-Log sehen, enthält es den Hostnamen des betroffenen Back-Ends.

Das folgende Beispiel zeigt den Logeintrag für ein Webhook-Backend, das von einem Dienst namens example-webhook im Namespace example-webhook gehostet wird:

{
  ...
  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",
    ...
  },
  ...
}

Die Hostnamen der betroffenen Dienste (z. B. example-webhook.default.svc) sind als Suffixe in den Labelnamen enthalten, die mit invalid-cert.webhook.gke.io/ beginnen. Sie können auch den Namen des Clusters, der den Aufruf ausgeführt hat, über das Label resource.labels.cluster_name abrufen, das in diesem Beispiel den Wert example-cluster hat.

Informationen zur Einstellung

Informationen dazu, welche Cluster inkompatible Zertifikate verwenden, finden Sie in den Informationen zur Einstellung. Informationen sind für Cluster verfügbar, in denen Version 1.22.6-gke.1000 oder höher ausgeführt wird.

Weitere Clusterversionen

Wenn Ihr Cluster in einer Patchversion vor 1.22.3 in der Nebenversion 1.22 oder in einer beliebigen Patchversion vor 1.21.9 ausgeführt wird, haben Sie zwei Möglichkeiten, um festzustellen, ob Ihr Cluster von dieser Einstellung betroffen ist:

Option 1 (empfohlen): Aktualisieren Sie den Cluster auf eine Patchversion, mit der betroffene Zertifikate mit Logs identifiziert werden können. Achten Sie darauf, dass Cloud Logging für Ihren Cluster aktiviert ist. Nach dem Upgrade Ihres Clusters werden die identifizierenden Cloud-Audit-Logs jedes Mal erstellt, wenn der Cluster versucht, einen Dienst aufzurufen, der kein Zertifikat mit einem geeigneten SAN bereitstellt. Da die Logs nur bei einem Aufrufversuch erstellt werden, sollten Sie nach einem Upgrade 30 Tage warten, damit genügend Zeit vorhanden war, um alle Aufrufpfade aufzurufen.

Das Identifizieren von betroffenen Diensten mithilfe von Logs wird empfohlen, da automatisch Logs zu den betroffenen Diensten erstellt werden. Auf diese Weise wird der manuelle Aufwand reduziert.

Option 2: Prüfen Sie alle Zertifikate, die von Webhooks oder aggregierten API-Diensten in Ihren Clustern verwendet werden, um festzustellen, ob sie aufgrund von fehlenden SANs betroffen sind:

  1. Rufen Sie die Liste der Webhooks und aggregierten API-Server im Cluster ab und identifizieren Sie deren Back-Ends (Dienste oder URLs).
  2. Prüfen Sie die Zertifikate, die von den Backend-Diensten verwendet werden.

Angesichts des manuellen Aufwands, der mit der Prüfung aller Zertifikate auf diese Weise verbunden ist, sollte diese Methode nur verwendet werden, wenn Sie die Auswirkungen der verworfenen Funktionen in Kubernetes Version 1.23 vor dem Upgrade des Clusters auf Version 1.21 bewerten müssen. Wenn Sie Ihren Cluster auf Version 1.21 aktualisieren können, sollten Sie zuerst ein Upgrade ausführen. Folgen Sie dann der Anleitung in Option 1, um den manuellen Aufwand zu reduzieren.

Zu prüfende Backend-Dienste bestimmen

Um Back-Ends zu bestimmen, die von der Einstellung betroffen sein können, rufen Sie die Liste der Webhooks und aggregierten API-Dienste und ihre zugehörigen Back-Ends im Cluster ab.

Verwenden Sie die folgenden kubectl-Befehle, um alle relevanten Webhooks im Cluster aufzulisten:

kubectl get mutatingwebhookconfigurations -A   # mutating admission webhooks

kubectl get validatingwebhookconfigurations -A # validating admission webhooks

Sie können einen verknüpften Backend-Dienst oder eine zugehörige URL für einen bestimmten Webhook abrufen, indem Sie das Feld clientConfig.service oder das Feld webhooks.clientConfig.url in der Konfiguration des Webhooks untersuchen.

kubectl get mutatingwebhookconfigurations example-webhook -o yaml

Die Ausgabe dieses Befehls sieht so aus:

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

Beachten Sie, dass clientConfig sein Backend als Kubernetes-Dienst (clientConfig.service) oder als URL (clientConfig.url) angeben kann.

Mit dem folgenden kubectl-Befehl können Sie alle relevanten aggregierten API-Dienste im Cluster auflisten:

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

Die Ausgabe dieses Befehls sieht so aus:

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

In diesem Beispiel wird der metric-server-Dienst aus dem Namespace kube-system zurückgegeben.

Anhand des Felds spec.service können Sie einen verknüpften Dienst für eine bestimmte aggregierte API abrufen:

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

Die Ausgabe dieses Befehls sieht so aus:

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

Zertifikat eines Dienstes prüfen

Nachdem Sie die entsprechenden Backend-Dienste bestimmt haben, können Sie das Zertifikat jedes einzelnen Dienstes prüfen, z. B. example-service:

  1. Suchen Sie den Selektor und den Zielport des Dienstes:

    kubectl describe service example-service
    

    Die Ausgabe dieses Befehls sieht so aus:

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

    In diesem Beispiel hat example-service den Selektor run=nginx und den Zielport 444.

  2. Suchen Sie einen Pod, der dem Selektor entspricht:

    kubectl get pods --selector=run=nginx
    

    Die Ausgabe dieses Befehls sieht in etwa so aus:

    NAME          READY   STATUS    RESTARTS   AGE
    example-pod   1/1     Running   0          21m
    
  3. Richten Sie eine Portweiterleitung ein.

    vom kubectl-localhost zum Pod.

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

    Ersetzen Sie im Befehl Folgendes:

    • LOCALHOST_PORT: die zu überwachende Adresse.
    • TARGET_PORT: TargetPort aus Schritt 1.
  4. Verwenden Sie openssl, um das vom Dienst verwendete Zertifikat auszugeben:

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

    Diese Beispielausgabe zeigt ein gültiges Zertifikat (mit SAN-Einträgen):

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

    Diese Beispielausgabe zeigt ein Zertifikat mit einem fehlenden SAN:

    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. Entfernen Sie die Portweiterleitung mit den folgenden Befehlen, damit sie nicht im Hintergrund ausgeführt wird:

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

Zertifikat eines URL-Back-Ends prüfen

Wenn der Webhook ein url-Backend verwendet, stellen Sie eine direkte Verbindung zum in der URL angegebenen Hostnamen her. Wenn die URL beispielsweise https://example.com:123/foo/bar lautet, geben Sie den folgenden openssl-Befehl aus, um das vom Backend verwendete Zertifikat auszugeben:

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

Risiko eines 1.23-Upgrades verringern

Wenn Sie die betroffenen Cluster und deren Backend-Dienste mithilfe von Zertifikaten ohne SANs bestimmt haben, müssen Sie die Webhooks und aggregierten API-Server-Backends aktualisieren, um Zertifikate mit entsprechenden SANs vor dem Upgrade der Cluster auf Version 1.23 zu verwenden.

GKE aktualisiert Cluster auf die Version 1.22.6-gke.1000 oder höher nicht automatisch mit Back-Ends, die inkompatible Zertifikate verwenden, bis Sie die Zertifikate ersetzen oder bisVersion 1.22 das Standardsupport-Ende erreicht hat.

Wenn Ihr Cluster eine GKE-Version vor 1.22.6-gke.1000 verwendet, können Sie automatische Upgrades vorübergehend verhindern, indem Sie einen Wartungsausschluss konfigurieren, um geringfügige Upgrades zu verhindern.

Ressourcen

Weitere Informationen zu dieser Änderung finden Sie in den folgenden Ressourcen:

  • Versionshinweise zu Kubernetes 1.23
    • Kubernetes basiert auf Go 1.17. Mit dieser Version von Go ist es nicht mehr möglich, eine GODEBUG=x509ignoreCN=0-Umgebungseinstellung zu verwenden, um das verworfene Legacy-Verhalten für den Umgang mit dem CN-Feld für X.509-Bereitstellungszertifikate als Hostnamen wieder zu aktivieren.
  • Versionshinweise zu Kubernetes 1.19 und Kubernetes 1.20
    • Das verworfene Legacy-Verhalten für den Umgang mit dem CN-Feld in X.509-Zertifikaten als Host, wenn keine SANs vorhanden sind, ist jetzt standardmäßig deaktiviert.
  • Go 1.17-Versionshinweise
    • Das temporäre Flag GODEBUG=x509ignoreCN=0 wurde entfernt.
  • Go 1.15-Versionshinweise
    • Das verworfene Legacy-Verhalten für den Umgang mit dem CN-Feld in X.509-Zertifikaten als Host, wenn keine SANs vorhanden sind, ist jetzt standardmäßig deaktiviert.
  • RFC 6125 (Seite 46)
    • Die Verwendung des CN-Werts ist zwar üblich, wurde aber verworfen. Zertifizierungsstellen sollten stattdessen subjectAltName-Werte angeben.
  • Zulassungs-Webhooks