Containers não são o que você acha
Um processo Linux com visão limitada do sistema. Namespaces, cgroups, capabilities e seccomp — o que realmente acontece entre o docker run e seu app recebendo requests.

Um container é um processo Linux com visão limitada do sistema.
Roda no mesmo kernel, com as mesmas syscalls, na mesma máquina que todos os outros processos. Inclusive os de outras organizações.
Este post abre o que realmente acontece entre o docker run e o seu app recebendo requests.
O que acontece no docker run
Entre o comando e o seu app no ar, quatro mecanismos entram em ação:
Seu processo
│
├─ Namespaces → o que ele vê
├─ Cgroups → o que ele consome
├─ Capabilities → o que ele pode fazer
├─ Seccomp → como ele fala com o kernel
│
└─ Kernel Linux (compartilhado)
Nenhum deles foi criado pra containers. São primitivas do kernel combinadas até parecer isolamento.
Funcionam bem. Até não funcionarem.
Namespaces: isolamento por perspectiva
Namespaces não bloqueiam acesso. Eles mudam o que o processo enxerga.
PID — O processo acha que está sozinho. Vê só seus próprios processos. Um PID 1 que não é o PID 1 de verdade. O kernel mantém a árvore global — você só recebe um recorte. Se algo escapa do namespace, volta a ver tudo.
NET — Cada container ganha uma interface virtual. O tráfego sai por um par de interfaces (veth): uma dentro do container, outra no host. Quando você faz docker run -p 8080:80, o Docker cria regras de NAT. O container acha que tem rede própria. Mas todo pacote passa pelo kernel do host.
MNT — O filesystem é uma pilha de layers. Base read-only, topo descartável. Você escreve em /etc/hosts, mas não altera a imagem. Altera uma camada que morre com o container.
USER — UIDs são remapeados. Root dentro do container pode não ser root no host. Na teoria. Na prática, muita plataforma nem ativa isso. Root no container = root no host, com restrições.
Cgroups: limite, não proteção
Cgroups controlam consumo de recursos: quanta CPU, quanta memória, quanto I/O.
Se o processo ultrapassar o limite de memória: o OOM killer mata. Sem negociação.
CPU funciona diferente. --cpus=0.5 configura um quota de 50%. O processo continua rodando, mas throttled. Pra ele, parece que a CPU é lenta.
O ponto importante: cgroups não impedem acesso ao kernel. Se o processo explora uma vulnerabilidade no kernel, cgroups deixam de existir.
Capabilities: root fragmentado
Linux dividiu "root" em ~40 permissões granulares.
CAP_NET_BIND_SERVICE → portas < 1024
CAP_SYS_PTRACE → debug de processos
CAP_SYS_ADMIN → quase tudo
CAP_NET_RAW → raw sockets
CAP_CHOWN → mudar dono de arquivos
Docker remove várias por padrão.
A que importa mais: CAP_SYS_ADMIN. É tão ampla que, na prática, é quase root. Se um container tem CAP_SYS_ADMIN, pode montar filesystems e manipular namespaces — exatamente o que CVEs de container escape exploram.
Seccomp: filtro de syscalls
Seccomp intercepta syscalls antes de chegarem no kernel. Se a syscall não está na whitelist: falha ou o processo morre.
Docker bloqueia algumas dezenas de syscalls — mount, reboot, init_module.
O problema: exploits não usam syscalls exóticas. Usam read, write, ioctl. As mais comuns. As que não podem ser bloqueadas sem quebrar o container.
Onde o isolamento para
Cada camada faz algo importante: eleva o custo de ataque.
Mas todas dependem de uma premissa:
O kernel funcionar corretamente.
Se existe um bug explorável:
- Namespaces não seguram — são visão, não barreira
- Cgroups não importam — controlam recurso, não acesso
- Capabilities são contornáveis —
CAP_SYS_ADMINabre portas demais - Seccomp não vê — os exploits usam syscalls permitidas
O kernel Linux tem dezenas de milhões de linhas de código e recebe CVEs constantemente. O modelo de segurança de containers foi adicionado ao longo dos anos como extensão. Cada camada adiciona fricção significativa, mas o modelo depende da integridade do kernel compartilhado.
O que vem depois
Se você não quer depender só do kernel, existem abordagens que adicionam uma barreira real:
Sandbox em userspace — Syscalls são interceptadas e executadas por um kernel alternativo. Se alguém explora um bug, explora contra o sandbox, não contra o host.
MicroVMs — Cada container roda com kernel próprio. Isolamento real. Overhead pequeno o suficiente pra ser viável.
Nenhuma é bala de prata. Cada uma tem trade-offs reais de compatibilidade e performance.
Mas a pergunta que vale fazer antes de colocar qualquer workload com dados reais em produção:
Quantas camadas existem entre o meu processo e o kernel do host?
Se a resposta for "namespace + cgroup + seccomp", agora você sabe exatamente o que isso significa.
Esse post foi sobre o que já existe. O próximo é sobre o que a gente empilhou por cima — porque quatro camadas não são suficientes quando o kernel é compartilhado.
Referências: Quarkslab — Digging into Linux Namespaces · LWN — Namespaces API · Edera — What Engineers Should Know About Container Isolation · Wiz — Container Security · SCHUTZWERK — Linux Container Namespaces · Kernel Docs — unshare() · Red Hat — Capabilities and Seccomp
O que a gente construiu por cima das quatro camadas