← todos os artigos

cert-manager 1.21.2: por que resposta de ACME não devia ir parar no Events do seu cluster

0

Em 11 de setembro de 2026 o cert-manager lançou a v1.21.2, e em 16 de setembro a v1.20.4 (patch pra quem ainda está na linha 1.20). Nenhuma das duas vem com CVE ou advisory formal — mas a mudança central da 1.21.2 é o tipo de fix que vale entender mesmo sem selo de CVE, porque mexe com uma suposição que muita gente faz sobre RBAC no Kubernetes sem perceber: “Events são inofensivos, só Secrets precisa de cuidado”.

O que mudou

A partir da 1.21.2, o comportamento dos issuers ACME e Vault muda assim:

  • Corpo de resposta HTTP não vai mais pro status do Issuer/ClusterIssuer nem pros Events do cluster. Antes, quando o servidor ACME (ou o Vault, no caso do Vault issuer) respondia algo fora do esperado, o cert-manager copiava esse corpo de resposta pra dentro da condição de status do recurso e pra um Event — objetos que ficam visíveis via kubectl describe ou kubectl get events pra qualquer principal com permissão de leitura no namespace, que costuma ser bem mais gente do que quem pode ler um Secret.
  • Só o “problem document” do ACME (RFC 7807) é exposto, e com tamanho limitado. É o formato estruturado de erro que o protocolo ACME já prevê — não é resposta bruta do servidor. Qualquer outra coisa vira só o código de status HTTP no status do recurso; o corpo completo fica exclusivamente no log do controller.
  • Resposta do servidor ACME agora tem teto de 16 MiB. Sem esse limite, um servidor ACME malicioso ou comprometido (ou um MITM em rede sem TLS pinning correto) podia devolver uma resposta arbitrariamente grande, que o cert-manager tentava processar inteira — vetor de negação de serviço por exaustão de memória/CPU no controller.
  • No Vault issuer, o mesmo tratamento: respostas HTTP que não vêm do próprio Vault não populam mais status/Events com o corpo, e mensagens de erro do Vault são truncadas antes de ser armazenadas.

Junto veio uma leva de correções sem relação direta com isso — panic no webhook de validação quando o AdmissionReview vem sem campos opcionais, race condition no self-check do HTTP-01, panic no controller de issuing quando um CertificateRequest tem timestamp de falha mas não tem condição Ready, e um bug de agenda cron pontual (29 de fevereiro cruzando século não-bissexto — específico, mas reflete o tipo de teste de borda que o projeto vem fazendo). A 1.20.4, por sua vez, é patch de dependências: Go atualizado pra 1.26.6 (fixes em crypto/tls, encoding/asn1, net/http), golang.org/x/crypto e grpc bumped. As notas de release citam três achados em golang.org/x/crypto que continuam sem correção na linha 1.20 — o próprio changelog registra que eles não afetam a operação do cert-manager, então não é motivo pra travar o upgrade.

Por que isso importa mesmo sem CVE

O ponto não é “vazamento de segredo direto” — nenhum dos dois issuers coloca token de API ou chave privada na resposta HTTP em condições normais. O ponto é a classe de risco: dado não confiável, vindo de um endpoint externo (ACME ou Vault), sendo refletido sem sanitização num objeto do Kubernetes com modelo de RBAC mais permissivo do que o dado provavelmente merecia.

Dois cenários concretos que essa mudança fecha:

  1. Exposição de informação além do necessário. Times que têm RBAC read-only em Events (comum — é um objeto “de observabilidade”, não “de segredo”) passam a enxergar qualquer coisa que o servidor ACME/Vault decidiu devolver, incluindo detalhes de infraestrutura interna do provedor ou do seu próprio ambiente que apareciam nas mensagens de erro. Não é uma falha de autorização — é dado sensível indo parar num objeto pensado pra ser mais aberto.
  2. Superfície de injeção em pipelines que consomem Events/status como texto. Se você tem automação — bot de Slack, exportador de Events pra um SIEM, dashboard que renderiza status.conditions[].message — que trata esse campo como texto confiável, um corpo de resposta controlado por um endpoint comprometido virava um jeito de injetar conteúdo arbitrário nesse pipeline.

Vale registrar o contraste com o que é tratado como vulnerabilidade formal no projeto: em junho de 2026 saiu a GHSA-8rvj-mm4h-c258, severidade alta, sobre a ClusterRole padrão de edit permitir que qualquer usuário de namespace crie recursos Challenge/Order diretamente e explore credenciais DNS de um ClusterIssuer sem passar pela política do Issuer. São dois problemas diferentes — um é bypass de política via RBAC padrão permissivo demais, o outro é dado não confiável indo pra objeto errado — mas os dois apontam pro mesmo ponto cego: no fluxo ACME do cert-manager, o que “parece” read-only ou inofensivo (Challenge, Order, Event, status) às vezes carrega mais poder ou mais dado do que a intuição sugere.

O que fazer

  • Atualize. Se você está na linha 1.21, vá pra 1.21.2. Se está preso na 1.20 (LTS-like, ainda recebendo patch), vá pra 1.20.4. Nenhuma das duas traz breaking change documentado — é upgrade de rotina, sem motivo pra esperar janela especial.
  • Se você tem automação lendo status.conditions ou Events do cert-manager como fonte de diagnóstico, ajuste pra também consultar o log do controller depois do upgrade — o corpo completo do erro não estará mais no status pra requisições que não são “problem document” ACME.
  • Revise quem tem get/list em Events e em Issuer/ClusterIssuer no seu cluster e compare com quem tem acesso a Secret. Se a resposta for “praticamente todo mundo tem Events, poucos tem Secret”, vale entender que isso é intencional no design do Kubernetes — Events são efêmeros e de baixa confidencialidade por padrão — mas só funciona se nada sensível for escrito lá, que era exatamente a falha que essa versão corrige.
  • Se você usa a ClusterRole edit padrão e ClusterIssuers com credenciais DNS, revisite a GHSA-8rvj-mm4h-c258 separadamente — é advisory de junho, já tem tempo, mas continua relevante pra quem não aplicou a mitigação.

Fora essas duas frentes, o resto do release é manutenção: menos superfície de bug em cenários de borda, dependências atualizadas. Não é um patch que pede reunião de emergência — mas é o tipo de upgrade que vale entrar na próxima janela normal, não na próxima vez que sobrar tempo.

Fontes