Gestão de Segredos no Kubernetes: Automatizando Secrets com External Secrets Operator

Arquitetura e Motivação do External Secrets Operator

Operadores de segredos tradicionais no kubernetes, como o segredo nativo, armazenam dados sensíveis (como tokens de API, certificados e credenciais de banco de dados) como objetos base64. Essa abordagem apresenta desafios críticos: a criptografia em repouso pode não ser configurada por padrão em provisionamentos de cluster vanilla, o ciclo de vida dos segredos é acoplado ao ciclo de vida do objeto PersistentVolume (PV) ou ConfigMap, e a revogação em caso de violação exige a recriação manual de todos os objetos afetados. Além disso, o compartilhamento de segredos entre namespaces é um problema operacional complexo e sujeito a erros.

External Secrets Operator (ESO) resolve isso implementando um padrão de bridge (ponte). Ele atua como um controlador Kubernetes que sincroniza informações externas de um Secrets Manager (como AWS Secrets Manager, HashiCorp Vault, Google Secret Manager ou Azure Key Vault) com objetos Secret nativos do Kubernetes. A filosofia é simples: o ESO consome um CRD (Custom Resource Definition) chamado ExternalSecret. Este objeto define uma referência a um segredo remoto e como mapeá-lo para um segredo nativo. O operador em tempo real reconcilia o estado, garantindo que o segredo Kubernetes permaneça idêntico à fonte de verdade externa.

Esta arquitetura de “fonte de verdade externa” é fundamental para a segurança moderna. Ela centraliza a gestão de credenciais em uma solução dedicada com políticas de acesso granular (IAM/RBAC), audit logs e capacidade de rotação de chaves. O Kubernetes torna-se o executor, consumindo segredos que são provisionados dinamicamente. Isso elimina a necessidade de armazenar segredos persistentemente nos repositórios de manifesto do Kubernetes, reduzindo a superfície de ataque.

Provisionamento do Operador via Helm e Kustomize

Para começar, o deploy do ESO precisa ser executado com permissões mínimas. Utilizar o Helm Charter oficial é o método padrão. Primeiro, é necessário adicionar o repositório do projeto ao seu client Helm:

helm repo add external-secrets https://charts.external-secrets.io
helm repo update

O Helm instala a CRD ExternalSecret e o controlador. Em ambientes de produção, a configuração deve ser afinada via valores personalizados (values.yaml). Um exemplo prático de um values.yaml que instala o operador com logging estruturado e limites de recursos (resource requests/limits) para garantir estabilidade em clusters sob alta carga:

installCRDs: true

image:
  repository: ghcr.io/external-secrets/external-secrets
  tag: "v0.9.15"

resources:
  requests:
    memory: "128Mi"
    cpu: "100m"
  limits:
    memory: "512Mi"
    cpu: "500m"

nodeSelector:
  node: "monitored"

tolerations:
  - key: "dedicated"
    operator: "Equal"
    value: "operator"
    effect: "NoSchedule"

metrics:
  enabled: true
  serviceMonitor:
    enabled: true
    namespace: monitoring

O deploy final é executado com:

helm install external-secrets external-secrets/external-secrets 
  -f values.yaml 
  --namespace external-secrets 
  --create-namespace

Para ambientes GitOps (como ArgoCD ou Flux), o Helm deve ser baixado e renderizado localmente, gerando os manifests YAML brutos que são então commitados no repositório de infraestrutura.

Conectividade com AWS Secrets Manager

A integração com a AWS exige um setup de Workload Identity ou amplo controle de policies via IAM. No EKS, o padrão é anotar o ServiceAccount do operador com um IAM Role. Primeiro, crie uma Policy IAM permitindo leitura no segredo desejado. Exemplo de policy JSON:


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/db/credentials-AbCdEf"
    }
  ]
}

Após anexar essa policy a um Role, o ESO utiliza o módulo irsa (IAM Roles for Service Accounts) para autenticação. O ESO então fornece um CRD SecretStore para configurar a conexão com o Secrets Manager.

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secretsmanager
  namespace: app-namespace
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets

Isso define onde o operador deve buscar os dados. Note que o SecretStore está em um namespace específico. Um ClusterSecretStore poderia ser usado para uma configuração global, mas a prática de “least privilege” prefere store por namespace.

Criando External Secrets e Mapeamento de Chave

O coração da automação reside no objeto ExternalSecret. Ele instrui o ESO qual segredo ler do AWS e como nomear as chaves dentro do segredo Kubernetes resultante. Vamos supor que o segredo no AWS SM está armazenado como chave-valor JSON: {"username": "admin", "password": "s3cr3tP@ss"}.

O recurso dataFrom ou data controla o mapeamento.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: app-namespace
spec:
  refreshInterval: 1h # Intervalo de sincronização
  secretStoreRef:
    name: aws-secretsmanager
    kind: SecretStore
  target:
    name: db-secret-k8s # Nome do Segredo no K8s
    creationPolicy: Owner # O ESO gerencia o lifecycle
    template:
      type: Opaque
      data:
        # Sobrescrevendo ou definindo estrutura
        MYSQL_USER: "{{ .username }}"
        MYSQL_PASSWORD: "{{ .password }}"
  data:
    - secretKey: username # Chave no segredo K8s
      remoteRef:
        key: prod/db/credentials # ID do segredo no AWS
        property: username
    - secretKey: password
      remoteRef:
        key: prod/db/credentials
        property: password

O ESO executa uma chamada API ao AWS, obtém o segredo, injeta as propriedades no template e persiste o objeto db-secret-k8s no namespace. Se o segredo no AWS for alterado (ex: rotação via AWS Lambda), o ESO detecta a mudança através do refreshInterval e atualiza o segredo Kubernetes automaticamente.

Uma alternativa poderosa é usar dataFrom para extrair todas as chaves do JSON remoto automaticamente, sem mapeamento manual:

spec:
  dataFrom:
  - extract:
      key: prod/db/credentials

Isso gera um segredo Kubernetes com as mesmas chaves do JSON externo (username, password).

Segurança em Runtime com Vault e Cert-Manager

Em ambientes on-premise ou cloud-agnostic, HashiCorp Vault é frequentemente preferido. O ESO suporta o provider Vault, permitindo o uso de AppRole ou tokens JWT para autenticação. Um cenário avançado envolve a geração dinâmica de segredos via Vault, como credenciais de banco de dados com TTL curto.

Primeiro, configure o SecretStore para Vault:

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: app-namespace
spec:
  provider:
    vault:
      server: https://vault.internal:8200
      path: secret
      version: v2
      auth:
        kubernetes:
          mountPath: kubernetes
          role: external-secrets-role
          serviceAccountRef:
            name: external-secrets

Para gerar certificados TLS dinamicamente, podemos integrar o ESO com o Cert-Manager. Embora o Cert-Manager gere certificados, eles podem ser armazenados no Vault. O ESO lê o segredo do Vault e o expõe para a aplicação. Alternativamente, use o ESO para ler o segredo tls.crt e tls.key gerados pelo Cert-Manager em um namespace e espalhá-los para outros namespaces.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: tls-cert-shared
  namespace: other-namespace
spec:
  refreshInterval: 24h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: wildcard-tls
  data:
    - secretKey: tls.crt
      remoteRef:
        key: pki/issue/example-com
        property: certificate
    - secretKey: tls.key
      remoteRef:
        key: pki/issue/example-com
        property: private_key

Migração Gradual e Estratégia de Deployment

Substituir segredos estáticos por dinâmicos requer um plano de migração. O padrão de fluxo deve ser: 1. Identificar todos os segredos estáticos (via kubectl get secrets --all-namespaces -o json). 2. Provisionar o Secrets Manager com os valores iniciais. 3. Substituir o objeto Secret estático por um ExternalSecret no mesmo namespace. 4. Atualizar os DeploymentConfigs ou Deployments para lerem do novo segredo montado via volume ou env vars.

Para facilitar a transição, o ESO suporta o recurso creationPolicy: Orphan. Se você definir isso no ExternalSecret, o ESO não deletará o segredo antigo se o objeto for removido. Isso permite rollback manual.

Em pipelines CI/CD (GitLab CI, GitHub Actions), a inserção de segredos pode ser feita via script que chama a API do AWS CLI ou HashiCorp Vault. Um exemplo de script Bash para criar/atualizar um segredo no AWS SM via CI:

#!/bin/bash
SECRET_VALUE='{"username":"app_user","password":"$(openssl rand -base64 32)"}'
aws secretsmanager create-secret 
  --name prod/app/credentials 
  --secret-string "$SECRET_VALUE" 
  --region us-east-1
# Se já existir, use update-secret
# aws secretsmanager update-secret ...

Isso garante que o ciclo de vida do segredo seja gerenciado pela pipeline de entrega, e não pela pipeline de infraestrutura.

Observabilidade e Tratamento de Falhas

Uma operação segura depende de visibilidade. O ESO exporta métricas Prometheus padrão. Métricas críticas para alertas:

  • external_secrets_sync_calls_total: Total de chamadas de sincronização.
  • external_secrets_sync_calls_fail_total: Contagem de falhas. Um aumento aquí pode indicar problemas de conectividade ou permissões.
  • external_secrets_reconcile_duration_seconds: Tempo gasto na reconciliação.

Exemplo de configuração do ServiceMonitor (necessário se o Prometheus Operator estiver instalado):

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: external-secrets
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: external-secrets
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics

Para tratamento de falhas, o ESO implementa backoff exponencial nas tentativas de conexão com o provedor externo. No entanto, deve-se configurar alertas no Prometheus para falhas persistentes. Um exemplo de regra Alertmanager para falhas de sync:

- alert: ExternalSecretSyncFailure
  expr: rate(external_secrets_sync_calls_fail_total[5m]) > 0
  for: 5m
  labels:
    severity: warning
  annotations:
    summary: Falha na sincronização do ExternalSecret {{ $labels.name }}
    description: O operador falhou em sincronizar o segredo por mais de 5 minutos.

Além das métricas, a configuração de logs estruturados (JSON) é essencial. Adicione o parâmetro --zap-log-level=debug ao comando de inicialização do operador para depuração detalhada de erros de mapeamento ou autenticação.

Padrões de Segurança Avançados e Validadores

Para garantir a segurança no nível do cluster, utilize o Kubernetes Validating Admission Policy (ou ValidatingWebhookConfiguration) para restringir a criação de Secrets nativos. Você pode criar um webhook que rejeite qualquer Secret que não seja gerenciado pelo ESO (identificado por labels específicas).

Um exemplo de YAML para uma webhook policy (requer a instalação do gatekeeper ou kyverno):

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPAllowedRepos
metadata:
  name: require-external-secrets
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Secret"]
  parameters:
    repos:
      - " ghcr.io/external-secrets/*"

Embora isso seja uma simplificação, a ideia é validar que segredos críticos sejam provenientes do operador. Outra camada é o uso de Secret Scanning (como GitGuardian ou secrets in SAST) nos repositórios Git para detectar tentativas de commitar segredos antes mesmo de chegar ao cluster.

Finalmente, pratique o princípio do mínimo privilégio absoluto. Nunca dê ao ServiceAccount do operador acesso a todo o namespace. Utilize SecretStore por namespace e roles IAM específicas por região ou grupo de segredos. A automação com External Secrets Operator transfere a complexidade da gestão de segredos de você para a ferramenta, mas a responsabilidade pela configuração segura dos credenciais de acesso permanece crucial.