Desenvolvimento para Kubernetes: Funciona localmente, mas não em produção – Um estudo de caso com Tetragon e eBPF
Um relato detalhado sobre os desafios de integrar o Tetragon, uma ferramenta de segurança em tempo de execução, em ambientes Kubernetes. O artigo aborda problemas como perda de eventos, duplicação e erros de deduplicação, fornecendo soluções e lições aprendidas.
MundiX News·11 de maio de 2026·10 min de leitura·👁 5 views
Desenvolvimento para Kubernetes: Funciona localmente, mas não em produção – Um estudo de caso com Tetragon e eBPF
Introdução
Olá a todos, sou o Sasha, arquiteto de segurança da informação (SI) em um projeto de proteção de ambientes de contêineres. Resumidamente, realizamos a varredura de imagens, protegemos os clusters contra a execução de imagens "ruins" e protegemos os pods em tempo real.
Ao desenvolver segurança em tempo de execução para Kubernetes, é fácil cair em uma armadilha: tudo funciona perfeitamente localmente, mas começa a se comportar de forma imprevisível no cluster real. Neste artigo, analisarei três problemas reais que encontramos ao integrar o Tetragon: perda de eventos, duplicações e erros de deduplicação.
Para esse tipo de proteção, é necessário assinar as chamadas de sistema Linux. Não quisemos reinventar a roda e pegamos uma solução de código aberto (Tetragon) para rastrear e gerenciar eventos no nível do kernel Linux.
O que é Tetragon?
[Imagem: Diagrama geral do Tetragon]
Tetragon reside no kernel Linux no nível eBPF, escuta chamadas de sistema e eventos (como abertura de arquivos, inicialização de processos, conexões de rede), compara-os com as políticas definidas e registra ou bloqueia. Essencialmente, é como um guarda invisível dentro do kernel que vê tudo e decide o que fazer com isso.
Brevemente sobre a estrutura do sistema
Em nosso caso, uma arquitetura específica é usada. Existem serviços de kernel e serviços de agente. Os serviços de kernel são o cérebro do sistema, os agentes estão envolvidos na proteção de clusters/nós específicos. A especificidade reside no fato de que um serviço proxy é usado entre o lado do agente e o kernel. Isso é feito para interação de rede unidirecional, para que o cliente se preocupe menos com as configurações de acesso à rede.
Quase toda a comunicação entre os serviços é realizada usando o broker de mensagens Nats (no lado do kernel e dos agentes).
[Imagem: Diagrama arquitetônico simplificado]
A principal tarefa é criar uma política, detectar violações de políticas e mostrar ao usuário. Parece que não há nada de complicado - conectamos via gRPC ao Tetragon, ouvimos seus logs, encaminhamos do agente e salvamos no ClickHouse.
A tarefa é clara, as políticas foram feitas, as violações foram detectadas. Para preparar o recurso para lançamento, testamos em ambientes de desenvolvimento - tudo funciona.
Problemas em Produção
Infelizmente, durante os testes de carga em um cluster com vários nós, descobriu-se que a maior parte dos logs do Tetragon não chega ao ClickHouse.
Primeiro problema - um kernel que governa todos.
Encontramos o problema. O fato é que o Tetragon carrega programas eBPF no kernel Linux, escuta eventos e decide o que fazer com eles.
Durante o desenvolvimento local, usamos o minikube, que levanta um cluster Kubernetes leve em um único nó (uma única máquina virtual com uma instância do kernel Linux). Consequentemente, não foi possível verificar o trabalho em vários nós. E localmente, conectamo-nos a um pod Tetragon e ouvimos seus logs.
No ambiente de desenvolvimento, tínhamos não um K8s completo, mas seu irmão mais novo, K3s. Na compilação padrão, ele usa um banco de dados SQLite compartilhado, e não um etcd distribuído. Em um nó, o problema de perda de logs não pôde ser reproduzido - por mais que tentássemos. E quando implantamos um K8s multi-cluster real com etcd - ele apareceu imediatamente.
Existe um problema - começamos a corrigi-lo. A solução é bastante simples. Conectar a todas as instâncias do Tetragon em cada nó.
Tetragon funciona como um DaemonSet: um pod em cada nó. Fazemos list pods por seletor, obtemos a lista e conectamos a cada um separadamente.
Novo nó? DaemonSet levantou o pod, o agente o viu no próximo sync, adicionado ao pool.
Conectamos a todos os nós do cluster k8s.
Como resultado, obtivemos um código de trabalho que ajuda a detectar violações de políticas. Os testes de carga confirmaram isso.
Agora estamos realmente concluindo o artigo, obrigado pela atenção.
Spam de violações de políticas
Tudo bem, sou forçado a continuar, porque nem tudo é tão bom. Começamos a testar outros tipos de políticas. Se antes um log chegava para uma ação, agora - dois, três eventos idênticos. Inaceitável. Cavamos mais fundo.
Para entender por que as duplicatas apareceram, você precisa olhar sob o capô das políticas do Tetragon. Pegamos o comando mais simples touch file. Para nós, esta é uma ação. Para o kernel - duas chamadas de sistema: primeiro openat (criar arquivo), depois utimensat (definir o tempo). Na saída, obtemos dois logs. E dois logs não são o limite, pode haver muito.
Mostrar duplicatas como uma potencial ameaça à segurança é uma má abordagem. Você pode criar políticas que, em questão de minutos, em um cluster ativo, gerarão milhões de duplicatas por minuto.
[Imagem: Aparência de eventos duplicados]
Entendemos o problema - é hora de consertar. Fizemos a deduplicação por três sinais:
PID, tempo de recebimento e campos-chave
dos logs do Tetragon.
[Imagem: Deduplicação]
Novamente testes de carga, políticas diferentes, nós diferentes - tudo ótimo, obrigado a todos.
Deduplicação quebrada
Novamente, o testador veio e começou a dar carga. Ele mudou a abordagem. Antes, ele fazia spam de violações de diferentes PIDs. Agora ele decidiu fazer tudo dentro de um script python. E que azar - a deduplicação quebrou. Ele faz cem violações diferentes para políticas diferentes - nós as colamos em um. Problema. Novamente, vamos consertar, desta vez removemos o PID das chaves de deduplicação.
Tudo bem, mas agora dois processos diferentes no mesmo carimbo de data/hora fazem a mesma violação - e nós os colamos em um único evento. Em um cluster carregado, isso é normal.
Como resultado, depois de gastar muita energia, inventando muitos cenários de teste diferentes, alcançamos nosso objetivo. As políticas funcionam, o tempo de execução nos pods é rastreado, os infratores estão tristes, o usuário vê as violações em tempo real.
Conclusão
A principal dificuldade de desenvolvimento para Kubernetes não está na API nem no YAML, mas no fato de que o sistema é originalmente distribuído e isso deve ser sempre lembrado.
eBPF fornece eventos de baixo nível - na verdade, chamadas de sistema (por exemplo, open, execve, connect).
Mas as políticas de segurança são descritas em um nível mais alto:
"o processo dentro do contêiner não deve criar arquivos em /etc"
ou
"não deve executar o shell".
O problema é que uma dessas "ações" do usuário quase sempre se decompõe em várias chamadas de sistema. Por exemplo, um simples touch file se transforma em open e utimensat. Como resultado, há uma lacuna entre o nível da política e o nível do evento, que precisa ser colada de alguma forma.
Na verdade, ainda existem pontos interessantes relacionados à integração de políticas (que compartilharei em artigos futuros). Planejamos aumentar o rps em cerca de 8 vezes. Também resolvemos vários problemas de largura de banda devido à arquitetura incomum. Há uma compreensão do que e como fazer. Algum dia vou descrever essa história também.
Trabalhamos com segurança de contêineres há vários anos, na maioria das vezes olhamos para k8s. E assim que começa a parecer que chegamos ao fundo do poço, no bom sentido, alguém sempre bate de baixo. Temos que testar melhor, encontrar mais casos de canto, organizar brainstorms conjuntos. Mas nós gostamos disso!
Qualquer sistema que pareça simples em um ambiente único se transforma em um sistema distribuído no Kubernetes. O que significa que seu comportamento precisa ser verificado não no nível "funciona/não funciona", mas no nível "como ele se comporta com escalabilidade e concorrência".
🛡️⚡
Pare de pesquisar. Comece a hackear.
O MundiX é seu copiloto de pentest com IA: comandos exatos, análise de outputs e próximo passo na kill chain — em segundos.
Sem cartão para começar · Planos a partir de R$49/mês
Desenvolvimento para Kubernetes: Funciona localmente, mas não em produção – Um estudo de caso com Tetragon e eBPF
Introdução
Olá a todos, sou o Sasha, arquiteto de segurança da informação (SI) em um projeto de proteção de ambientes de contêineres. Resumidamente, realizamos a varredura de imagens, protegemos os clusters contra a execução de imagens "ruins" e protegemos os pods em tempo real.
Ao desenvolver segurança em tempo de execução para Kubernetes, é fácil cair em uma armadilha: tudo funciona perfeitamente localmente, mas começa a se comportar de forma imprevisível no cluster real. Neste artigo, analisarei três problemas reais que encontramos ao integrar o Tetragon: perda de eventos, duplicações e erros de deduplicação.
Para esse tipo de proteção, é necessário assinar as chamadas de sistema Linux. Não quisemos reinventar a roda e pegamos uma solução de código aberto (Tetragon) para rastrear e gerenciar eventos no nível do kernel Linux.
O que é Tetragon?
[Imagem: Diagrama geral do Tetragon]
Tetragon reside no kernel Linux no nível eBPF, escuta chamadas de sistema e eventos (como abertura de arquivos, inicialização de processos, conexões de rede), compara-os com as políticas definidas e registra ou bloqueia. Essencialmente, é como um guarda invisível dentro do kernel que vê tudo e decide o que fazer com isso.
Brevemente sobre a estrutura do sistema
Em nosso caso, uma arquitetura específica é usada. Existem serviços de kernel e serviços de agente. Os serviços de kernel são o cérebro do sistema, os agentes estão envolvidos na proteção de clusters/nós específicos. A especificidade reside no fato de que um serviço proxy é usado entre o lado do agente e o kernel. Isso é feito para interação de rede unidirecional, para que o cliente se preocupe menos com as configurações de acesso à rede.
Quase toda a comunicação entre os serviços é realizada usando o broker de mensagens Nats (no lado do kernel e dos agentes).
[Imagem: Diagrama arquitetônico simplificado]
A principal tarefa é criar uma política, detectar violações de políticas e mostrar ao usuário. Parece que não há nada de complicado - conectamos via gRPC ao Tetragon, ouvimos seus logs, encaminhamos do agente e salvamos no ClickHouse.
A tarefa é clara, as políticas foram feitas, as violações foram detectadas. Para preparar o recurso para lançamento, testamos em ambientes de desenvolvimento - tudo funciona.
Problemas em Produção
Infelizmente, durante os testes de carga em um cluster com vários nós, descobriu-se que a maior parte dos logs do Tetragon não chega ao ClickHouse.
Primeiro problema - um kernel que governa todos.
Encontramos o problema. O fato é que o Tetragon carrega programas eBPF no kernel Linux, escuta eventos e decide o que fazer com eles.
Durante o desenvolvimento local, usamos o minikube, que levanta um cluster Kubernetes leve em um único nó (uma única máquina virtual com uma instância do kernel Linux). Consequentemente, não foi possível verificar o trabalho em vários nós. E localmente, conectamo-nos a um pod Tetragon e ouvimos seus logs.
No ambiente de desenvolvimento, tínhamos não um K8s completo, mas seu irmão mais novo, K3s. Na compilação padrão, ele usa um banco de dados SQLite compartilhado, e não um etcd distribuído. Em um nó, o problema de perda de logs não pôde ser reproduzido - por mais que tentássemos. E quando implantamos um K8s multi-cluster real com etcd - ele apareceu imediatamente.
Existe um problema - começamos a corrigi-lo. A solução é bastante simples. Conectar a todas as instâncias do Tetragon em cada nó.
Tetragon funciona como um DaemonSet: um pod em cada nó. Fazemos list pods por seletor, obtemos a lista e conectamos a cada um separadamente.
Novo nó? DaemonSet levantou o pod, o agente o viu no próximo sync, adicionado ao pool.
Conectamos a todos os nós do cluster k8s.
Como resultado, obtivemos um código de trabalho que ajuda a detectar violações de políticas. Os testes de carga confirmaram isso.
Agora estamos realmente concluindo o artigo, obrigado pela atenção.
Spam de violações de políticas
Tudo bem, sou forçado a continuar, porque nem tudo é tão bom. Começamos a testar outros tipos de políticas. Se antes um log chegava para uma ação, agora - dois, três eventos idênticos. Inaceitável. Cavamos mais fundo.
Para entender por que as duplicatas apareceram, você precisa olhar sob o capô das políticas do Tetragon. Pegamos o comando mais simples touch file. Para nós, esta é uma ação. Para o kernel - duas chamadas de sistema: primeiro openat (criar arquivo), depois utimensat (definir o tempo). Na saída, obtemos dois logs. E dois logs não são o limite, pode haver muito.
Mostrar duplicatas como uma potencial ameaça à segurança é uma má abordagem. Você pode criar políticas que, em questão de minutos, em um cluster ativo, gerarão milhões de duplicatas por minuto.
[Imagem: Aparência de eventos duplicados]
Entendemos o problema - é hora de consertar. Fizemos a deduplicação por três sinais:
PID, tempo de recebimento e campos-chave
dos logs do Tetragon.
[Imagem: Deduplicação]
Novamente testes de carga, políticas diferentes, nós diferentes - tudo ótimo, obrigado a todos.
Deduplicação quebrada
Novamente, o testador veio e começou a dar carga. Ele mudou a abordagem. Antes, ele fazia spam de violações de diferentes PIDs. Agora ele decidiu fazer tudo dentro de um script python. E que azar - a deduplicação quebrou. Ele faz cem violações diferentes para políticas diferentes - nós as colamos em um. Problema. Novamente, vamos consertar, desta vez removemos o PID das chaves de deduplicação.
Tudo bem, mas agora dois processos diferentes no mesmo carimbo de data/hora fazem a mesma violação - e nós os colamos em um único evento. Em um cluster carregado, isso é normal.
Como resultado, depois de gastar muita energia, inventando muitos cenários de teste diferentes, alcançamos nosso objetivo. As políticas funcionam, o tempo de execução nos pods é rastreado, os infratores estão tristes, o usuário vê as violações em tempo real.
Conclusão
A principal dificuldade de desenvolvimento para Kubernetes não está na API nem no YAML, mas no fato de que o sistema é originalmente distribuído e isso deve ser sempre lembrado.
eBPF fornece eventos de baixo nível - na verdade, chamadas de sistema (por exemplo, open, execve, connect).
Mas as políticas de segurança são descritas em um nível mais alto:
"o processo dentro do contêiner não deve criar arquivos em /etc"
ou
"não deve executar o shell".
O problema é que uma dessas "ações" do usuário quase sempre se decompõe em várias chamadas de sistema. Por exemplo, um simples touch file se transforma em open e utimensat. Como resultado, há uma lacuna entre o nível da política e o nível do evento, que precisa ser colada de alguma forma.
Na verdade, ainda existem pontos interessantes relacionados à integração de políticas (que compartilharei em artigos futuros). Planejamos aumentar o rps em cerca de 8 vezes. Também resolvemos vários problemas de largura de banda devido à arquitetura incomum. Há uma compreensão do que e como fazer. Algum dia vou descrever essa história também.
Trabalhamos com segurança de contêineres há vários anos, na maioria das vezes olhamos para k8s. E assim que começa a parecer que chegamos ao fundo do poço, no bom sentido, alguém sempre bate de baixo. Temos que testar melhor, encontrar mais casos de canto, organizar brainstorms conjuntos. Mas nós gostamos disso!
Qualquer sistema que pareça simples em um ambiente único se transforma em um sistema distribuído no Kubernetes. O que significa que seu comportamento precisa ser verificado não no nível "funciona/não funciona", mas no nível "como ele se comporta com escalabilidade e concorrência".
📤 Compartilhar & Baixar
🧰 Ferramentas recomendadas
Divulgação: alguns links são patrocinados. Podemos receber comissão se você comprar — sem custo extra para você. Só indicamos o que faz sentido para a comunidade.