Asegurarse de que los certificados de webhook son compatibles antes de actualizar a la versión 1.23

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

Para evitar que esto afecte a tus clústeres, debes sustituir los certificados incompatibles sin SANs por los back-ends de los webhooks y los servidores de la 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 SANs

GKE utiliza Kubernetes de código abierto, que usa el componente kube-apiserver para ponerse en contacto con los back-ends de tu webhook y del servidor de APIs agregadas mediante Seguridad en la capa de transporte (TLS). El componente kube-apiserver está escrito en el lenguaje de programación Go.

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

  1. Comprueba si el nombre DNS (o la dirección IP) del servidor figura como uno de los SANs del certificado del servidor.
  2. Como alternativa, comprueba si el nombre DNS (o la dirección IP) del servidor es igual al nombre común del certificado del servidor.

En el 2011, la RFC 6125 dejó de usar por completo la validación de la identidad del servidor basada en el campo CN. Los navegadores y otras aplicaciones críticas para la seguridad ya no usan este campo.

Para adaptarse al ecosistema de TLS en general, Go 1.15 ha eliminado el paso 2 de su proceso de validación, pero ha dejado un interruptor de depuración (x509ignoreCN=0) para habilitar el comportamiento antiguo y facilitar el proceso de migración. La versión 1.19 de Kubernetes fue la primera versión creada con Go 1.15. En los clústeres de GKE con versiones de 1.19 a 1.22, el interruptor de depuración estaba habilitado de forma predeterminada para dar a los clientes más tiempo para sustituir los certificados de los back-ends de webhook y de servidor de API agregados afectados.

Kubernetes versión 1.23 se ha creado con Go 1.17, que elimina 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 los webhooks o a los servicios de API agregados que no proporcionen un certificado X.509 válido con SAN adecuado.

Identificar los clústeres afectados

En el caso de los clústeres que ejecutan versiones de parche 1.21.9 o 1.22.3 (o posteriores)

En los clústeres con las versiones de parche 1.21.9 y 1.22.3 o posteriores con Cloud Logging habilitado, GKE proporciona un registro de auditoría de Cloud para identificar las llamadas a los back-ends 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 han llamado a back-ends 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.

A continuación, se muestra un ejemplo de entrada de registro de 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 (por ejemplo, example-webhook.default.svc) se incluyen como sufijos en los nombres de las etiquetas que empiezan por invalid-cert.webhook.gke.io/. También puedes obtener el nombre del clúster que ha hecho la llamada desde la etiqueta resource.labels.cluster_name, que tiene el valor example-cluster en este ejemplo.

Estadísticas de discontinuación

Puedes consultar qué clústeres usan certificados incompatibles en estadísticas de obsolescencia. Las estadísticas están disponibles en los clústeres que ejecutan la versión 1.22.6-gke.1000 o posterior.

Otras versiones del clúster

Si tienes un clúster con 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 la 1.21.9, tienes dos opciones para determinar si tu clúster se ve afectado por esta discontinuación:

Opción 1 (recomendada): Actualiza tu clúster a una versión de parche que admita la identificación de los certificados afectados con registros. Asegúrate de que Cloud Logging esté habilitado en tu clúster. Una vez que se haya actualizado el clúster, los registros de Registros de auditoría de Cloud identificativos se generarán 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 generarán cuando se intente hacer una llamada, te recomendamos que esperes 30 días después de una actualización para que haya tiempo suficiente para invocar todas las rutas de llamadas.

Se recomienda usar los registros para identificar los servicios afectados, ya que este enfoque minimiza el esfuerzo manual al generar automáticamente registros que muestran los servicios afectados.

Opción 2: Inspecciona los certificados que usan los webhooks o los servidores de APIs agregadas de tus clústeres para determinar si se ven afectados por no tener SANs:

  1. Obtén la lista de webhooks y servidores de APIs agregados de tu clúster e identifica sus back-ends (servicios o URLs).
  2. Inspecciona los certificados que usan los servicios backend.

Dado el esfuerzo manual que se requiere para inspeccionar todos los certificados de esta forma, este método solo se debe seguir si necesitas evaluar el impacto de las obsolescencias 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, deberías hacerlo primero y, después, seguir las instrucciones de la opción 1 para evitar el esfuerzo manual.

Identificar los servicios de backend que se van a inspeccionar

Para identificar los back-ends que pueden verse afectados por la discontinuación, obtén la lista de webhooks y servicios de API agregados, así como sus back-ends asociados en el clúster.

Para obtener una lista de todos los webhooks relevantes del clúster, utiliza los siguientes comandos kubectl:

kubectl get mutatingwebhookconfigurations -A   # mutating admission webhooks

kubectl get validatingwebhookconfigurations -A # validating admission webhooks

Para obtener un servicio o una URL de backend asociados a un webhook determinado, consulta el clientConfig.service campo o el webhooks.clientConfig.url campo de la configuración del webhook:

kubectl get mutatingwebhookconfigurations example-webhook -o yaml

El resultado debe ser 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 servicio de Kubernetes (clientConfig.service) o como una URL (clientConfig.url).

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

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

El resultado debe ser similar al siguiente:

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

Este ejemplo devuelve el servicio metric-server del espacio de nombres kube-system.

Para obtener un servicio asociado a una API agregada determinada, consulta el campo spec.service:

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

El resultado debe ser similar al siguiente:

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

Inspeccionar el certificado de un servicio

Una vez que hayas identificado los servicios backend relevantes que quieras 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 debe ser 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. Configurar una redirección de puertos

    desde tu kubectl localhost al pod.

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

    Sustituye lo siguiente en el comando:

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

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

    En este ejemplo de salida 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 ejemplo de salida se muestra un certificado al que le 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. Para quitar el reenvío de puertos de la ejecución en segundo plano, usa 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
    

Inspeccionar el certificado de un backend de URL

Si el webhook usa un url backend, conéctate directamente 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

Mitigar el riesgo de la actualización a la versión 1.23

Una vez que hayas identificado los clústeres afectados y sus servicios backend mediante certificados sin SANs, debes actualizar los webhooks y los backends del servidor de APIs agregadas para que usen certificados con SANs adecuados antes de actualizar los clústeres a la versión 1.23.

GKE no actualizará automáticamente los clústeres de las versiones 1.22.6-gke.1000 o posteriores con back-ends que usen certificados incompatibles hasta que sustituyas los certificados o hasta que la versión 1.22 llegue al final del periodo de asistencia estándar.

Si tu clúster tiene una versión de GKE anterior a 1.22.6-gke.1000, puedes evitar temporalmente las actualizaciones automáticas configurando una exclusión de mantenimiento para evitar las actualizaciones secundarias.

Recursos

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

  • Notas de la versión 1.23 de Kubernetes
    • Kubernetes se ha creado con Go 1.17. Esta versión de Go elimina la posibilidad de usar el ajuste de entorno GODEBUG=x509ignoreCN=0 para volver a habilitar el comportamiento antiguo obsoleto de tratar el CN de los certificados de servicio X.509 como un nombre de host.
  • Notas de las versiones Kubernetes 1.19 y Kubernetes 1.20
    • El comportamiento antiguo y obsoleto de tratar el campo CN de los certificados de servicio X.509 como un nombre de host cuando no hay SANs presentes ahora está inhabilitado de forma predeterminada.
  • Notas de la versión 1.17 de Go
    • Se ha quitado la marca temporal GODEBUG=x509ignoreCN=0.
  • Notas de la versión 1.15 de Go
    • El comportamiento antiguo y obsoleto de tratar el campo CN de los certificados X.509 como un host cuando no hay SANs presentes ahora está inhabilitado de forma predeterminada.
  • RFC 6125 (página 46)
    • Aunque el uso del valor CN es una práctica habitual, está obsoleto y se recomienda que las autoridades de certificación proporcionen valores subjectAltName.
  • Webhooks de admisión