Como garantir a compatibilidade dos certificados do webhook antes do upgrade para a v1.23


A partir da versão 1.23, o Kubernetes não é mais compatível com a validação de identidade do servidor usando o campo X.509 Common Name (CN) nos certificados. Em vez disso, o Kubernetes confiará apenas nas informações dos campos X.509 Subject Alternative Name (SAN).

Para evitar impacto nos clusters, substitua certificados incompatíveis sem SANs para back-ends de webhooks e servidores de API agregados antes de fazer upgrade dos clusters para a versão 1.23 do Kubernetes.

Por que o Kubernetes não é mais compatível com certificados de back-end sem SANs

O GKE opera o Kubernetes de código aberto, que usa o componente kube-apiserver para entrar em contato com o webhook e os back-ends de servidores de API agregados usando o 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 conectaram usando um processo de duas etapas:

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

A validação de identidade do servidor RFC 6125 baseada no campo CN foi totalmente suspensa em 2011. Navegadores e outros aplicativos essenciais para a segurança não usam mais o campo.

Para se alinhar ao ecossistema TLS mais amplo, o Go 1.15 removeu a Etapa 2 do processo de validação, mas deixou uma chave de depuração (x509ignoreCN=0) para permitir que o comportamento antigo facilite o processo de migração. A versão 1.19 do Kubernetes foi a primeira versão criada com o Go 1.15. Por padrão, os clusters do GKE nas versões 1.19 a 1.22 ativaram a chave de depuração para oferecer aos clientes mais tempo para substituir os certificados do webhook e dos back-ends de servidor de APIs agregados afetados.

O Kubernetes versão 1.23 foi criado com o Go 1.17, que remove a chave de depuração. Depois que o GKE fizer upgrade dos clusters para a versão 1.23, as chamadas não serão conectadas do plano de controle do cluster para webhooks ou serviços de API agregados que não forneçam um certificado X.509 válido com SAN apropriado.

Como 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 mais recentes com o Cloud Logging ativado, o GKE fornece um registro dos Registros de auditoria do Cloud para identificar chamadas para back-ends afetados de seu cluster. Use o filtro a seguir para pesquisar os registros:

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

Se os clusters não tiverem chamado back-ends com certificados afetados, você não verá nenhum registro. Se você vir um registro de auditoria desse tipo, ele incluirá o nome do host do back-end afetado.

Veja a seguir um exemplo da entrada de registro de um back-end de webhook hospedado por um serviço chamado example-webhook no namespace example-webhook:

{
  ...
  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 do host dos serviços afetados (por exemplo, example-webhook.default.svc) são incluídos como sufixos nos nomes dos rótulos que começam com invalid-cert.webhook.gke.io/. Também é possível receber o nome do cluster que fez a chamada do rótulo resource.labels.cluster_name, que tem o valor example-cluster neste exemplo.

Insights de descontinuação

Saiba quais clusters usam certificados incompatíveis em insights de descontinuação. Os insights estão disponíveis para clusters que executam a versão 1.22.6-gke.1000 ou mais recente.

Outras versões do cluster

Se você tiver um cluster em uma versão de patch anterior à 1.22 na versão secundária de 1.22 ou qualquer versão de patch anterior à 1.21.9, terá duas opções para determinar se o cluster foi afetado por essa suspensão de uso:

Opção 1 (recomendado): faça upgrade do cluster para uma versão de patch compatível com a identificação de certificados afetados com registros. Verifique se o Cloud Logging está ativado no cluster. Depois que o cluster for atualizado, os registros de auditoria do Cloud identificados serão produzidos sempre que o cluster tentar chamar um serviço que não forneça um certificado com um SAN adequado. Como os registros só serão produzidos em uma tentativa de chamada, recomendamos aguardar 30 dias após um upgrade para ter tempo suficiente para que todos os caminhos de chamada sejam invocados.

O uso de registros para identificar serviços afetados é recomendado porque essa abordagem minimiza o esforço manual, gerando registros automaticamente para mostrar os serviços afetados.

Opção 2: inspecione todos os certificados usados por webhooks ou serviços de API agregados nos clusters para determinar se eles foram afetados por não usar SANs:

  1. Veja a lista de webhooks e servidores de API agregados no cluster e identifique os back-ends deles (serviços ou URLs).
  2. Inspecione os certificados usados pelos serviços de back-end.

Considerando o esforço manual necessário para inspecionar todos os certificados dessa maneira, esse método só será seguido se você precisar avaliar o impacto das descontinuações no Kubernetes versão 1.23 antes de fazer upgrade do cluster para a versão 1.21. Se você puder fazer upgrade do cluster para a versão 1.21, faça primeiro o upgrade e, em seguida, siga as instruções da Opção 1 para evitar o esforço manual.

Como identificar serviços de back-end a serem inspecionados

Para identificar os back-ends que podem ser afetados pela suspensão de uso, receba a lista de webhooks e serviços de API agregados e os back-ends associados no cluster.

Para listar todos os webhooks relevantes no cluster, use os seguintes comandos kubectl:

kubectl get mutatingwebhookconfigurations -A   # mutating admission webhooks

kubectl get validatingwebhookconfigurations -A # validating admission webhooks

Para receber um URL ou serviço de back-end associado a um determinado webhook, examine o campo clientConfig.service ou o campo webhooks.clientConfig.url na configuração do webhook:

kubectl get mutatingwebhookconfigurations example-webhook -o yaml

A saída deste comando é semelhante a:

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

O clientConfig pode especificar o back-end como um Serviço do Kubernetes (clientConfig.service) ou um URL (clientConfig.url).

Para listar 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

A saída deste comando é semelhante a:

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

Este exemplo retorna o serviço metric-server a partir do namespace kube-system.

É possível receber um serviço associado para determinada API agregada examinando o campo spec.service:

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

A saída deste comando é semelhante a:

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

Como inspecionar o certificado de um serviço

Depois de identificar os serviços de back-end relevantes para inspeção, é possível 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
    

    A saída deste comando é semelhante a:

    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 que corresponda ao seletor:

    kubectl get pods --selector=run=nginx
    

    A saída deste comando é semelhante a:

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

    do seu localhost kubectl 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 ser detectado.
    • TARGET_PORT do TargetPort da etapa 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 uma SAN ausente:

    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 em 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
    

Como inspecionar o certificado de um back-end de URL

Se o webhook usar um back-end url, conecte-se diretamente ao nome do host 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

Como minimizar o risco de upgrade 1,23

Depois de identificar os clusters afetados e os serviços de back-end usando certificados sem SANs, você precisa atualizar os webhooks e os back-ends de servidores de API agregados para usar certificados com SANs apropriados antes de fazer upgrade dos clusters para a versão 1.23.

O GKE não fará upgrade automático dos clusters nas versões 1.22.6-gke.1000 ou posterior com back-ends que usam certificados incompatíveis até que você substitua os certificados ou até que a versão 1.22 alcance o fim do suporte padrão.

Se o cluster estiver em uma versão do GKE anterior a 1.22.6-gke.1000, é possível impedir temporariamente os upgrades automáticos configurando uma exclusão de manutenção para evitar upgrades secundários.

Recursos

Consulte os seguintes recursos para mais informações sobre essa alteração:

  • Notas de lançamento do Kubernetes 1.23
    • O Kubernetes foi criado usando o Go 1.17. Essa versão do Go remove a capacidade de usar uma configuração de ambiente GODEBUG=x509ignoreCN=0 para reativar o comportamento legado descontinuado de tratar o CN dos certificados de exibição de X.509 como um nome de host.
  • Notas de lançamento do Kubernetes 1.19 e do Kubernetes 1.20
    • O comportamento descontinuado do legado de tratar o campo CN nos certificados de exibição de X.509 como um nome de host quando não houver SANs está desativado por padrão.
  • Notas de lançamento do Go 1.17
    • A sinalização GODEBUG=x509ignoreCN=0 temporária foi removida.
  • Notas de lançamento do Go 1.15
    • O comportamento legado descontinuado de tratar o campo CN nos certificados X.509 como um host quando não há SANs está desativado por padrão.
  • RFC 6125 (página 46)
    • Embora o uso do valor de CN seja uma prática existente, ele está descontinuado. As autoridades de certificação são incentivadas a fornecer valores subjectAltName.
  • Webhooks de admissão