Container não é isolamento
Container é um processo fantasiado. Trate como fronteira de segurança e uma hora você vai se arrepender.
Em algum momento, 'roda num container' virou sinônimo de 'está isolado'. Eu ouço isso em revisão de design quando alguém propõe rodar código não confiável, plugin de terceiro ou script enviado pelo cliente. O container é oferecido como rede de segurança. Não é. Ou melhor, é uma rede com buracos bem documentados e fáceis de esquecer.
O que um container realmente é
Container é um processo comum no host, iniciado com algumas features do kernel que restringem o que ele enxerga. Namespaces dão a ele uma visão própria da tabela de processos, das interfaces de rede, dos mounts, do hostname. Control groups limitam quanto de CPU e memória ele consome. Um conjunto reduzido de capabilities tira algumas das coisas que root normalmente pode fazer. Um perfil seccomp filtra quais syscalls ele pode chamar.
Esse é o truque inteiro. O processo continua compartilhando o kernel do host com todos os outros containers. Cada syscall que ele faz é tratada pelo mesmo kernel que atende o banco ao lado. Um bug de kernel alcançável de dentro de um container é um bug de kernel alcançável no host. Máquina virtual coloca um hipervisor entre o guest e o hardware. Container coloca uma tabela de regras entre o processo e o kernel. São distâncias diferentes.
Os buracos que não são bugs
A parte perigosa é que a maioria dos escapes de container que vejo em auditoria não são exploits. São configuração. Um container iniciado com a flag privileged tem essencialmente todas as capabilities de root no host, e as pessoas usam essa flag para fazer algo funcionar sem ler o que ela faz. Um container que monta o socket do runtime pode iniciar novos containers com qualquer configuração, inclusive privilegiada. Um container que roda como root por dentro, num host sem remapeamento de user namespace, é root em qualquer arquivo que alcance por um mount.
Depois vêm os buracos mais sutis. Container sem limite de memória pode sufocar o host. Container sem limite de PID pode derrubar o nó com fork bomb. Container que alcança o endpoint de metadata do provedor de nuvem geralmente consegue pegar as credenciais do papel da instância, que costumam ser bem mais poderosas do que a aplicação precisa. Nenhum desses exige escapar do container. Exigem só que o container tenha permissão para fazer coisas que ele nunca precisou fazer.
O que eu checo na prática
- O processo interno roda como usuário não-root, e a imagem é construída de um jeito que não permita virar root.
- O filesystem raiz é montado como somente leitura, com volumes graváveis explícitos só onde é preciso.
- Todas as capabilities são removidas e só as específicas necessárias são devolvidas, o que normalmente é nenhuma.
- Limites de CPU, memória e PID estão definidos, e o limite de memória foi testado contra picos reais de carga.
- Sem path do host, sem socket do runtime, sem host network, sem flag privileged, e o endpoint de metadata bloqueado a menos que a carga realmente precise.
Essa lista não é exótica. É a recomendação padrão de todo guia de hardening. Também é violada na maioria dos sistemas que reviso, geralmente porque a imagem base roda como root e ninguém mudou, ou porque um passo de build precisou de uma capability uma vez e ela nunca foi removida.
Quando você realmente precisa de isolamento
Se a carga é o seu próprio código, containers endurecidos em infraestrutura gerenciada são uma fronteira razoável. O modelo de ameaça é uma dependência comprometida ou um bug no seu serviço, e o hardening acima contém a maior parte do que esse comprometimento consegue fazer.
Se a carga é código que você não escreveu e não confia, ou seja, script de cliente, plugin, código gerado ou qualquer coisa vinda de uma feature de sandbox multi-tenant, container não basta. Aí é hora de buscar uma fronteira real: uma microVM por carga, um runtime sandboxed projetado para código não confiável ou um host separado por tenant. Custam mais e sobem mais devagar. Esse é o preço do isolamento de verdade que a palavra 'container' sempre insinuou.
A regra que dou aos times é simples. Container é ferramenta de empacotamento e agendamento com limites de recurso úteis. Não é fronteira de segurança entre partes que não confiam uma na outra. Projete de acordo, e não vai ser você explicando como o upload de um cliente leu o ambiente de outro.