Garantir a compatibilidade dos certificados de webhook antes da atualização para a versão 1.23

A partir da versão 1.23, o Kubernetes já não suporta a validação da identidade do servidor através do campo Nome comum (CN) X.509 nos certificados. Em alternativa, o Kubernetes baseia-se apenas nas informações nos campos de nome alternativo do assunto (SAN) X.509.

Para evitar o impacto nos seus clusters, tem de substituir os certificados incompatíveis sem SANs para back-ends de webhooks e servidores de API agregados antes de atualizar os seus clusters para a versão 1.23 do Kubernetes.

Por que motivo o Kubernetes já não suporta certificados de back-end sem SANs

O GKE opera o Kubernetes de código aberto, que usa o componente kube-apiserver para contactar os back-ends do servidor de API agregado e webhook através do Transport Layer Security (TLS). O componente kube-apiserver é escrito na linguagem de programação Go.

Antes do Go 1.15, os clientes TLS validavam a identidade dos servidores aos quais se ligavam através de um processo de dois passos:

  1. Verifique se o nome DNS (ou o endereço IP) do servidor está presente como um dos SANs no certificado do servidor.
  2. Como alternativa, verifique se o nome DNS (ou o endereço IP) do servidor é igual ao CN no certificado do servidor.

A RFC 6125 descontinuou totalmente a validação da identidade do servidor com base no campo CN em 2011. Os navegadores e outras aplicações críticas para a segurança já não usam o campo.

Para se alinhar com o ecossistema TLS mais amplo, o Go 1.15 removeu o passo 2 do respetivo processo de validação, mas deixou um comutador de depuração (x509ignoreCN=0) para ativar o comportamento antigo para facilitar o processo de migração. A versão 1.19 do Kubernetes foi a primeira versão criada com o Go 1.15. Os clusters do GKE nas versões 1.19 a 1.22 ativaram o interruptor de depuração por predefinição para dar aos clientes mais tempo para substituir os certificados do webhook afetado e dos back-ends do servidor da API agregada.

O Kubernetes versão 1.23 é criado com o Go 1.17, que remove o interruptor de depuração. Assim que o GKE atualizar os seus clusters para a versão 1.23, as chamadas não vão conseguir estabelecer ligação do plano de controlo do seu cluster a webhooks ou serviços de API agregados que não forneçam um certificado X.509 válido com SAN adequado.

Identificar clusters afetados

Para clusters que executam versões de patch, pelo menos, 1.21.9 ou 1.22.3

Para clusters nas versões de patch 1.21.9 e 1.22.3 ou posteriores com o Cloud Logging ativado, o GKE fornece um registo de registos de auditoria da nuvem para identificar chamadas para back-ends afetados a partir do seu cluster. Pode usar o seguinte filtro para pesquisar os registos:

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

Se os seus clusters não tiverem chamado back-ends com certificados afetados, não vê registos. Se vir um registo de auditoria deste tipo, este vai incluir o nome do anfitrião do back-end afetado.

Segue-se um exemplo da entrada do registo para um back-end de webhook alojado por um serviço denominado example-webhook no espaço de nomes 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",
    ...
  },
  ...
}

Os nomes de anfitrião dos serviços afetados (por exemplo, example-webhook.default.svc) são incluídos como sufixos nos nomes das etiquetas que começam por invalid-cert.webhook.gke.io/. Também pode obter o nome do cluster que fez a chamada a partir da etiqueta resource.labels.cluster_name, que tem o valor example-cluster neste exemplo.

Estatísticas de descontinuação

Pode saber que clusters usam certificados incompatíveis nas estatísticas de descontinuação. As estatísticas estão disponíveis para clusters com a versão 1.22.6-gke.1000 ou posterior.

Outras versões do cluster

Se tiver um cluster numa versão de patch anterior a 1.22.3 na versão secundária 1.22 ou numa versão de patch anterior a 1.21.9, tem duas opções para determinar se o seu cluster é afetado por esta descontinuação:

Opção 1 (recomendada): Atualize o seu cluster para uma versão de patch que suporte a identificação de certificados afetados com registos. Certifique-se de que o Cloud Logging está ativado para o seu cluster. Após a atualização do cluster, os registos de identificação do Cloud Audit Logs são produzidos sempre que o cluster tenta chamar um serviço que não faculta um certificado com um SAN adequado. Como os registos só são gerados numa tentativa de chamada, recomendamos que aguarde 30 dias após uma atualização para dar tempo suficiente para que todos os caminhos de chamadas sejam invocados.

Recomendamos a utilização de registos para identificar os serviços afetados, uma vez que esta abordagem minimiza o esforço manual através da produção automática de registos para mostrar os serviços afetados.

Opção 2: inspecione os certificados usados pelos webhooks ou pelos servidores da API agregada nos seus clusters para determinar se são afetados por não terem SANs:

  1. Obtenha a lista de webhooks e servidores de API agregados no seu cluster e identifique os respetivos backends (serviços ou URLs).
  2. Inspeção dos certificados usados pelos serviços de back-end.

Dado o esforço manual necessário para inspecionar todos os certificados desta forma, este método só deve ser seguido se precisar de avaliar o impacto das descontinuações na versão 1.23 do Kubernetes antes de atualizar o cluster para a versão 1.21. Se puder atualizar o cluster para a versão 1.21, deve atualizá-lo primeiro e, em seguida, seguir as instruções na Opção 1 para evitar o esforço manual.

Identificar serviços de back-end a inspecionar

Para identificar os back-ends que podem ser afetados pela descontinuação, obtenha a lista de Webhooks e serviços de API agregados, bem como os respetivos back-ends associados no cluster.

Para apresentar uma lista de todos os webhooks relevantes no cluster, use os seguintes kubectl comandos:

kubectl get mutatingwebhookconfigurations -A   # mutating admission webhooks

kubectl get validatingwebhookconfigurations -A # validating admission webhooks

Pode obter um serviço ou um URL de back-end associado para um determinado webhook examinando o campo clientConfig.service ou o campo webhooks.clientConfig.url na configuração do webhook:

kubectl get mutatingwebhookconfigurations example-webhook -o yaml

O resultado deste comando é semelhante ao seguinte:

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

Tenha em atenção que clientConfig pode especificar o respetivo back-end como um serviço Kubernetes (clientConfig.service) ou como um URL (clientConfig.url).

Para apresentar uma lista de todos os serviços de API agregados relevantes no cluster, use o seguinte comando kubectl:

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

O resultado deste comando é semelhante ao seguinte:

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

Este exemplo devolve metric-server Service do espaço de nomes kube-system.

Pode obter um serviço associado para uma determinada API agregada examinando o campo spec.service:

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

O resultado deste comando é semelhante ao seguinte:

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

Inspeção do certificado de um serviço

Depois de identificar os serviços de back-end relevantes para inspecionar, pode inspecionar o certificado de cada serviço específico, como example-service:

  1. Encontre o seletor e a porta de destino do serviço:

    kubectl describe service example-service
    

    O resultado deste comando é semelhante ao seguinte:

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

    Neste exemplo, example-service tem o seletor run=nginx e a porta de destino 444.

  2. Encontre um pod correspondente ao seletor:

    kubectl get pods --selector=run=nginx
    

    O resultado do comando é semelhante ao seguinte:

    NAME          READY   STATUS    RESTARTS   AGE
    example-pod   1/1     Running   0          21m
    
  3. Configure um encaminhamento de porta

    do seu kubectl localhost para o pod.

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

    Substitua o seguinte no comando:

    • LOCALHOST_PORT: o endereço a ouvir.
    • TARGET_PORT o TargetPort do passo 1.
  4. Use openssl para imprimir o certificado usado pelo Serviço:

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

    Este exemplo de saída mostra um certificado válido (com entradas SAN):

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

    Este exemplo de saída mostra um certificado com um SAN em falta:

    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. Remova o encaminhamento de portas da execução em segundo plano com os seguintes comandos:

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

Inspeção do certificado de um back-end de URL

Se o webhook usar um url backend, ligue-se diretamente ao nome de anfitrião especificado no URL. Por exemplo, se o URL for https://example.com:123/foo/bar, use o seguinte comando openssl para imprimir o certificado usado pelo back-end:

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

Mitigar o risco da atualização para a versão 1.23

Depois de identificar os clusters afetados e os respetivos serviços de back-end que usam certificados sem SANs, tem de atualizar os back-ends dos webhooks e do servidor de API agregado para usar certificados com SANs adequados antes de atualizar os clusters para a versão 1.23.

O GKE não atualiza automaticamente os clusters nas versões 1.22.6-gke.1000 ou posteriores com back-ends que usam certificados incompatíveis até que substitua os certificados ou até que a versão 1.22 atinja o fim do suporte padrão.

Se o seu cluster estiver numa versão do GKE anterior a 1.22.6-gke.1000, pode impedir temporariamente as atualizações automáticas configurando uma exclusão de manutenção para impedir atualizações secundárias.

Recursos

Consulte os seguintes recursos para ver informações adicionais sobre esta alteração:

  • Notas de lançamento do Kubernetes 1.23
    • O Kubernetes é criado com o Go 1.17. Esta versão do Go remove a capacidade de usar uma definição do ambiente GODEBUG=x509ignoreCN=0 para reativar o comportamento antigo descontinuado de tratar o CN dos certificados de serviço X.509 como um nome de anfitrião.
  • Notas de lançamento do Kubernetes 1.19 e Kubernetes 1.20
    • O comportamento antigo descontinuado de tratar o campo CN nos certificados de serviço X.509 como um nome de anfitrião quando não existem SANs está agora desativado por predefinição.
  • Notas de lançamento do Go 1.17
    • A indicação temporária GODEBUG=x509ignoreCN=0 foi removida.
  • Notas de lançamento do Go 1.15
    • O comportamento antigo e descontinuado de tratar o campo CN em certificados X.509 como um anfitrião quando não existem SANs está agora desativado por predefinição.
  • RFC 6125 (página 46)
    • Embora a utilização do valor CN seja uma prática existente, está descontinuada e as autoridades de certificação são incentivadas a fornecer valores subjectAltName.
  • Webhooks de admissão