Protegendo o Docker em uma máquina Linux
O acesso ao grupo Docker concede privilégios de root. Proteja o Docker no Linux com modo rootless, regras sudo, Podman, contêineres reforçados e checklist de auditoria.
Adicionar um usuário ao grupo docker dá a ele acesso root à máquina. Não é “quase root”, é root completo: qualquer pessoa que consiga se comunicar com o socket do Docker pode montar o sistema de arquivos do host em um contêiner e editá-lo.
Você executou usermod -aG docker $USER para não precisar mais digitar sudo, e o próprio guia de pós-instalação do Docker mandou você fazer isso. A mesma página de pós-instalação do Docker Engine no Linux também avisa, em um quadro que a maioria das pessoas lê por cima, que pertencer a esse grupo equivale a ter root. Se você ainda está se familiarizando com o básico, o guia para iniciantes sobre imagens e contêineres Docker cobre esses conceitos. Este artigo demonstra o risco com um único comando. Em seguida, apresenta as correções da mais fraca para a mais forte e termina com um checklist para auditar uma máquina que você herdou.
Principais conclusões
- Qualquer membro do grupo
docker, e qualquer processo executado como esse membro, pode obter um shell root no host comdocker run --rm -it -v /:/host alpine chroot /host sh. - O Docker rootless executa tanto o
dockerdquanto os seus contêineres dentro de um user namespace. Assim, um comprometimento do daemon ou uma fuga de contêiner resulta em um UID sem privilégios no host, e não em root. - Uma regra restrita no sudoers para
/usr/bin/dockercontinua equivalente a root, mas exige senha para usar o Docker e registra cada uso em log. - O Podman não tem daemon e roda em modo rootless por padrão quando usado por um usuário comum. Como sua CLI é em grande parte compatível com a do Docker,
alias docker=podmanatende à maioria dos fluxos de trabalho do dia a dia. - Em um host herdado, verifique quem está no grupo
docker, qual usuário é dono dodockerd, as permissões do socket e se o daemon escuta na porta TCP 2375.
Por que o grupo docker concede root?
O grupo docker equivale a root porque o daemon do Docker é executado como root e o grupo controla o acesso de escrita ao seu socket Unix. Poder escrever nesse socket significa poder mandar um processo root fazer qualquer coisa. Se quiser entender o mecanismo por trás disso, o guia de permissões de arquivos no Linux explica como as permissões de grupo de um arquivo determinam quem pode abri-lo. No caso do Docker, o único detalhe que importa é que o grupo docker tem permissão de escrita no socket.
Esta é a prova:
docker run --rm -it -v /:/host alpine chroot /host sh
O comando faz um bind mount do / do host dentro do contêiner e depois executa chroot nele. Você passa a ter um shell root no sistema de arquivos real do host. A partir daí, pode editar /etc/shadow ou /etc/sudoers, ou adicionar chaves SSH em /root. Não é preciso senha nem exploit. Qualquer script, dependência ou extensão de editor comprometida que rode com o seu usuário pode fazer o mesmo. A documentação do Docker trata disso na seção superfície de ataque do daemon do Docker.
Configure o Docker rootless
O Docker rootless é um modo que executa o dockerd e todos os contêineres dentro de um user namespace pertencente à sua conta, o que o torna a correção direta para o problema do grupo docker. O socket fica em $XDG_RUNTIME_DIR/docker.sock, e não em /var/run/docker.sock. O RootlessKit cria o namespace. Os auxiliares setuid newuidmap e newgidmap mapeiam o UID 0 do namespace para o seu próprio UID e mapeiam um intervalo definido em /etc/subuid e /etc/subgid para os demais UIDs do contêiner. A rede passa por uma pilha de rede em modo usuário, como slirp4netns, gvisor-tap-vsock ou pasta.
Instale os pré-requisitos e depois verifique seus intervalos de IDs subordinados. A documentação do modo rootless exige um intervalo de pelo menos 65.536 IDs subordinados para o seu usuário, tanto em /etc/subuid quanto em /etc/subgid.
sudo apt-get install -y uidmap docker-ce-rootless-extras
grep ^$(whoami): /etc/subuid /etc/subgid
Desative o daemon root e, em seguida, execute a ferramenta de configuração com o seu usuário comum:
sudo systemctl disable --now docker.service docker.socket
dockerd-rootless-setuptool.sh install
systemctl --user enable --now docker
sudo loginctl enable-linger $USER
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
A linha com enable-linger mantém o seu daemon em execução depois que você faz logout. Quando o Docker rootless estiver funcionando, remova-se do grupo antigo com sudo gpasswd -d $USER docker. A documentação oficial do modo rootless aborda outras distribuições. Se o cliente não conseguir alcançar o daemon depois disso, o artigo sobre solução de erros “Docker daemon not running” mostra as verificações de socket e systemd.
O modo rootless precisa de user namespaces sem privilégios. O Ubuntu 24.04 e versões posteriores os restringem por padrão via AppArmor. O pacote apparmor do Ubuntu já inclui o perfil de que o rootlesskit precisa, então instalar o docker-ce-rootless-extras via apt funciona sem etapas extras. Se, em vez disso, você instalar com o script get.docker.com/rootless, precisará adicionar esse perfil manualmente. Não desative a restrição para todo o sistema com kernel.apparmor_restrict_unprivileged_userns=0. A página de solução de problemas do modo rootless cobre os demais casos de falha.
Qual é o custo do modo rootless?
Estas são as principais concessões em comparação com um daemon rootful:
| Limitação | Alternativa |
|---|---|
| Portas abaixo de 1024 não podem ser usadas por padrão | sysctl net.ipv4.ip_unprivileged_port_start=80 ou um proxy reverso |
--privileged não concede privilégios reais no host | Apenas as capabilities dentro do user namespace se aplicam |
| A rede em modo usuário é mais lenta que o driver bridge | O driver experimental lxc-user-nic, ou faça benchmarks antes de ir para produção |
| Kernels anteriores ao 5.11 precisam de fuse-overlayfs | O kernel 5.11+ suporta overlay2 nativo |
A página de solução de problemas lista mais algumas, como a ausência de AppArmor, de checkpoints e de redes overlay.
Use sudo com uma regra restrita
Exigir sudo para o Docker, em vez de usar o grupo docker, é o meio-termo quando o modo rootless quebra a sua carga de trabalho. Continua sendo equivalente a root, porque sudo docker pode executar o mesmo comando chroot. O que você ganha é uma solicitação de senha e um registro de auditoria para cada chamada. Código executado silenciosamente com o seu usuário deixa de ter acesso ao socket.
sudo gpasswd -d alice docker
sudo visudo -f /etc/sudoers.d/docker
alice ALL=(root) /usr/bin/docker
Essa regra permite apenas o binário do Docker, e não comandos arbitrários. Revise o uso com journalctl _COMM=sudo. A documentação de pós-instalação também descreve um método com newgrp que exige uma senha de grupo para usar o Docker, enquanto a CLI continua sendo executada com o seu próprio usuário.
Migre para o Podman
O Podman não tem daemon e, quando executado por um usuário comum, é rootless por padrão. Os contêineres rodam como processos filhos do seu usuário, dentro de um user namespace, usando os mesmos mapeamentos de /etc/subuid. Sua CLI espelha a do Docker de forma tão próxima que a maioria dos comandos funciona sem alterações.
sudo apt-get install -y podman
alias docker=podman
podman run --rm -p 8080:80 docker.io/library/nginx
As diferenças aparecem nos detalhes: nomes de imagem totalmente qualificados, suporte ao Compose e ferramentas que esperam um socket do Docker. Teste seus scripts antes de migrar uma equipe inteira.
Fortaleça o próprio contêiner
Seja qual for o daemon que você usa, limite o que o processo dentro do contêiner pode fazer. Quatro configurações cobrem a maior parte do risco: um USER não root, nenhuma capability além das que a aplicação precisa, um sistema de arquivos raiz somente leitura e nenhuma escalada de privilégios por meio de binários setuid.
FROM node:22-alpine
WORKDIR /app
COPY --chown=node:node . .
USER node
CMD ["node", "server.js"]
docker run -d \
--cap-drop=ALL \
--read-only --tmpfs /tmp \
--security-opt no-new-privileges \
-p 8080:8080 myapp
A instrução USER do Dockerfile define o UID em tempo de execução. --cap-drop=ALL remove todas as capabilities. Se uma aplicação realmente precisar de alguma de volta, adicione-a com --cap-add. Esta aplicação escuta na porta 8080, então não precisa de nenhuma. --read-only combinado com --tmpfs faz com que apenas /tmp seja gravável. no-new-privileges impede que binários setuid elevem privilégios. Todas essas opções estão documentadas na referência do docker run.
Audite uma máquina herdada
Em uma VPS configurada por outra pessoa, responda a quatro perguntas: quem está no grupo docker, qual usuário executa o dockerd, quem pode escrever no socket e se o daemon escuta na rede.
getent group docker
ps -eo user,cmd | grep [d]ockerd
ls -l /var/run/docker.sock
sudo ss -ltnp | grep -E ':2375|:2376'
systemctl cat docker | grep -- '-H'
grep -s hosts /etc/docker/daemon.json
Todo nome exibido pelo getent tem acesso root. Se o ps mostrar root, o daemon é rootful. O socket deve aparecer como srw-rw---- root docker, e qualquer permissão de escrita para todos exige correção imediata. Um listener na porta 2375 significa que a API do Docker está exposta sem TLS. As versões atuais do Docker Engine se recusam a iniciar com essa configuração, mas uma engine mais antiga em um host herdado ainda pode estar rodando assim. Qualquer pessoa que consiga alcançar essa porta tem root. Um listener TLS na porta 2376 é mais seguro, mas quem tiver as chaves do cliente continua tendo root. Siga o guia de proteção do socket do daemon para migrar para SSH ou TLS mútuo. Verifique também se há contêineres que montam o socket, já que cada um deles é uma porta de entrada para root:
docker ps -q | xargs -r docker inspect \
--format '{{.Name}} {{range .Mounts}}{{.Source}} {{end}}' | grep docker.sock
Acesso ao socket do Docker é acesso root, ponto final. Comece executando getent group docker em cada máquina que você usa. Depois, migre esses usuários para o Docker rootless ou para o Podman, ou coloque o socket atrás de uma regra sudo com registro em log.
Perguntas frequentes
Qual é a diferença entre o Docker rootless e o userns-remap?
Com o userns-remap, o dockerd continua sendo executado como root e apenas os contêineres rodam com UIDs subordinados remapeados, como o usuário dockremap criado ao definir userns-remap como 'default' em /etc/docker/daemon.json. Isso não fecha a brecha do grupo docker: um membro do grupo pode passar --userns=host para desativar o remapeamento em um contêiner e executar a fuga via chroot. O modo rootless executa o próprio daemon com o seu usuário.
O Docker rootless e o rootful podem rodar na mesma máquina?
Sim. Os dois daemons usam sockets separados: /var/run/docker.sock para o rootful e $XDG_RUNTIME_DIR/docker.sock para o rootless. Executar dockerd-rootless-setuptool.sh install cria um contexto de CLI chamado 'rootless' e muda para ele. Use docker context use default para apontar para o daemon root e docker context use rootless para voltar. Enquanto o daemon rootful continuar em execução, qualquer pessoa que consiga escrever no socket dele ainda terá root.
Minhas imagens e contêineres existentes são transferidos para o Docker rootless?
Não. O daemon rootless mantém seu próprio diretório de dados, ~/.local/share/docker por padrão, separado do diretório /var/lib/docker usado pelo daemon root. Imagens, contêineres e volumes criados no Docker rootful não ficam visíveis para ele. Faça o pull das imagens novamente, ou exporte-as com docker save a partir do daemon rootful e importe-as com docker load no rootless. Atualize também os jobs de backup que cobrem apenas /var/lib/docker.
Por que arquivos gravados por contêineres rootless aparecem com donos estranhos no host?
O Docker rootless mapeia os UIDs do contêiner para UIDs do host. O root dentro do contêiner é mapeado para o seu próprio UID, então os arquivos que ele grava em um bind mount aparecem como seus. Todos os outros UIDs do contêiner são mapeados para o seu intervalo em /etc/subuid. Com um intervalo que começa em 100000, o UID 1000 do contêiner se torna o UID 100999 no host, que o seu usuário não consegue editar diretamente. Executar chown para 0:0 dentro de um contêiner devolve a posse dos arquivos ao seu usuário.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k