Secrets rotation automática: renovando credenciais sem downtime

1. Fundamentos da Rotação de Secrets em Kubernetes

1.1. O problema: credenciais estáticas e riscos de vazamento

Em ambientes Kubernetes, é comum que equipes criem Secrets e os mantenham imutáveis por meses ou anos. Essa abordagem estática representa um risco grave de segurança: se uma credencial for exposta em logs, repositórios Git ou clusters comprometidos, a janela de exploração é indefinida. Além disso, regulamentações como SOC 2, PCI-DSS e ISO 27001 exigem rotação periódica de credenciais.

1.2. Conceitos de rotação: manual vs. automática vs. contínua

  • Rotação manual: operador edita o Secret, reinicia pods manualmente. Alto risco de erro humano e downtime.
  • Rotação automática: ferramentas como External Secrets Operator (ESO) atualizam Secrets no cluster, e controllers como Reloader reiniciam pods automaticamente.
  • Rotação contínua: credenciais com TTL curto (ex.: Vault Dynamic Secrets), renovadas antes da expiração, sem intervenção humana.

1.3. Desafios de downtime em aplicações stateful e stateless

Aplicações stateless (APIs REST) toleram reinicializações rápidas via rolling update. Já aplicações stateful (bancos de dados, filas) exigem cuidado: fechar conexões ativas sem perda de dados, migrar sessões e garantir que credenciais antigas ainda sejam aceitas durante a transição.

2. Estratégias de Rotação sem Interrupção

2.1. Blue-green deployment de secrets com múltiplas versões

Crie dois Secrets: db-creds-v1 e db-creds-v2. O Deployment referencia ambos via envFrom com chaves sobrepostas. Durante a rotação, a aplicação tenta conectar com a nova credencial; se falhar, usa a antiga.

apiVersion: v1
kind: Secret
metadata:
  name: db-creds-v1
data:
  DB_PASSWORD: cGFzc3dvcmQxMjM=
---
apiVersion: v1
kind: Secret
metadata:
  name: db-creds-v2
data:
  DB_PASSWORD: cGFzc3dvcmQ0NTY=

2.2. Dual lease pattern: coexistência de credenciais antigas e novas

O banco de dados ou serviço externo deve aceitar ambas as credenciais por um período de transição (ex.: 10 minutos). A aplicação tenta a nova; se falha, fallback para a antiga. Após o grace period, a credencial antiga é revogada.

2.3. Grace period e TTL para transição segura

Defina um TTL para a credencial antiga no Vault (ex.: 5 minutos após a emissão da nova). Durante esse período, ambas funcionam. O Kubernetes Sidecar ou InitContainer aguarda o TTL expirar antes de remover a credencial antiga do pod.

3. Ferramentas de Gerenciamento de Secrets no Cluster

3.1. External Secrets Operator (ESO) com HashiCorp Vault ou AWS Secrets Manager

ESO sincroniza secrets de fontes externas para o cluster. Exemplo com Vault:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: vault-example
spec:
  refreshInterval: "1h"
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: database-creds
  data:
  - secretKey: password
    remoteRef:
      key: secret/data/db
      property: password

Quando o secret no Vault é rotacionado, o ESO atualiza o Secret do Kubernetes no próximo ciclo de refresh.

3.2. Sealed Secrets e SOPS para criptografia GitOps

Sealed Secrets permitem armazenar secrets criptografados no Git. O controller decripta apenas dentro do cluster. Para rotação, basta atualizar o SealedSecret no repositório Git e aplicar via ArgoCD.

3.3. CSI Drivers: montando secrets como volumes sem reinicialização de pods

O Secrets Store CSI Driver monta secrets como volumes efêmeros. Quando o secret externo é atualizado, o driver atualiza o conteúdo do volume sem reiniciar o pod. A aplicação precisa monitorar o arquivo montado para reload automático.

apiVersion: v1
kind: Pod
metadata:
  name: app-with-csi
spec:
  containers:
  - name: app
    image: myapp:latest
    volumeMounts:
    - name: secrets-store
      mountPath: "/mnt/secrets"
      readOnly: true
  volumes:
  - name: secrets-store
    csi:
      driver: secrets-store.csi.k8s.io
      readOnly: true
      volumeAttributes:
        secretProviderClass: "vault-database"

4. Implementação com Kubernetes Native + Controllers

4.1. Reloader: reinicialização automática de Deployments após mudança de Secrets

Stakater Reloader observa mudanças em Secrets e ConfigMaps e realiza rolling update nos Deployments que os referenciam.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  annotations:
    reloader.stakater.com/auto: "true"
spec:
  template:
    spec:
      containers:
      - name: app
        envFrom:
        - secretRef:
            name: database-creds

Quando database-creds é atualizado, o Reloader incrementa uma anotação de configuração, forçando o rollout.

4.2. Stakater Reloader vs. custom webhooks para injeção dinâmica

Reloader é simples e amplamente usado. Para cenários onde reinicialização não é aceitável, use um mutating admission webhook que injeta um sidecar que faz reload da aplicação via signal (SIGHUP) ou API interna.

4.3. Rolling update estratégico: health checks e readiness probes

Configure probes para que o novo pod só receba tráfego quando estiver usando a nova credencial:

readinessProbe:
  exec:
    command:
    - sh
    - -c
    - "pg_isready -h localhost -U app -d mydb"
  initialDelaySeconds: 5
  periodSeconds: 10

5. Rotação de Database Credentials sem Downtime

5.1. Padrão sidecar com proxy de banco (PgBouncer, ProxySQL)

Um sidecar PgBounger gerencia o pool de conexões. Durante a rotação, o sidecar atualiza a senha interna e reconecta gradualmente ao banco, sem que a aplicação precise ser reiniciada.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-with-pgbouncer
spec:
  template:
    spec:
      containers:
      - name: app
        env:
        - name: DB_HOST
          value: "127.0.0.1"
        - name: DB_PORT
          value: "5432"
      - name: pgbouncer
        image: bitnami/pgbouncer:latest
        volumeMounts:
        - name: pgbouncer-config
          mountPath: "/opt/bitnami/pgbouncer/conf"

5.2. Rotação de senhas via Vault Dynamic Secrets

Vault gera senhas temporárias para bancos. O sidecar Vault Agent renova o token e reconfigura o proxy automaticamente.

apiVersion: v1
kind: Pod
metadata:
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "app-role"
    vault.hashicorp.com/agent-inject-secret-database: "database/creds/app"
spec:
  containers:
  - name: app
    env:
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: vault-db-creds
          key: password

5.3. Migração gradual de conexões ativas com pool de conexões

Configure o pool de conexões da aplicação para usar duas credenciais: primária (nova) e fallback (antiga). Feche conexões antigas gradativamente, respeitando transações em andamento.

6. Monitoramento e Validação da Rotação

6.1. Métricas de sucesso: tempo de rotação, pods atualizados, falhas

Exporte métricas via Prometheus:

  • secret_rotation_duration_seconds — tempo entre início da rotação e todos os pods atualizados
  • secret_rotation_pods_updated — contagem de pods com nova credencial
  • secret_rotation_failures_total — falhas de rotação

6.2. Alertas para secrets expirados ou rotação mal-sucedida

Crie alertas no Prometheus + Alertmanager:

groups:
- name: secrets-alerts
  rules:
  - alert: SecretExpired
    expr: time() - secret_last_rotation_timestamp > 86400 * 30
    for: 1h
    labels:
      severity: critical
    annotations:
      summary: "Secret {{ $labels.secret_name }} não foi rotacionado nos últimos 30 dias"

6.3. Testes de integração automatizados pós-rotação

Execute um job Kubernetes após cada rotação que valide:

  • Conexão ao banco com nova credencial
  • Conexão ao banco com credencial antiga falha (após grace period)
  • Métricas de erro zero nos últimos 5 minutos

7. Casos Avançados e Boas Práticas

7.1. Rotação de TLS certificates com cert-manager + ACME

cert-manager renova certificados automaticamente antes da expiração. Configure renewBefore para renovar 30 dias antes:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-cert
spec:
  secretName: my-cert-tls
  renewBefore: 720h  # 30 dias
  dnsNames:
  - myapp.example.com
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer

7.2. Rotação de tokens de serviço (ServiceAccount, OIDC)

Use TokenRequest API para tokens com TTL curto. Configure o pod para renovar o token antes da expiração:

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/enforce-mountable-secrets: "true"
spec:
  automountServiceAccountToken: true
  containers:
  - name: app
    env:
    - name: K8S_TOKEN
      valueFrom:
        fieldRef:
          fieldPath: spec.serviceAccountName

7.3. GitOps workflow: ArgoCD + External Secrets para rotação declarativa

No GitOps, defina o ExternalSecret no Git. Quando o secret no Vault é rotacionado, o ESO atualiza o Secret no cluster. O Reloader reinicia os pods. Tudo declarativo e auditável.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
spec:
  source:
    repoURL: https://github.com/myorg/gitops-config
    path: apps/my-app
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Referências