Uma das maiores cargas cognitivas quando falamos em Kubernetes é a configuração dos vários componentes necessários para entregar valor real ao cluster. Em outras palavras, deparamo-nos com uma quantidade massiva de YAMLs para configurar Custom Resources (CRs), Custom Resource Definitions (CRDs), Roles e tudo o que é necessário para que um serviço tenha um Ingress com TLS, por exemplo.
Isso ficou ainda mais evidente para mim durante os meus estudos de Kubernetes sem as abstrações fornecidas pelos Cloud Providers (assunto que cobrimos na Parte 1). Para resolver esse problema, decidi aprofundar o conhecimento nas maneiras pelas quais o Kubernetes nos permite automatizar essas operações e criar abstrações self-service através de Custom Operators.
Neste artigo, vou documentar a criação de dois operators desenvolvidos em Go que fazem a ponte entre o HashiCorp Vault e os operadores de mercado External Secrets Operator e Cert-Manager:
-
vaultreaver: Configura a integração e os limites de segurança entre o Kubernetes e a API externa do Vault. -
platform-operator: Atua como o centralizador e orquestrador de configurações dentro do cluster.
Motivação
Durante a configuração manual dos componentes responsáveis pela comunicação com o Vault, percebi um padrão repetitivo de tarefas que gerava muita fricção:
- Criar uma
ServiceAccountno Kubernetes. - Criar uma
Vault Rolepara permitir a generation de tokens atrelados a essaServiceAccount. - Criar uma política de acesso no Vault (
Vault Policy) e vinculá-la à Role.
Essa operação é mandatória para que os componentes do cluster se comuniquem de forma segura com o Vault (utilizando o token JWT da ServiceAccount para se autenticarem).
Para entender a fundo a mecânica desse ecossistema, resolvi construir um operador que utilizasse esse mesmo mecanismo de autenticação nativa, mas de forma totalmente automatizada. O objetivo era esconder essa complexidade do usuário final da plataforma, automatizando todo o boilerplate de segurança e gerando os tokens necessários para as operações de cada componente.
Como tudo funciona por baixo dos panos
Nota: Para esse fluxo funcionar, o Vault já deve estar configurado previamente com o método de autenticação de Kubernetes ativo (auth/kubernetes). O nosso operador utiliza a sua própria identidade no cluster para interagir com a API do Vault e criar as novas permissões.
A configuração começa pelo vaultreaver. Ele é responsável por gerenciar o ciclo de vida dos recursos fora do cluster (na API do Vault) e recebe dois Custom Resources principais:
-
VaultPolicy: Declara a política de segurança com as permissões que a aplicação/componente terá dentro do Vault. -
VaultKubernetesRoleBinding: Faz o vínculo (binding) daVaultPolicycom aServiceAccountdo Kubernetes e a respectivaVaultRole.
O exemplo abaixo demonstra a criação de uma permissão declarativa de leitura no path kv do Vault para um namespace de aplicação:
apiVersion: security.platform.io/v1alpha1
kind: VaultPolicy
metadata:
name: nginx-deployment-external-secret
namespace: nginx-apps
spec:
policy: |
path "kv/data/app-teste/secret-secreto" {
capabilities = ["read"]
}
vaultPolicyName: nginx-deployment-external-secret-policy
---
apiVersion: security.platform.io/v1alpha1
kind: VaultKubernetesRoleBinding
metadata:
name: nginx-deployment-external-secret
namespace: nginx-apps
spec:
audience: vault
authMount: kubernetes
boundNamespaces:
- nginx-apps
boundServiceAccounts:
- nginx-deployment-external-secret
roleName: nginx-deployment-external-secret
tokenPolicies:
- nginx-deployment-external-secret-policy
tokenTTL: 1h
Depois que o vaultreaver estabelece com sucesso a ponte de segurança com o Vault, o platform-operator assume a responsabilidade pelas automações dentro do cluster.
A API do platform-operator é desenhada para ser mais ampla, aceitando contratos simplificados focados na experiência do desenvolvedor (Self-Service). O exemplo a seguir demonstra o manifesto VaultCertificate, que abstrai todo o setup necessário para provisionar TLS via Cert-Manager com o backend de PKI do Vault.
Ao receber este único CR, o operador gera dinamicamente os recursos internos:
- A ServiceAccount configurada com o RBAC necessário para o fluxo de certificados.
- As regras do Cert-Manager (como o ClusterIssuer ou Issuer apontando para o Vault).
- O recurso final de Certificate que dispara a emissão real do certificado TLS.
apiVersion: security.platform.io/v1alpha1
kind: VaultCertificate
metadata:
name: nginx-deployment-tls
namespace: nginx-apps
spec:
vaultUrl: [http://172.18.0.12:8200](http://172.18.0.12:8200)
authPath: /v1/auth/kubernetes
vaultRole: nginx-deployment-tls-role
pkiPath: pki/sign/internal-dot-infra
commonName: my-app.internal.infra
dnsNames:
- my-app.internal.infra
- my-app-teste.internal.infra
SecretName: nginx-deployment-tls
certManagerServiceAccount: "cert-manager"
certManagerNamespace: "cert-manager"
Da mesma forma, o operador possui controllers dedicados a simplificar o uso do ExternalSecrets, encapsulando e criando de forma automatizada o SecretStore e o ExternalSecret correspondente a partir de uma interface limpa.
Testes e Validação
Para validar o comportamento do ecossistema e garantir a idempotência dos controllers em Go, montei um laboratório completo que pode ser conferido neste repositório de demonstração:
https://github.com/pedrohro1992/homelab-app-demo
O objetivo do laboratório foi:
- Garantir a automação e o provisionamento de um certificado TLS válido via Cert-Manager usando o nosso platform-operator.
- Injetar credenciais sensíveis via ExternalSecrets vindas diretamente do Vault de forma totalmente declarativa.
O cenário de teste consiste no deployment de uma aplicação Nginx. Através do Ingress configurado com o TLS gerado e com o segredo resolvido pelo operador, conseguimos expor com sucesso a aplicação. Ao acessar a página de diagnóstico, é possível validar visualmente que todas as variáveis de ambiente baseadas no segredo do Vault (como o nosso VALOR_SECRET) foram injetadas perfeitamente no container em tempo de execução.
Considerações finais
Apesar de existirem diversas soluções prontas para integração entre Kubernetes e Vault, implementar esses Operators foi um excelente exercício para compreender:
- Reconciliação de recursos no Kubernetes
- Desenvolvimento de Operators com Kubebuilder
- Fluxos de autenticação Kubernetes ↔ Vault
- Automação de plataforma
- Criação de abstrações de self-service
Além do aprendizado técnico, o projeto também ajudou a reduzir significativamente a complexidade operacional envolvida na configuração manual desses componentes.























