Mais um desabafo em favor de Kubernetes Secrets

No passado, os segredos do Kubernetes deixavam muitos engenheiros coçando a cabeça, como assim… será que isso é uma piada? Os segredos são implementados em codificação base64 que não é um sistema de criptografia, mas uma simples codificação. Claro, qualquer pessoa poderia então decodificá-lo. Devo admitir que eu era um desses engenheiros. Minha opinião mudou, mas posso entender o sentimento que a maioria das pessoas tem com relação a esses objetos, por mais equivocada que seja essa premissa.

Por que os segredos do Kubernetes são codificados em base64? (E tá tudo bem assim)

A codificação base64 dos segredos do Kubernetes nunca foi destinada como uma medida de segurança. Ela não existe para obscurecer ou ocultar quaisquer valores. A decisão de implementar este método de codificação foi inicialmente feita para permitir que os segredos acomodassem dados binários. Se os segredos fossem estruturados como mapas simples, talvez houvesse menos confusão. No entanto, esse não é o ponto. Quando observadores notam a codificação base64, eles muitas vezes a assumem como uma tentativa falha de proteger um valor, até mesmo considerando-a como um erro. Essa interpretação, como acabamos de ver, é equivocada.

Como tornar os Segredos do Kubernetes seguros?

Inicialmente, RBAC (Role Base Access Control): Restrinja usuários ou equipes que têm acesso aos segredos, ou bloqueie esse acesso completamente. Dê acesso de um segredo específico apenas para contas de serviço que precisam dele.

Se você tem alguma engine de policies: Inclua políticas que não permitam que os segredos sejam globalmente acessíveis. Elabore políticas que façam sentido com o seu contexto, bloqueando ou alertando quando alguém está violando suas diretrizes. Talvez você queira apenas evitar que equipes tenham acesso aos segredos de outras equipes. Talvez seu controle seja mais detalhado, e isso cabe a você decidir/descobrir.

Criptografe o etcd at rest: Este é o método para proteger os valores secretos armazenados dentro do etcd e você não deveria se preocupar com o fato de ser fácil decodificar quando você obtém o valor em uma resposta. Quando você recupera um valor do kube api-server com curl ou kubectl, e parece estranho por ser apenas base64, pare para considerar. Quando você está autenticado e autorizado a fazê-lo, não é essencialmente a mesma coisa obter um segredo de uma fonte que você considera segura? Por exemplo, se você usa curl com Hashicorp Vault (ou Azure Key Vault, ou qualquer equivalente), armado com suas chaves e certificados adequados, a resposta não é em plain text (eu sei que é óbvio, acho que nos esquecemos disso)? O valor precisa estar em plain text em algum momento, para o consumidor — para o aplicativo que usará essa credencial — fazer algo. Nosso objetivo principal deve ser garantir o armazenamento seguro e a transmissão segura desses valores (embora este último não seja o foco neste artigo). Não devemos nos preocupar desnecessariamente com os dados sendo exibidos em plain text, desde que o pedido venha de uma entidade autenticada e autorizada que necessite desta informação.

Criptografar o etcd Apenas não é suficiente

A maioria dos guias que ensinam como criptografar o etcd at rest pode te colocar em uma situação difícil. As chaves de criptografia, em muitos casos, ainda são armazenadas nas mesmas máquinas onde os componentes do seu control plane estão sendo executados. Se você considerar que uma instância etcd foi comprometida e, digamos, o invasor tem acesso aos arquivos naquela máquina, é muito simples apenas descriptografar todo o banco de dados.

O método preferido de criptografar os valores do etcd é usando um Serviço de Gerenciamento de Chaves. Aqui temos alguns níveis extras de indireção e também outra camada de garantia de autorização. Você armazena a chave de criptografia, criptografada, em um serviço remoto. E somente serviços ou usuários autorizados podem acessar e descriptografar a chave e, em seguida, os dados. Isso ainda pode ser imitado por um invasor e eles poderiam usar de engenharia reversa apra conseguir o que eles precisam, mas ainda é o método recomendado até o momento (você sempre pode criar mais indireções, mas a complexidade também pode impactar negativamente a resposta a incidentes de segurança, então é melhor seguir abordagens suportadas).

Algumas outras medidas adjacentes serão listadas no final do artigo.

Quem usa Segredos?

Kubernetes 😂. Seu control plane, neste momento, está usando Segredos Nativos do Kubernetes, e esses valores de secrets são, claro, armazenados no etcd. Não importa a versão do Kubernetes, qual provedor de nuvem. Invasores com acesso a esses segredos já podem causar muitos danos se você não proteger esses segredos.

Meu raciocínio para isso é o seguinte: se você acredita que os Segredos do Kubernetes devem ser evitados, isso significa que você também está descartando os segredos que vêm intrinsecamente com o uso do Kubernetes? Provavelmente você não está fazendo isso. Pode ser mais pragmático reconhecer sua existência e planejar maneiras de protegê-los, juntamente com seus outros segredos, neste contexto.

Além disso, ferramentas Cloud Native do Cloud Native Landscape provavelmente anteciparão que seus valores secretos sejam Segredos Nativos do Kubernetes. Se você deseja aproveitar as capacidades plug-and-play do Ecossistema Kubernetes, evitar segredos pode levar a dificuldades desnecessárias. Esta escolha provavelmente resultaria em uma dependência excessiva de soluções personalizadas internas, que não só aumentam a complexidade, mas também introduzem riscos de segurança adicionais.

Então, o etcd está comprometido

Vamos tentar explorar algumas premissas e cenários com relação a evitar segredos.

As suposições relevantes ao evitar Segredos do Kubernetes

O maior argumento contra os Segredos do Kubernetes vem do fato de que, se o etcd for comprometido, basicamente você terá acesso a qualquer valor secreto nele. E eu consigo ver como isso pode ser muito perigoso. Mas eu argumentaria que isso é relevante apenas em um cenário muito irrealista:

  • O invasor tem apenas acesso de leitura ao etcd (maior pegadinha, na minha opinião)

  • O invasor possui a chave de criptografia (ou o etcd não está criptografado)

  • (Opcional) O invasor possui kubeconfg/credenciais de um desenvolvedor com permissões limitadas

    • não pode executar pods em um contêiner

    • não pode criar pods em namespaces importantes

    • só pode obter alguns recursos, logs, e algumas outras permissões menores

  • O cluster está atualizado (acima de 1.24), e não possui segredos de token legados para contas de serviço

Se este cenário se concretizar (um ataque acontecendo exatamente assim), evitar Segredos do Kubernetes na verdade acabou protegendo seu ambiente. Isso é bom.

Então, por que eu digo que este é um cenário irrealista? Você já ouviu falar de uma violação de segurança em que um invasor obteve apenas acesso de leitura ao etcd, sem escrita? Isso exigiria que um invasor tivesse acesso a uma instância ou endpoint do etcd, e tivesse acesso às credenciais de um usuário que só tem leitura (mas eles têm a chave de criptografia). Outra opção poderia ser: se o invasor tem acesso à instância e por algum motivo eles têm acesso apenas à chave de criptografia e não às credenciais. Na minha experiência, e opinião, este não é o cenário que realmente acontece. Quando o etcd é comprometido, é totalmente comprometido (vou tocar no que acontece quando o etcd é totalmente comprometido mais adiante neste artigo).

Os outros requisitos deste cenário também são importantes. Evitar segredos não ajudaria se QUALQUER um deles não fosse verdadeiro. Se um invasor tem pod exec em qualquer pod consumindo um valor secreto (sendo um Segredo Nativo do Kubernetes ou não) esse invasor agora tem um valor secreto (se eles conseguirem pegar um shell — se isso não estiver protegido ou você não estiver usando distro-less). Da mesma forma, se os invasores puderem criar pods usando qualquer mecanismo-que-você-usou-para-evitar-segredos-k8s, com um valor secreto montado, novamente, eles conseguiram um segredo.

Por último, mas não menos importante, se você estiver no Kubernetes <1.24, as contas de serviço costumavam armazenar tokens em segredos (não usando TokenRequests). Qualquer mecanismo-que-você-usou-para-evitar-segredos-k8s, provavelmente usava tokens de conta de serviço ou outras credenciais para se comunicar com Vaults externos. Um invasor com acesso a um etcd comprometido poderia apenas usar esse token para obter um segredo remoto.

Dado que todos esses fatores devem se alinhar para que evitar Segredos Nativos do Kubernetes tenha significado puramente do ponto de vista de proteção de valor secreto, eu consideraria isso nicho, e não realmente relevante para a maioria das configurações ao redor do mundo.

Os não-argumentos

O cenário anterior é um bom exemplo de quando você está realmente protegido por não ter Segredos Nativos do Kubernetes, mesmo que seja irrealista. Os outros listados aqui são, na minha opinião, não são bons argumentos contra o uso de Segredos:

  • Dizer que você pode obter o valor facilmente decodificado através do kube api-server: Isso também é facilmente protegido por RBAC. E mesmo considerando qualquer outra maneira de montar um valor secreto em um pod, através de uma API desprotegida você ainda pode dar pod exec e obter valores secretos.

  • Dizer que Segredos como variáveis de ambiente são facilmente inspecionados: Segredos Nativos do Kubernetes podem ser montados como volumes.

  • Dizer que comprometimentos que não estão relacionados ao etcd também poderiam deixar segredos expostos: O risco ainda existe com qualquer outro mecanismo para evitar segredos do k8s. Acesso root a nós com dumping de RAM ou varredura de memória, sniffing de rede, ou qualquer coisa do tipo. Como dissemos antes, as aplicações têm o segredo final em texto puro porque precisam dele assim. Uma varredura de memória (ou violação semelhante), devido à sua natureza intrusiva, poderia revelar valores secretos independentemente de serem Segredos Nativos do Kubernetes ou não.

A escrita no etcd

Se um invasor tem escrita no etcd (e acesso a qualquer kubeconfig mesmo com um role sem permissões), ele te tem. Não importa se você está evitando segredos nativos do Kubernetes. Não importa se você está usando políticas complexas com engines de policies populares.

Algumas maneiras de obter segredos (ou quase qualquer coisa) com escrita no etcd

  • Insira uma RoleBinding com qualquer papel privilegiado para obter acesso a pods com valores secretos montados.

  • Insira um pod ou Deployment diretamente através do etcd com montagem de valor secreto inline.

  • Se um pod tem uma conta de serviço que o permite obter segredos de um Vault remoto: insira um pod no etcd e faça-o usar a mesma conta de serviço que pode falar com o Vault externo e escreva um código simples para chamar a API de TokenRequest. Isso também funcionaria no Kubernetes >1.24. Você pode registrar o token e obter todos os segredos antes de expirar.

  • Existem algumas outras maneiras mais complicadas de obter segredos, mesmo se o invasor não tiver acesso ao servidor api do kube. Mudar as roles e role bindings de um lado pra outro, obter pods privilegiados dentro do cluster, agir e obter respostas todas através do etcd. É mais difícil, mas possível.

Então, nada é seguro?

Esse não é o ponto. O ponto é que você tem modelos de ameaça muito semelhantes ao evitar os Segredos Nativos do Kubernetes e ao não evitá-los. Portanto, com modelos de ameaça semelhantes, a melhor opção é a mais simples. Basta usar o que você está acostumado e descobrir como proteger isso.

Então ..., não temos nenhum motivo para evitar os Segredos do Kubernetes?

Agora, essa é a pergunta certa! Você tem? Existem muitos motivos para evitar os Segredos do Kubernetes. Talvez você tenha uma política geral da empresa a seguir. Talvez você tenha que aderir a vários tipos de compliance. Talvez a complexidade de evitar os Segredos Nativos do Kubernetes valha a pena em seu caso, porque facilitará sua vida durante auditorias e alguns outros processos recorrentes.

Meu ponto está mais no lado de: Evitar Segredos do Kubernetes não é intrinsecamente mais seguro o tempo todo. Por outro lado, fazer o hash das senhas ao armazená-las em uma tabela de banco de dados é, e é simples o suficiente para sempre valer a pena, e isso claramente não é a mesma coisa. Você PRECISA verificar seus requisitos e ver o que funciona para você, neste caso.

Qual é a melhor maneira de proteger seus segredos então?

Algumas das melhores práticas foram listadas no início do artigo quando falamos sobre RBAC, políticas e criptografia e proteção do ETCD. Mas existem outras práticas de segurança comuns que são talvez ainda mais importantes:

  • Evite vetores de ataque iniciais e uso de software/dependências vulneráveis ​​(segurança de supply chain). Se o seu front end tem uma biblioteca vulnerável que permite que as pessoas injetem código arbitrário e de alguma forma obtenham um shell desse contêiner de front end. Então, se de alguma forma eles conseguem escapar do contêiner e escalar privilégios para o root, e então fazer uma varredura de memória... Essa pessoa tem todos os seus segredos neste nó. Não importa se você está evitando os Segredos Nativos do Kubernetes ou qual solução você está usando para sincronizar seus segredos. Além de evitar software/dependências vulneráveis, existem várias outras maneiras de se proteger, como treinamento contra phishing para funcionários não técnicos, WAF e muitos outros.

  • Esteja atento ao gerenciamento de acesso em todos os lugares. Não apenas RBAC no Kubernetes, mas também para suas contas na nuvem, subcontas e toda a sua infraestrutura.

  • Projete boas práticas de isolamento de identidades e limites de segurança para o seu sistema. Isolar tenants ou equipes de sua empresa de uma maneira que faça sentido em relação ao seu trabalho diário, à sua arquitetura de software e à estrutura de sua equipe, concedendo permissões e acessos apenas às pessoas que precisam.

Eu sei, isso parece genérico e basicamente melhores práticas para proteger seu sistema em geral. Meu ponto é exatamente esse. Você tem que fazer isso primeiro.

Adicionalmente:

  • Garanta que sua abordagem leve em consideração a resposta à segurança, e não apenas a prevenção. Não se trata apenas de proteger segredos. Trata-se também do que fazer quando (não se) eles vazarem.

  • Sendo um dos mantenedores do External Secrets Operator (ESO) e tendo dado a maioria dos conselhos gerais, também quero te dar alguns motivos pelos quais o ESO ajuda com a segurança e facilita seus processos, e acho que você também deve considerá-lo (ou algumas outras ferramentas que também entregam em alguns desses pontos):

    • você pode fornecer aos seus desenvolvedores apenas referências aos segredos, e não os valores dos segredos diretamente.

    • o ESO se integra aos processos de GitOps dos desenvolvedores, facilitando a adoção e a capacidade de depuração.

    • ele facilita o fardo de fazer rotação automática e geração de segredos, ajudando com prevenção e resposta.

    • o ESO já considera configurações multi-tenant e multi-cluster, então, se esse for o seu caso, você não precisa improvisar.

    • ESO pode usar outra identidade de conta de serviço, por configuração do provedor, então você não precisa dar permissões ao pod do operador em todos os seus external providers. Isso permite também que o próprio pod não tenha permissões para o external provider, apenas uma conta de serviço avulsa.

    • Aceitar os Segredos Nativos do Kubernetes como o destino final para os valores dos segredos simplifica sua solução (na maioria das vezes). A complexidade geralmente prejudica a segurança do seu sistema.

Resumindo tudo

O entendimento e a gestão adequada dos segredos do Kubernetes são críticos, e a codificação base64, muitas vezes mal compreendida como uma medida de segurança fraca, é na verdade destinada a lidar com dados binários. Usar RBAC e políticas, e criptografar o etcd em repouso são maneiras de tornar os segredos mais seguros. No entanto, evitar totalmente segredos do Kubernetes não é necessariamente a iniciativa mais segura e pode adicionar complexidade. Avalie sempre suas necessidades específicas para escolher a abordagem correta. Foque não apenas na prevenção, mas também em estratégias de resposta eficazes para possíveis vazamentos. Ferramentas como o External Secrets Operator (ESO) podem ajudar a proteger os segredos Kubernetes, especialmente facilitando as rotações. Essencialmente, a segurança do sistema requer uma visão abrangente, em vez de focar apenas em elementos individuais.

Referências:

Infelizmente, o raciocínio por trás da codificação base64 em segredos não é compartilhado na documentação, ou em PRs/issues, pois isso, é claro, parece óbvio para os desenvolvedores core do Kubernetes. No entanto, Jordan Liggitt, um dos top contributors do Kubernetes, compartilha uma resposta aqui:

Com relação à criptografia em repouso e ao KMS:

Considerando minha declaração de que qualquer control plane do Kubernetes usa segredos para funcionar nos bastidores: se você é um administrador, pode simplesmente usar kubectl get secrets -A dentro de um cluster recém-provisionado para ver todos os segredos sendo usados. Algumas soluções gerenciadas nem mesmo escondem alguns de você. Se for gerenciado por você, você verá centenas deles.

Dada minha opinião e experiência sobre o aspecto de leitura/escrita do etcd durante incidentes. Eu estaria curioso para ouvir sobre outras experiências de pessoas. Talvez eu esteja completamente enganado aqui. Fique à vontade para entrar em contato!

É relevante também dar uma olhada na documentação para Segredos e RBAC nos documentos oficiais:

Olhando para a parte em que falamos sobre injetar pods ou outros recursos diretamente no etcd, aqui você pode ver um exemplo simples passo a passo de injetar um pod:

Embora eu não concorde necessariamente com todas as afirmações do seguinte artigo (já que eu ainda acho que vale a pena usar provedores externos), este foi um dos blogs que ressoou comigo algum tempo atrás quando estávamos tendo esses tipos de discussões dentro da comunidade: https://www.macchaffee.com/blog/2022/k8s-secrets/, escrito por Mac Chaffee. Eu tiro algumas analogias, e meu argumento e pensamento sobre partir de threat modeling para fazer decisões, dele.

Finalmente, uma vez que mencionamos o ESO, eu recomendaria dar uma olhada nos documentos do ESO e na palestra dada no Kubecon:

E também gostaria de agradecer a toda a comunidade do external-secrets por confiar no ESO ao lidar com seus segredos! :D

Como um possível próximo blog post, estou pensando em compartilhar alguns exemplos de labs de como obter segredos em vários cenários e como proteger contra esses ataques. Por favor, me diga se isso seria interessante para você, pois seria muito trabalho, e eu posso fazer isso sob demanda! 😁

Lucas Severo Alves