9 de junho de 202610 min de leitura

Secret Sprawl em Kubernetes multi-account: como centralizar sem perder a agilidade com External Secrets Operator

Viktoria Bisova, DevOps Engineer, Itigix

Cloud Native Computing Foundation

Banner - Secret Sprawl em Kubernetes multi-account: como centralizar sem perder a agilidade com External Secrets Operator

Times de infra que operam múltiplos clusters e contas AWS enfrentam o desafio de replicar credenciais compartilhadas sem duplicação manual. Este artigo mostra como o External Secrets Operator (ESO) com Bitwarden resolve esse problema, centralizando a gestão enquanto mantém o consumo nativo de Secrets do Kubernetes.

TL;DR: Gerenciar secrets manualmente em cada cluster ou conta cloud gera retrabalho, risco de falha humana e dificulta rotações. Este artigo analisa a implementação prática do External Secrets Operator com Bitwarden como central source of truth, demonstrando como sincronizar credenciais compartilhadas entre clusters isolados (Dev, Staging, Prod) sem duplicação. Para empresas brasileiras que operam múltiplas contas AWS ou multi-cloud, a abordagem reduz custos operacionais e riscos de segurança.

O provisionamento de infraestrutura em Kubernetes está cada vez mais automatizado, mas o gerenciamento de secrets frequentemente continua sendo um desafio à medida que os ambientes crescem. Organizações normalmente separam workloads de desenvolvimento, staging e produção entre clusters, namespaces ou contas cloud para melhorar a segurança e reduzir o blast radius. Embora esse isolamento seja benéfico, ele introduz um problema operacional recorrente: como distribuir e rotacionar credenciais compartilhadas de forma consistente através desses limites? Nossa equipe enfrentou isso recentemente ao projetar um ambiente escalável para um cliente rodando em AWS EKS.

O problema que nos propusemos a resolver não é exclusivo da AWS, nem da infraestrutura cloud em geral. Esteja você rodando no Azure, Google Cloud, em um setup multi-cloud, infraestrutura on-premise ou automatizando ambientes de desenvolvimento local com ferramentas como KIND ou Minikube, o ponto de dor permanece idêntico: garantir a replicação transparente de secrets através de limites isolados. Cada ambiente (Dev, Staging, Prod) reside em sua própria conta, namespace ou cluster distintos. Essa separação é excelente para segurança e mitigação de blast radius, mas introduz uma complexidade operacional significativa. Como replicar secrets compartilhados entre esses ambientes isolados sem copiar e colar manualmente?

Este artigo explica como resolvemos o problema de sincronização de secrets multi-conta usando External Secrets Operator (ESO) e Bitwarden Secrets Manager.

Qual era o desafio real? Gerenciar secrets compartilhados entre ambientes isolados

Nosso cliente roda duas aplicações com dependências pesadas de integrações de terceiros. Encontramos uma barreira ao projetar a automação para provisionamento de novos ambientes:

  1. Shared credentials: Em ambientes não produtivos, as aplicações compartilham credenciais "sandbox" idênticas para ferramentas terceiras.
  2. Fragmented storage: Cada cluster EKS vive em uma conta AWS separada. Usar AWS Secrets Manager por conta significava que, quando uma chave de API de terceiro era rotacionada, tínhamos que atualizá-la manualmente em todas as contas AWS.

Centralized management: Queríamos uma única fonte da verdade para credenciais compartilhadas, de modo que a rotação de secrets pudesse ocorrer em um local e se propagar automaticamente para os ambientes consumidores. O requisito fundamental era separar o armazenamento de secrets do consumo de secrets.

Como o External Secrets Operator funciona como ponte?

Selecionamos o External Secrets Operator (ESO) porque ele fornece um modelo de reconciliação nativo do Kubernetes para sincronizar secrets de sistemas externos de gerenciamento de secrets para a Secret API do Kubernetes. Isso permite que as aplicações continuem consumindo Secrets padrão do Kubernetes enquanto a source of truth permanece externa ao cluster.

Para o backend de armazenamento, selecionamos o Bitwarden Secrets Manager. A lógica foi pragmática: nosso cliente já usava o Bitwarden Password Manager em toda a organização. Isso facilitou a recomendação do uso da solução Secrets Manager, que nos permitiu manter os controles de acesso da organização em um único lugar.

Um dos pontos fortes do ESO é seu design provider-agnostic. O operador suporta inúmeros backends de secrets, incluindo HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, Google Secret Manager, Bitwarden Secrets Manager e outros. Nesta implementação, selecionamos Bitwarden porque estava alinhado com as práticas existentes de gerenciamento de credenciais do cliente. O padrão permanece o mesmo independentemente do provider: armazene secrets centralmente, deixe o ESO sincronizá-los para Kubernetes Secrets em cada cluster em todas as contas.

Diagrama da arquitetura mostrando o fluxo entre Bitwarden, ESO e múltiplos clusters EKS

Como a arquitetura funciona na prática?

Em alto nível, a arquitetura consiste em três componentes:

  1. Um sistema centralizado de gerenciamento de secrets que serve como source of truth.
  2. O External Secrets Operator rodando dentro de cada cluster Kubernetes.
  3. Kubernetes Secrets gerados e mantidos pelo ESO para consumo das aplicações.

Quando um recurso ExternalSecret é criado, o ESO recupera o secret referenciado do provider externo e cria ou atualiza o Kubernetes Secret correspondente. O reconciliation loop verifica continuamente por atualizações e sincroniza as mudanças de acordo com o intervalo de refresh configurado.

Essa abordagem desacopla o armazenamento de secrets do consumo de secrets, permitindo que as aplicações continuem usando mecanismos nativos padrão do Kubernetes.

A Implementação passo a passo

Abaixo está um guia passo a passo para configurar isso em um ambiente Kubernetes. Embora tenhamos automatizado todos esses recursos usando Terraform, este guia apresenta os comandos diretos para fins de clareza. Para o código de automação, consulte este repositório GitHub.

Pré-requisitos

  • Um Cluster Kubernetes (EKS, AKS, GKE ou outro).
  • kubectl e helm instalados localmente.
  • Acesso a um secret management provider suportado. Este exemplo usa Bitwarden Secrets Manager, mas o mesmo workflow pode ser adaptado para outros providers suportados pelo External Secrets Operator.

Passo 1: Proteger a comunicação entre ESO e Bitwarden SDK

A integração entre ESO e o Bitwarden SDK requer comunicação HTTPS segura.

  1. Para lidar com certificados TLS dinamicamente dentro do cluster, primeiro instalamos o Cert Manager.

    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
    
  2. Crie um ClusterIssuer autoassinado:

    cat <<EOF | kubectl apply -f -
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: bitwarden-bootstrap-issuer
    spec:
      selfSigned: {}
    EOF
    
  3. Crie o namespace external-secrets e gere o Certificado CA Raiz neste namespace:

    kubectl create namespace external-secrets
    
    cat <<EOF | kubectl apply -f -
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: bitwarden-bootstrap-certificate
      namespace: external-secrets
    spec:
      commonName: bitwarden-tls-ca
      isCA: true
      secretName: bitwarden-ca-certs
      issuerRef:
        name: bitwarden-bootstrap-issuer
        kind: ClusterIssuer
    EOF
    
  1. Crie o Issuer Local e o Certificado TLS Final que será usado pelo servidor Bitwarden SDK para seu endpoint HTTPS.

    cat <<EOF | kubectl apply -f -
    apiVersion: cert-manager.io/v1
    kind: Issuer
    metadata:
      name: bitwarden-certificate-issuer
      namespace: external-secrets
    spec:
      ca:
        secretName: bitwarden-ca-certs
    ---
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: bitwarden-tls-certs
      namespace: external-secrets
    spec:
      secretName: bitwarden-tls-certs
      dnsNames:
        - bitwarden-sdk-server.external-secrets.svc.cluster.local
        - external-secrets-bitwarden-sdk-server.external-secrets.svc.cluster.local
        - localhost
      issuerRef:
        name: bitwarden-certificate-issuer
        kind: Issuer
    EOF
    

Passo 2: Instalar o External Secrets Operator com suporte a Bitwarden

Em seguida, instalamos o ESO. É crucial aqui habilitar o Bitwarden SDK especificamente, pois ele inicializa o serviço necessário para se comunicar com a API do Bitwarden.

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

helm install external-secrets external-secrets/external-secrets \
  --namespace external-secrets \
  --set installCRDs=true \
  --set "bitwarden-sdk-server.enabled=true"

Passo 3: Autenticação

Geramos um Machine Account Access Token a partir do portal Bitwarden. Este token concede acesso read-only ao projeto específico que contém nossos secrets de aplicação. O secret provider requer credenciais que permitam ao ESO recuperar secrets em nome das workloads. Neste exemplo, um token de conta de máquina é usado com permissões somente leitura e escopo para o projeto que contém os secrets da aplicação. Seguindo o princípio do menor privilégio, a conta deve ter apenas as permissões necessárias para a sincronização.

Armazenamos este token como um Kubernetes Secret padrão para que o ESO pudesse usá-lo para autenticar:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: bitwarden-access-token
  namespace: external-secrets
type: Opaque
stringData:
  token: <YOUR_BITWARDEN_ACCESS_TOKEN>
EOF

Nota: Você pode conceder ao seu access token permissão de escrita também. Você pode configurar um External Secrets Operator para criar secrets no Bitwarden com a API ESO PushSecret.

Passo 4: Criar o ClusterSecretStore

No ESO, um SecretStore define como falar com o provider. Optamos por um ClusterSecretStore (escopo global) para que desenvolvedores em qualquer namespace pudessem referenciar o store central sem precisar reconfigurar a autenticação.

cat <<EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
  name: bitwarden-global-store
spec:
  provider:
    bitwardensecretsmanager:
      apiURL: https://vault.bitwarden.eu./api
      identityURL: https://vault.bitwarden.eu./identity
      auth:
        secretRef:
          credentials:
            key: token
            name: bitwarden-access-token
            namespace: external-secrets
      bitwardenServerSDKURL: https://bitwarden-sdk-server.external-secrets.svc.cluster.local:9998
      caProvider:
        type: Secret
        name: bitwarden-ca-certs
        key: ca.crt
        namespace: external-secrets
      organizationID: <YOUR_ORGANIZATION_ID>
      projectID: <YOUR_PROJECT_ID>
EOF

Passo 5: A sincronização (ExternalSecret)

Finalmente, criamos o ExternalSecret. Este é o recurso que diz ao operador: "Vá para o Bitwarden, pegue o secret chamado 'stripe-api-key' e crie um Kubernetes Secret chamado 'payment-creds'."

cat <<EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: app-payment-creds
  namespace: app-backend
spec:
  refreshInterval: 15m   # Rotaciona automaticamente a cada 15 minutos
  secretStoreRef:
    name: bitwarden-global-store
    kind: ClusterSecretStore
  target:
    name: payment-creds     # O nome do K8s Secret resultante
    creationPolicy: Owner
  data:
  - secretKey: api_key      # Chave dentro do K8s Secret
    remoteRef:
      key: "<YOUR_NAME_OR_ID_SECRET_HERE"    # Nome/ID do secret no Bitwarden
EOF

Resultado: Gestão centralizada de secrets em escala

Ao adotar o External Secrets Operator e um secret provider centralizado, desacoplamos o gerenciamento de secrets do provisionamento de ambientes. Novos clusters podem consumir secrets existentes sem duplicação manual, e as rotações de credenciais ocorrem em um único local, em vez de em múltiplas contas ou ambientes.

Agora, quando provisionamos um novo ambiente, seja em uma conta cloud existente ou em uma nova, precisamos apenas instalar o ESO e o ClusterSecretStore. O operador busca automaticamente os secrets mais recentes do Bitwarden. Se rotacionarmos uma chave, atualizamos uma vez no Bitwarden e, dentro de 15 minutos, todos os clusters recebem a atualização automaticamente.

Esteja você gerenciando dois clusters ou duzentos, o objetivo permanece constante: eliminar gargalos de deployment. O External Secrets Operator forneceu um mecanismo consistente para sincronizar secrets entre ambientes Kubernetes isolados, preservando o controle centralizado sobre o gerenciamento de credenciais. Embora a implementação descrita aqui use Bitwarden Secrets Manager, o mesmo padrão pode ser aplicado com muitos providers suportados. Para equipes que operam múltiplos clusters ou contas cloud, essa abordagem pode reduzir a sobrecarga operacional, simplificar a rotação de secrets e melhorar a consistência entre ambientes.

Perguntas Frequentes

  • O External Secrets Operator funciona com qualquer provedor de secrets ou apenas com Bitwarden?

    • O ESO é provider-agnostic: suporta HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, Google Secret Manager, Bitwarden Secrets Manager e outros. O padrão de implementação (SecretStore + ExternalSecret) é o mesmo independente do backend escolhido.
  • Preciso instalar o Cert Manager para usar o ESO com Bitwarden?

    • Sim, para o Bitwarden SDK Server. O ESO com Bitwarden requer comunicação HTTPS segura entre o SDK server e o cluster. O Cert Manager é usado para gerar e gerenciar os certificados TLS internos. Outros providers podem não exigir essa etapa.
  • Como fica a segurança se o ClusterSecretStore for global?

    • O ClusterSecretStore é global, mas as credenciais de acesso (access token) são armazenadas em um Kubernetes Secret no namespace do ESO. O operador usa esse token com permissões read-only e escopo de projeto, seguindo o princípio do menor privilégio. Cada namespace pode referenciar o store sem reconfigurar autenticação.
  • Qual o impacto no SLA do cluster se o provedor de secrets ficar indisponível?

    • O ESO usa um reconciliation loop contínuo. Se o provedor externo falhar, os Kubernetes Secrets já existentes continuam funcionando para os pods. O refresh interval define a janela de exposição a secrets desatualizados. Em caso de falha prolongada, o cluster mantém o último estado sincronizado.
  • Vale a pena adotar essa arquitetura para equipes pequenas com apenas um cluster?

    • Sim, especialmente se você quiser centralizar rotações e evitar que secrets fiquem espalhados em repositórios ou documentação. Mesmo com um cluster, o ESO elimina a duplicação manual entre namespaces e facilita auditoria. O custo operacional de setup é baixo comparado ao ganho de consistência.

Artigo originalmente publicado por Viktoria Bisova, DevOps Engineer, Itigix em Cloud Native Computing Foundation.

Gostou? Compartilhe:
Precisa de ajuda?Fale com nossos especialistas 👋
Avatar Walcew - Headset