Tools: Breaking: Kubernetes sem Cloud Provider (Parte 2): Criando Operators em Go para automação e self-service de plataforma

Tools: Breaking: Kubernetes sem Cloud Provider (Parte 2): Criando Operators em Go para automação e self-service de plataforma

Motivação

Como tudo funciona por baixo dos panos

Testes e Validação

Considerações finais 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: 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: 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. 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: 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: 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: 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. 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: 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. Apesar de existirem diversas soluções prontas para integração entre Kubernetes e Vault, implementar esses Operators foi um excelente exercício para compreender: Além do aprendizado técnico, o projeto também ajudou a reduzir significativamente a complexidade operacional envolvida na configuração manual desses componentes. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. as well , this person and/or

Code Block

Copy

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 CODE_BLOCK: 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 CODE_BLOCK: 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 CODE_BLOCK: 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" CODE_BLOCK: 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" CODE_BLOCK: 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" - 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. - Criar uma ServiceAccount no Kubernetes. - Criar uma Vault Role para permitir a generation de tokens atrelados a essa ServiceAccount. - Criar uma política de acesso no Vault (Vault Policy) e vinculá-la à Role. - 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) da VaultPolicy com a ServiceAccount do Kubernetes e a respectiva VaultRole. - 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. - 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. - 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