Garantiza la compatibilidad de los certificados de webhook antes de actualizar a v1.23


A partir de la versión 1.23, Kubernetes ya no admite la validación de identidad del servidor mediante el campo Nombre común (CN) de X.509 en los certificados. En su lugar, Kubernetes solo se basará en la información en los campos de nombre alternativo (SAN) del sujeto X.509.

A fin de evitar el impacto en tus clústeres, debes reemplazar los certificados incompatibles sin SAN para backends de webhooks y servidores de API agregados antes de actualizar tus clústeres a la versión 1.23 de Kubernetes.

Por qué Kubernetes ya no admite certificados de backend sin SAN

GKE opera con Kubernetes de código abierto, que usa el componente kube-apiserver para comunicarse con los backends agregados del servidor de la API y el webhook mediante la seguridad de la capa de transporte (TLS). El componente kube-apiserver se escribe en el lenguaje de programación Go.

Antes de Go 1.15, los clientes de TLS validan la identidad de los servidores a los que se conectaron mediante un proceso de dos pasos:

  1. Verifica si el nombre de DNS (o la dirección IP) del servidor está presente como uno de los SAN en el certificado del servidor.
  2. Como resguardo, verifica si el nombre de DNS (o la dirección IP) del servidor es igual a la CN en el certificado del servidor.

RFC 6125 dejó completamente obsoleta la validación de identidad de servidor basada en el campo CN en 2011. Los navegadores y otras aplicaciones esenciales de seguridad ya no usan el campo.

Para alinearse con el ecosistema de TLS más amplio, Go 1.15 quitó el Paso 2 de su proceso de validación, pero dejó un interruptor de depuración (x509ignoreCN=0) para habilitar el comportamiento anterior a fin de facilitar el proceso de migración. La versión 1.19 de Kubernetes fue la primera versión compilada con Go 1.15. Los clústeres de GKE en las versiones de la 1.19 a la 1.22 habilitaron el interruptor de depuración de forma predeterminada a fin de proporcionar a los clientes más tiempo para reemplazar los certificados de los backends agregados del servidor de la API y el webhook afectados.

La versión 1.23 de Kubernetes se compila con Go 1.17, que quita el interruptor de depuración. Una vez que GKE actualice tus clústeres a la versión 1.23, las llamadas no se podrán conectar desde el plano de control de tu clúster a webhooks o servicios de API agregados que no proporcionen un certificado X.509 válido con un SAN apropiado.

Identifica los clústeres afectados

Para los clústeres que ejecutan versiones de parche 1.21.9 o 1.22.3 como mínimo

Para los clústeres con versiones de parche 1.21.9 y 1.22.3 o posteriores con Cloud Logging habilitado, GKE proporciona un registro de Registros de auditoría de Cloud para identificar las llamadas a los backends afectados desde tu clúster. Puedes usar el siguiente filtro para buscar los registros:

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

Si tus clústeres no llamaron a los backends con certificados afectados, no verás ningún registro. Si ves un registro de auditoría de este tipo, incluirá el nombre de host del backend afectado.

El siguiente es un ejemplo de la entrada de registro para un backend de webhook alojado por un servicio llamado example-webhook en el espacio de nombres 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",
    ...
  },
  ...
}

Los nombres de host de los servicios afectados (p. ej., example-webhook.default.svc) se incluyen como sufijos en los nombres de las etiquetas que comienzan con invalid-cert.webhook.gke.io/. También puedes obtener el nombre del clúster que realizó la llamada desde la etiqueta resource.labels.cluster_name, que tiene el valor example-cluster en este ejemplo.

Estadísticas de baja

Puedes obtener información sobre qué clústeres usan certificados incompatibles con las estadísticas de baja. Las estadísticas están disponibles para los clústeres que ejecutan la versión 1.22.6-gke.1000 o una posterior.

Otras versiones de clústeres

Si tienes un clúster en una versión de parche anterior a la 1.22.3 en la versión secundaria 1.22 o cualquier versión de parche anterior a 1.21.9, tienes dos opciones para determinar si tu clúster se ve afectado por esta baja:

Opción 1 (recomendado): Actualiza tu clúster a una versión de parche que admita la identificación de certificados afectados con registros.. Asegúrate de que Cloud Logging esté habilitado para tu clúster. Después de actualizar el clúster, se producirán los registros de auditoría de identificación de Cloud Audit Logging cada vez que el clúster intente llamar a un servicio que no proporcione un certificado con un SAN adecuado. Como los registros solo se producirán en un intento de llamada, recomendamos esperar 30 días después de una actualización para que haya tiempo suficiente para que se invoquen todas las rutas de llamada.

Se recomienda usar registros para identificar los servicios afectados, ya que este enfoque minimiza el esfuerzo manual mediante la producción automática de registros para mostrar los servicios afectados.

Opción 2: Inspecciona todos los certificados que usan los webhooks o los servicios agregados de la API en los clústeres para determinar si se ven afectados debido a que no tienen SAN:

  1. Obtén la lista de webhooks y servidores de la API agregados en tu clúster e identifica sus backends (Services o URL).
  2. Inspecciona los certificados que usan los servicios de backend.

Dado el esfuerzo manual que se necesita para inspeccionar todos los certificados de esta manera, este método solo debe seguirse si necesitas evaluar el impacto de las bajas en la versión 1.23 de Kubernetes antes de actualizar tu clúster a la versión 1.21. Si puedes actualizar tu clúster a la versión 1.21, primero debes actualizarlo y, luego, seguir las instrucciones de la Opción 1 para evitar el esfuerzo manual.

Identifica los servicios de backend que se inspeccionarán

Para identificar los backends que podrían verse afectados por la baja, obtén la lista de Webhooks y servicios de API agregados y sus backends asociados en el clúster.

Para obtener una lista de todos los webhooks relevantes en el clúster, usa los siguientes comandos kubectl:

kubectl get mutatingwebhookconfigurations -A   # mutating admission webhooks

kubectl get validatingwebhookconfigurations -A # validating admission webhooks

Puedes obtener un servicio de backend o una URL asociados para un webhook determinado si examinas el campo clientConfig.service o el campo webhooks.clientConfig.url en la configuración del webhook.

kubectl get mutatingwebhookconfigurations example-webhook -o yaml

El resultado de este comando es similar al siguiente:

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

Ten en cuenta que clientConfig puede especificar su backend como un Service de Kubernetes (clientConfig.service) o como una URL (clientConfig.url).

Para enumerar todos los servicios de API agregados relevantes en el clúster, usa el siguiente comando kubectl:

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

El resultado de este comando es similar al siguiente:

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

En este ejemplo, se muestra el servicio metric-server del espacio de nombres kube-system.

Puedes obtener un servicio asociado para una determinada API agregada si examinas el campo spec.service:

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

El resultado de este comando es similar al siguiente:

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

Inspecciona el certificado de un servicio

Una vez que identificaste servicios de backend relevantes para inspeccionar, puedes inspeccionar el certificado de cada servicio específico, como example-service:

  1. Busca el selector y el puerto de destino del servicio:

    kubectl describe service example-service
    

    El resultado de este comando es similar al siguiente:

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

    En este ejemplo, example-service tiene el selector run=nginx y el puerto de destino 444.

  2. Busca un pod que coincida con el selector:

    kubectl get pods --selector=run=nginx
    

    El resultado del comando es similar al siguiente:

    NAME          READY   STATUS    RESTARTS   AGE
    example-pod   1/1     Running   0          21m
    
  3. Configura una redirección de puertos

    desde tu localhost kubectl al Pod.

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

    Reemplaza lo siguiente en el comando:

    • LOCALHOST_PORT es la dirección en la que se escuchará.
    • TARGET_PORT es el TargetPort del paso 1.
  4. Usa openssl para imprimir el certificado que usa el Service:

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

    En este resultado de ejemplo, se muestra un certificado válido (con entradas SAN):

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

    En este resultado de ejemplo, se muestra un certificado en el que falta un 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. Quita el puerto de redirección para que se ejecute en segundo plano con los siguientes comandos:

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

Inspecciona el certificado de un backend de URL

Si el webhook usa un backend de url, conéctate de forma directa al nombre de host especificado en la URL. Por ejemplo, si la URL es https://example.com:123/foo/bar, usa el siguiente comando openssl para imprimir el certificado que usa el backend:

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

Mitiga el riesgo de la actualización 1.23

Una vez que identificaste los clústeres afectados y sus servicios de backend mediante certificados sin SAN, debes actualizar los webhooks y los backends del servidor de la API agregados para usar certificados con SAN adecuados antes de la actualización de los clústeres a la versión 1.23.

GKE no actualizará automáticamente los clústeres en las versiones 1.22.6-gke.1000 o posteriores con backends que usen certificados incompatibles hasta que reemplaces los certificados o hasta que la versión 1.22 alcance el final del ciclo de vida.

Si tu clúster se encuentra en una versión de GKE anterior a la 1.22.6-gke.1000, puedes evitar las actualizaciones automáticas de forma temporal si configuras una exclusión de mantenimiento para evitar actualizaciones menores.

Recursos

Consulta los siguientes recursos para obtener más información sobre este cambio:

  • Notas de la versión de Kubernetes 1.23
    • Kubernetes se compila con Go 1.17. Esta versión de Go quita la capacidad de usar una configuración de entorno GODEBUG=x509ignoreCN=0 para volver a habilitar el comportamiento heredado obsoleto de tratar el CN de certificados de entrega X.509 como un nombre de host.
  • Notas de la versión de Kubernetes 1.19 y Kubernetes 1.20
    • El comportamiento heredado y obsoleto de tratar el campo CN en los certificados de entrega X.509 como un nombre de host cuando no hay SAN ahora está inhabilitado de forma predeterminada.
  • Notas de la versión de Go 1.17
    • Se quitó la marca temporal GODEBUG=x509ignoreCN=0.
  • Notas de la versión de Go 1.15
    • El comportamiento heredado obsoleto de tratar el campo CN en los certificados X.509 como un host cuando no hay SAN ahora está inhabilitado de forma predeterminada.
  • RFC 6125 (página 46)
    • Aunque el uso del valor de CN es una práctica existente, está obsoleto y se recomienda a las autoridades certificadoras que proporcionen valores subjectAltName en su lugar.
  • Webhooks de admisión