Certificate management no cluster: cert-manager e ACME

1. Introdução ao Gerenciamento de Certificados em Kubernetes

Em ambientes cloud-native, o gerenciamento manual de certificados TLS/SSL é um pesadelo operacional. Certificados expiram, domínios mudam, e cada falha de renovação pode derrubar serviços inteiros. Em Kubernetes, onde dezenas de ingress podem expor serviços simultaneamente, a automação não é opcional — é requisito fundamental.

O cert-manager surgiu como a solução padrão para esse problema. Ele é um operador Kubernetes que automatiza a emissão, renovação e validação de certificados TLS. Integrado ao protocolo ACME (Automatic Certificate Management Environment), ele se comunica com autoridades certificadoras como Let's Encrypt e ZeroSSL para obter certificados válidos sem intervenção humana.

Os benefícios são claros: renovação automática antes do vencimento, validação de domínio simplificada (HTTP-01 ou DNS-01) e integração nativa com recursos Kubernetes como Ingress e Gateway API.

2. Instalação e Configuração do cert-manager no Cluster

A instalação via Helm é a abordagem mais recomendada por sua simplicidade e flexibilidade:

helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set installCRDs=true

Para quem prefere manifests YAML diretos:

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.0/cert-manager.yaml

Após a instalação, verifique se tudo está operacional:

kubectl get pods -n cert-manager
kubectl get crd | grep cert-manager
kubectl get validatingwebhookconfigurations

Os pods principais são: cert-manager (controlador), cert-manager-cainjector (injeção de CA) e cert-manager-webhook (validação de recursos). Os CRDs instalados incluem certificates.cert-manager.io, issuers.cert-manager.io, clusterissuers.cert-manager.io, orders.acme.cert-manager.io e challenges.acme.cert-manager.io.

3. Entendendo os Recursos Customizados (CRDs) do cert-manager

Issuer vs ClusterIssuer: O Issuer tem escopo de namespace — só pode emitir certificados dentro do namespace onde foi criado. O ClusterIssuer é global, acessível por qualquer namespace. Para ambientes multi-equipe, geralmente usa-se um ClusterIssuer centralizado.

Certificate: Define o domínio, duração (via duration e renewBefore) e o issuer referenciado. Exemplo:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: app-exemplo-tls
  namespace: default
spec:
  secretName: app-exemplo-tls
  duration: 2160h  # 90 dias
  renewBefore: 360h  # 15 dias antes
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - app.exemplo.com

Order e Challenge: Quando um Certificate é criado, o cert-manager gera uma Order (pedido ACME) e um Challenge (desafio de validação). O Challenge materializa a prova de posse do domínio — seja via HTTP-01 (arquivo temporário no servidor web) ou DNS-01 (registro TXT no DNS).

4. Integração com Provedores ACME (Let's Encrypt e ZeroSSL)

ClusterIssuer para Let's Encrypt (Staging vs Production)

Sempre teste com o ambiente de staging para evitar rate limits:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: admin@exemplo.com
    privateKeySecretRef:
      name: letsencrypt-staging-key
    solvers:
    - http01:
        ingress:
          class: nginx

Para produção, troque a URL do servidor:

    server: https://acme-v02.api.letsencrypt.org/directory

ClusterIssuer para ZeroSSL

ZeroSSL é uma alternativa confiável ao Let's Encrypt:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: zerossl-prod
spec:
  acme:
    server: https://acme.zerossl.com/v2/DV90
    email: admin@exemplo.com
    privateKeySecretRef:
      name: zerossl-prod-key
    externalAccountBinding:
      keyID: "seu-key-id"
      keySecretRef:
        name: zerossl-eab-secret
        key: hmac-key

Diferenças HTTP-01 vs DNS-01

Característica HTTP-01 DNS-01
Valida domínio específico domínio + wildcards
Requer Ingress acessível publicamente acesso à API do DNS
Renovação automática via Ingress automática via provedor DNS
Wildcards não suporta suporta

5. Desafio HTTP-01: Validação Automática via Ingress

O HTTP-01 funciona assim: o cert-manager cria um pod temporário que expõe um arquivo de validação no caminho /.well-known/acme-challenge/<token>. A CA tenta acessar esse arquivo para confirmar que você controla o domínio.

Exemplo prático — Ingress com certificado automático:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    acme.cert-manager.io/http01-edit-in-place: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - app.exemplo.com
    secretName: app-exemplo-tls
  rules:
  - host: app.exemplo.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-service
            port:
              number: 80

O cert-manager detecta a annotation cert-manager.io/cluster-issuer, verifica o tls no spec e automaticamente cria o Certificate e gerencia a renovação.

6. Desafio DNS-01: Validação com Provedores DNS

DNS-01 é obrigatório para certificados wildcard (*.exemplo.com) e útil para ambientes internos sem exposição pública.

Exemplo com AWS Route53 — primeiro crie o secret com credenciais IAM:

apiVersion: v1
kind: Secret
metadata:
  name: route53-credentials
  namespace: cert-manager
type: Opaque
stringData:
  secret-access-key: "sua-chave-secreta"

Depois configure o ClusterIssuer:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-dns
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@exemplo.com
    privateKeySecretRef:
      name: letsencrypt-dns-key
    solvers:
    - dns01:
        route53:
          region: us-east-1
          hostedZoneID: Z123456789
          accessKeyID: AKIAIOSFODNN7EXAMPLE
          secretAccessKeySecretRef:
            name: route53-credentials
            key: secret-access-key

Agora um certificado wildcard:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-exemplo
  namespace: default
spec:
  secretName: wildcard-exemplo-tls
  dnsNames:
  - "*.exemplo.com"
  issuerRef:
    name: letsencrypt-dns
    kind: ClusterIssuer

7. Estratégias de Renovação, Monitoramento e Troubleshooting

Renovação automática

O cert-manager verifica certificados periodicamente. Por padrão, tenta renovar 30 dias antes do vencimento (configurável via renewBefore). Se falhar, faz novas tentativas com backoff exponencial.

Monitoramento com Prometheus

O cert-manager expõe métricas no formato Prometheus. Exemplo de consulta útil:

# Certificados que expiram em menos de 30 dias
certmanager_certificate_expiration_timestamp_seconds < (time() + 2592000)

Instale o ServiceMonitor para coleta automática:

kubectl apply -f https://raw.githubusercontent.com/cert-manager/cert-manager/master/deploy/charts/cert-manager/templates/servicemonitor.yaml

Troubleshooting comum

  • Rate limits do Let's Encrypt: Use staging para testes. Em produção, limite-se a 50 certificados por domínio por semana.
  • DNS propagation lento: Para DNS-01, aumente o tempo de espera com --set dns01.dnsPropagationTimeout=120s no Helm.
  • Erro de validação HTTP-01: Verifique se o Ingress está acessível publicamente e se não há firewall bloqueando a porta 80.

Comandos úteis para debug:

kubectl describe certificate app-exemplo-tls
kubectl describe order -n default
kubectl logs -n cert-manager deployment/cert-manager

8. Boas Práticas e Integração no Pipeline DevOps

GitOps com cert-manager

Mantenha os manifests de Certificate e ClusterIssuer versionados no Git. Ferramentas como ArgoCD ou Flux sincronizam automaticamente:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: certificates
spec:
  source:
    repoURL: https://github.com/equipe/certificates.git
    path: ./production
  destination:
    server: https://kubernetes.default.svc

Integração com External DNS e Ingress Controllers

External DNS + cert-manager formam um combo poderoso: o External DNS cria registros DNS automaticamente, e o cert-manager emite certificados para esses domínios. O Ingress Controller (NGINX, Traefik, HAProxy) termina o TLS e roteia o tráfego.

Estratégias de rollback e backup

  • Backup: Exporte secrets com certificados usando kubectl get secret -n default app-exemplo-tls -o yaml > backup.yaml e armazene em cofre seguro.
  • Rollback: Mantenha versões anteriores dos CRDs no Git. Em caso de falha, reverta o commit e aplique novamente.
  • DR: Para clusters multi-região, replique os ClusterIssuer e mantenha os mesmos provedores ACME em todas as regiões.

Referências