Executando Docker, docker build e docker compose sem Root ou User Namespaces

Solução Rápida: Docker Seguro e Sem Privilégios em Três Etapas
Se você estiver trabalhando dentro de um contêiner Linux restrito, de um runner de CI, de um Pod do Kubernetes ou de um sandbox de agente de IA em que root, sudo, user namespaces (unshare(CLONE_NEWUSER)), mount(2) e /dev/fuse estão bloqueados, o gvx (rungv) é a solução segura e com defesa em profundidade para executar um mecanismo docker:dind completo no espaço do usuário.
Por que o
gvxÉ Seguro por Design (Não É um Contorno de Escalonamento de Privilégios): Diferentemente de montar o/var/run/docker.sockdo host ou de executardocker run --privileged(que concedem controle equivalente ao de root sobre o kernel do host), ogvxnão contorna nem enfraquece nenhuma política de segurança do host (PR_SET_NO_NEW_PRIVS,seccomp, AppArmor ou os limites de UID não root). Em vez disso, orungvrestringe seu próprio processo hospedeiro (PR_SET_NO_NEW_PRIVS+ allowlist estrita de BPFseccomp) e executa odockerddentro do kernel de espaço do usuário gVisor (Sentry), da Google, que é seguro em relação à memória. Todas as capacidades doUID 0do guest, as montagensoverlayfs(ext4lite) e as regras deiptablespermanecem estritamente isoladas dentro do processo do sandbox.
1# 1. Instale o rungv no espaço do usuário (binário pré-compilado com atestação SLSA ou go install)
2# Opção A: Verifique e extraia o binário pré-compilado com atestação SLSA das Releases do GitHub
3gh attestation verify rungv-v0.2.0-linux-amd64.tar.gz --repo gfire-sigs/gvx \
4 --signer-workflow gfire-sigs/gvx/.github/workflows/release.yml \
5 --source-ref refs/tags/v0.2.0 --deny-self-hosted-runners
6tar -xzf rungv-v0.2.0-linux-amd64.tar.gz && install -m 0755 rungv-v0.2.0-linux-amd64/rungv ~/.local/bin/rungv
7
8# Opção B: Ou instale diretamente a partir do código-fonte com Go
9CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest
10
11# 2. Inicie o docker:dind dentro do gvx com um volume ext4lite de arquivo único
12# Encaminha ~/.gvx/docker.sock (modo 0600) para a porta 2375 do guest
13mkdir -p ~/.gvx
14~/.local/bin/rungv run \
15 -p unix:~/.gvx/docker.sock:2375 \
16 -p 127.0.0.1:9000-9019:9000-9019 \
17 -v gvx-docker:/var/lib/docker \
18 -e DOCKER_TLS_CERTDIR= \
19 docker:dind --tls=false &
20
21# 3. Direcione a CLI padrão do Docker e o Docker Compose para o socket do espaço do usuário
22export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
23until docker info >/dev/null 2>&1; do sleep 0.5; done
24docker info --format '{{.Driver}} {{.OperatingSystem}}'
25# Saída: overlayfs Alpine Linux ... (em contêiner)
26
27docker build -t app:test .
28docker run --rm app:test
29docker compose up -d
Por Que as Ferramentas Rootless Padrão Falham em Sandboxes Restritos
Quando um engenheiro ou um agente autônomo de programação precisa executar docker build, docker run ou docker compose dentro de um contêiner reforçado ou de um runner multilocatário, as soluções alternativas padrão falham em cinco barreiras distintas do kernel.
1. Ausência do Socket do Daemon Docker do Host
Executar docker ps ou docker build falha imediatamente quando o /var/run/docker.sock do host não está montado no ambiente:
1Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
2. PR_SET_NO_NEW_PRIVS Bloqueia sudo e Auxiliares Setuid
Contêineres reforçados (securityContext.allowPrivilegeEscalation: false no Kubernetes, --security-opt=no-new-privileges no Docker, ou sandboxes exebox / bwrap) definem o bit PR_SET_NO_NEW_PRIVS do Linux na árvore de processos. A tentativa de escalonamento por meio de sudo ou de binários setuid (newuidmap, newgidmap, fusermount3) falha:
1sudo: The "no new privileges" flag is set, which prevents sudo from running as root.
2sudo: effective uid is not 0, is /usr/bin/sudo on a file system with the 'nosuid' option set or an NFS file system without root privileges?
3. Políticas de Seccomp e Sysctl Bloqueiam unshare(CLONE_NEWUSER)
Ferramentas apresentadas como "rootless" (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap) ainda dependem de user namespaces do Linux (CLONE_NEWUSER). Elas falham sempre que:
- O perfil padrão de seccomp do contêiner externo bloqueia
unshare(2)ouclone(CLONE_NEWUSER). - O Ubuntu 24.04+ habilita
kernel.apparmor_restrict_unprivileged_userns = 1. - Nós Debian ou RHEL definem
kernel.unprivileged_userns_clone = 0ouuser.max_user_namespaces = 0. - O
newuidmapnão consegue escrever em/proc/self/uid_mapporquePR_SET_NO_NEW_PRIVSestá ativo.
Os erros resultantes têm a seguinte aparência:
1unshare: unshare failed: Operation not permitted
2cannot clone: Operation not permitted
3Error: cannot re-exec process to join the existing user namespace
4newuidmap: write to uid_map failed: Operation not permitted
5RootlessKit: parent: failed to start the child: fork/exec /proc/self/exe: operation not permitted
4. mount(2) É Bloqueado Sem CAP_SYS_ADMIN no Host
Mesmo que um ambiente permita CLONE_NEWUSER, montar um /proc novo dentro de um mount namespace aninhado falha se o contêiner externo tiver mascarado subárvores de /proc (/proc/acpi, /proc/kcore, /proc/keys) ou removido mount(2) do seccomp:
1mount("/proc", "/proc", "proc", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL): Operation not permitted
2OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "proc" to rootfs at "/proc": mount proc:/proc (via /proc/self/fd/6), flags: 0xe: operation not permitted
3bwrap: Creating new namespace failed: Operation not permitted
5. A Ausência de /dev/fuse Quebra o fuse-overlayfs
Mecanismos de contêiner rootless que não conseguem montar o overlayfs do kernel recorrem ao fuse-overlayfs. Em contêineres mínimos ou em Pods do Kubernetes sem repasse do dispositivo /dev/fuse, o fuse-overlayfs falha imediatamente:
1fuse: device not found, try 'modprobe fuse' first
2fusermount3: failed to open /dev/fuse: No such file or directory
3error initializing graphdriver: driver not supported: fuse-overlayfs
Como o gvx Executa o docker:dind Inteiramente no Espaço do Usuário
O gvx contorna as cinco barreiras do kernel porque não solicita ao kernel Linux do host que crie namespaces, monte sistemas de arquivos ou configure interfaces de rede. Em vez disso, o rungv executa um kernel Linux interno de espaço do usuário (o Sentry do gVisor) e uma pilha de rede de espaço do usuário (netstack) dentro de um único processo comum e sem privilégios.

1. Namespaces em Memória e Tabela de Montagem (VFS2)
Quando dockerd, containerd e runc são executados dentro do rungv, o systrap intercepta as suas chamadas de sistema e as encaminha para o kernel Sentry em processo, e não para o kernel do host:
unshare(2),clone(2),pivot_root(2),chroot(2)emount(2)operam sobre a hierarquia de tarefas em memória doSentrye sobre a tabela de montagemVFS2(procfs,sysfs,tmpfs,devpts,cgroupfsv2), enquanto o processo hospedeirorungvé executado sob o seu UID não privilegiado no host.
2. overlayfs Nativo em Volumes ext4lite de Arquivo Único
Quando você passa -v gvx-docker:/var/lib/docker (ou --mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker), o gvx monta seu sistema de arquivos integrado ext4lite em /var/lib/docker.
Como o ext4lite implementa um sistema de arquivos ext4 com journal JBD2 dentro de um único arquivo do host (gvx-docker.ext4), com suporte aos atributos estendidos trusted.overlay.*, a dispositivos de whiteout de caracteres (0:0), a d_type e a RENAME_EXCHANGE, o dockerd utiliza seu driver de armazenamento nativo overlayfs dentro do guest. Operações de docker build em múltiplos estágios compartilham camadas Copy-on-Write sem /dev/fuse e sem a explosão no consumo de disco do driver de fallback vfs do Docker.
3. Bridge docker0 no Espaço do Usuário e iptables-legacy
Quando o docker:dind é iniciado dentro do gvx:
- O
dockerddo guest cria a bridgedocker0(172.17.0.1/16), anexa paresvethvirtuais para os contêineres aninhados e programa as cadeiasNAT(DOCKER,POSTROUTING) eFILTERcom correspondênciasaddrtypeeconntrackdentro donetstackdogvx, no espaço do usuário. - Passar
-p unix:~/.gvx/docker.sock:2375vincula um socket de domínio Unix do host (modo0600) e encaminha as conexões diretamente para a porta2375dodockerddentro do sandbox.
Comparação com Ferramentas Existentes de Contêineres
| Capacidade / Restrição | Docker Padrão (dockerd) | Docker Rootless / Podman | Kaniko / Buildah (Sem Privilégios) | gvx (rungv + docker:dind) |
|---|---|---|---|---|
Requer root ou sudo no host | Sim | Não | Não | Não |
Funciona com PR_SET_NO_NEW_PRIVS=1 | Não | Não (newuidmap falha) | Parcial | Sim |
Funciona quando unshare(CLONE_NEWUSER) é bloqueado (EPERM) | N/A | Não | Buildah: Não / Kaniko: Sim | Sim |
Funciona sem /dev/fuse | Sim (usa overlay2 do kernel) | Somente via vfs lento | Sim | Sim (overlayfs sobre ext4lite) |
Oferece suporte a docker build (BuildKit) | Sim | Sim (se userns permitido) | Somente build de imagem (sem runtime) | Sim |
Oferece suporte a docker run e docker compose up | Sim | Sim (se userns permitido) | Não | Sim |
| Executa em Windows e macOS sem uma VM Linux | Não (requer WSL2 / VM) | Não (requer VM Linux) | Não | Sim (whpx, hvf, systrap) |
Requisitos Exatos do Kernel do Host no Linux
O gvx não requer capacidades elevadas (CapEff: 0000000000000000) nem user namespaces. Em um host Linux, o rungv depende de apenas três primitivas padrão e sem privilégios do kernel:
ptrace(PTRACE_ATTACH)na sua própria thread filha, uma única vez durante a inicialização: osystrapcria uma thread auxiliar de stub local na inicialização, anexa-se a ela para inicializar o contexto de memória compartilhada e se desanexa. Se o host definirkernel.yama.ptrace_scope = 3(o que desativa todo optraceem todo o sistema) ou bloquearptraceem um filtro seccomp externo, a inicialização dosystrapnão pode ser concluída. (Comyama.ptrace_scope = 1, o padrão no Ubuntu e no Debian, ogvxfunciona porque o rastreamento de pai para filho é permitido.)seccomp(SECCOMP_SET_MODE_FILTER): usado para interceptar chamadas de sistema do guest originadas de threads stub do contêiner. Conforme abordado na Parte 3 desta série, ogvxfunciona tanto quandoSECCOMP_FILTER_FLAG_NEW_LISTENERestá disponível quanto quando retornaEBUSYdentro de um sandbox aninhado.mmap(..., PROT_EXEC, ...): usado para mapear segmentos ELF do guest e o trampolim de memória compartilhada dosystrap.
Exemplo Completo: Construção e Teste de um Aplicativo Multicontêiner Sem Root
O script a seguir executa um docker build completo e um teste de integração com dois contêineres (redis + contêiner da aplicação) dentro de uma sessão de usuário sem privilégios:
1mkdir -p ~/.gvx
2rungv run \
3 -p unix:~/.gvx/docker.sock:2375 \
4 -v gvx-docker:/var/lib/docker \
5 -e DOCKER_TLS_CERTDIR= \
6 docker:dind --tls=false &
7DIND_PID=$!
8
9export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
10until docker info >/dev/null 2>&1; do sleep 0.5; done
11
12mkdir -p /tmp/demo-app && cd /tmp/demo-app
13cat <<'EOF' > Dockerfile
14FROM alpine:3.20
15RUN apk add --no-cache redis
16CMD ["redis-cli", "-h", "cache", "ping"]
17EOF
18
19# Construa a imagem usando overlayfs sobre ext4lite
20docker build -t demo-ping:latest .
21
22# Crie uma rede bridge, inicie o Redis e verifique a conectividade entre os contêineres
23docker network create demo-net
24docker run -d --name cache --network demo-net redis:7-alpine
25docker run --rm --network demo-net demo-ping:latest
26# Saída: PONG
27
28# Remova os contêineres de carga de trabalho e encerre o sandbox
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID
Leitura Complementar Nesta Série
- gvx: Executando Contêineres Linux, Docker e Kubernetes em Um Único Processo Sem Privilégios: visão geral da arquitetura do
gvx, seus mecanismos de execução multiplataforma (whpx,hvf,systrap), oVFS2(erofs,ext4lite,hostfsv2) e onetstack. - Executando Docker, docker build e docker compose Sem Root ou User Namespaces (esta postagem)
- Executando Docker-in-Docker Dentro do gVisor e de Sandboxes Aninhados com o gvx: como o
gvxresolvemodprobe,iptables-nft,overlay2sobregofere oSECCOMP_FILTER_FLAG_NEW_LISTENERaninhado (EBUSY) dentro dogVisor(runsc). - Ambientes Docker Isolados e Rápidos para Rollouts de RL de Agentes de IA com o gvx: escalando ambientes paralelos de rollout de
SWE-benche RL com resets de volumes.ext4Copy-on-Write em 100 milissegundos.