GoSuda

Köra Docker, docker build och docker compose utan root eller användarnamnrymder

By Lemon Mint views ...

Snabblösning: Säker Docker utan privilegier i tre steg

Om du arbetar i en låst Linux-container, en CI-runner, en Kubernetes Pod eller en sandlåda för AI-kodagenter (till exempel en molnbaserad agentberäkningsmiljö) där root, sudo, användarnamnrymder (unshare(CLONE_NEWUSER)), mount(2) och /dev/fuse är blockerade, är gvx (rungv) den säkra, lågansträngda och tokeneffektiva lösningen för att köra en fullständig docker:dind-motor i användarutrymme.

Lågansträngd och tokeneffektiv lösning för AI-kodagenter: När en autonom kodagent stöter på Cannot connect to the Docker daemon slösar försök att installera och patcha docker.io, podman eller rootlesskit inuti en begränsad container vanligtvis 15 till 30 verktygsanrop och tiotusentals kontexttokens på att cirkulera mellan fel i systemctl, ulimit, iptables, unshare och runc, för att sedan misslyckas eller falla tillbaka på att skriva om Dockerfiles för hand. Med gvx (rungv) kör agenten en deterministisk 3-stegs-bootstrap i en enda tur och kör omedelbart standardkommandona docker build, docker run och docker compose oförändrade.

Varför gvx är säker by design (inte en workaround för privilegieeskalering): Till skillnad från att montera värdens /var/run/docker.sock eller köra docker run --privileged (vilket ger root-motsvarande kontroll över värdens kärna) kringgår eller försvagar inte gvx någon säkerhetspolicy på värden (PR_SET_NO_NEW_PRIVS, seccomp, AppArmor eller begränsningar för icke-root-UID). I stället låser rungv fast sin egen värdprocess (PR_SET_NO_NEW_PRIVS + en strikt BPF-seccomp-allowlist) och kör dockerd inuti Googles minnessäkra gVisor (Sentry)-kärna i användarutrymme. Alla gästens UID 0-förmågor, overlayfs-monteringar (ext4lite) och iptables-regler förblir strikt isolerade inom sandlådeprocessen.

 1# 1. Installera rungv i användarutrymme (förkompilerad SLSA-attesterad release eller go install)
 2# Alternativ A: Verifiera och extrahera den SLSA-attesterade förkompilerade binären från GitHub Releases
 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# Alternativ B: Eller installera direkt från källkod med Go
 9CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest
10
11# 2. Starta docker:dind inuti gvx med en enfils-ext4lite-volym
12#    Vidarebefordrar ~/.gvx/docker.sock (läge 0600) till gästens port 2375
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. Peka standard-Docker-CLI och Docker Compose mot användarutrymmessocketen
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# Utdata: overlayfs Alpine Linux ... (containerized)
26
27docker build -t app:test .
28docker run --rm app:test
29docker compose up -d

Varför standardverktyg för rootless-körning misslyckas i begränsade sandlådor

När en ingenjör eller autonom kodagent behöver köra docker build, docker run eller docker compose inuti en härdad container eller en multi-tenant-runner, misslyckas vanliga lösningar vid fem distinkta kärnbarriärer.

1. Ingen värd-Docker-daemonsocket (systemctl och service docker start fungerar också inte)

Att köra docker ps eller docker build misslyckas omedelbart när värdens /var/run/docker.sock inte är monterad i miljön. Att försöka starta docker.io via systemctl eller service docker start misslyckas eftersom PID 1 inte är systemd och /etc/init.d/docker träffar containerns hårda ulimit -n-tak:

1Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
2ERROR: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
3System has not been booted with systemd as init system (PID 1). Can't operate.
4Failed to connect to bus: Host is down
5/etc/init.d/docker: 62: ulimit: error setting limit (Operation not permitted)

2. PR_SET_NO_NEW_PRIVS blockerar sudo och setuid-hjälpare

Härdade containers (securityContext.allowPrivilegeEscalation: false i Kubernetes, --security-opt=no-new-privileges i Docker eller exebox- eller bwrap-sandlådor) sätter biten Linux PR_SET_NO_NEW_PRIVS på processträdet. Försök att eskalera via sudo eller setuid-binärer (newuidmap, newgidmap, fusermount3) misslyckas:

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. Seccomp- och sysctl-policyer blockerar unshare(CLONE_NEWUSER)

Verktyg som marknadsförs som "rootless" (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap) förlitar sig fortfarande på Linux användarnamnrymder (CLONE_NEWUSER). De misslyckas när:

  • Den yttre containerns standardprofil för seccomp blockerar unshare(2) eller clone(CLONE_NEWUSER).
  • Ubuntu 24.04 och senare aktiverar kernel.apparmor_restrict_unprivileged_userns = 1.
  • Debian- eller RHEL-noder sätter kernel.unprivileged_userns_clone = 0 eller user.max_user_namespaces = 0.
  • newuidmap inte kan skriva till /proc/self/uid_map eftersom PR_SET_NO_NEW_PRIVS är aktivt.

Felen ser ut så här:

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) och pivot_root(2) blockeras under docker build utan värdens CAP_SYS_ADMIN

Även om en miljö tillåter CLONE_NEWUSER eller dockerd tvingas starta med --iptables=false --bridge=none --storage-driver=vfs, misslyckas docker build vid den första RUN-instruktionen, eftersom montering av en ny /proc eller anrop av pivot_root(2) inuti en kapslad monteringsnamnrymd blockeras:

1ERROR: failed to solve: process "/bin/sh -c ..." did not complete successfully:
2mount("/proc", "/proc", "proc", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL): Operation not permitted
3OCI 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
4OCI runtime create failed: runc create failed: unable to start container process: error during container init: error jailing process inside rootfs: pivot_root .: invalid argument
5bwrap: Creating new namespace failed: Operation not permitted

5. Avsaknad av /dev/fuse bryter fuse-overlayfs

Rootless-containermotorer som inte kan montera kärnans overlayfs faller tillbaka på fuse-overlayfs. I minimala containers eller Kubernetes-pods utan genomslussning av enheten /dev/fuse misslyckas fuse-overlayfs omedelbart:

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

Hur gvx kör docker:dind helt i användarutrymme

gvx kringgår alla fem kärnbarriärerna eftersom det inte ber värdens Linux-kärna att skapa namnrymder, montera filsystem eller konfigurera nätverksgränssnitt. I stället kör rungv en intern Linux-kärna i användarutrymme (gVisor Sentry) och en nätverksstack i användarutrymme (netstack) inuti en vanlig, icke-privilegierad process.

Kör Docker-in-Docker utan privilegier med gvx

1. Namnrymder och monteringstabell i minnet (VFS2)

När dockerd, containerd och runc körs inuti rungv fångar systrap upp deras systemanrop och dirigerar dem till den processinterna Sentry-kärnan i stället för till värdens kärna:

  • unshare(2), clone(2), pivot_root(2), chroot(2) och mount(2) opererar på Sentrys uppgiftshierarki i minnet och monteringstabellen VFS2 (procfs, sysfs, tmpfs, devpts, cgroupfs v2), medan värdprocessen rungv körs under ditt icke-privilegierade värd-UID.

2. Inbyggd overlayfs på enfils-volymer av typen ext4lite

När du anger -v gvx-docker:/var/lib/docker (eller --mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker) monterar gvx sitt inbyggda filsystem ext4lite på /var/lib/docker.

Eftersom ext4lite implementerar ett JBD2-journalfört ext4-filsystem inuti en enda värdfil (gvx-docker.ext4) med stöd för trusted.overlay.*-utökade attribut, teckenbaserade whiteout-enheter (0:0), d_type och RENAME_EXCHANGE, använder dockerd sin inbyggda lagringsdrivrutin overlayfs inuti gästen. Flerstegs-docker build-operationer delar Copy-on-Write-lager utan /dev/fuse och utan den diskutrymmesexplosion som Dockers reservdrivrutin vfs orsakar.

3. Användarutrymmes-bryggan docker0 och iptables-legacy

När docker:dind startar inuti gvx:

  • Gästens dockerd skapar bryggan docker0 (172.17.0.1/16), kopplar virtuella veth-par för kapslade containers och programmerar kedjorna NAT (DOCKER, POSTROUTING) och FILTER med matchningar för addrtype och conntrack inuti gvx:s netstack i användarutrymme.
  • Med -p unix:~/.gvx/docker.sock:2375 binds ett värdbaserat Unix-domänsocket (läge 0600), och anslutningar vidarebefordras direkt till port 2375 på dockerd inuti sandlådan.

Jämförelse med befintliga containerverktyg

Förmåga / begränsningStandard-Docker (dockerd)Rootless Docker / PodmanKaniko / Buildah (icke-privilegierad)gvx (rungv + docker:dind)
Kräver värdens root eller sudoJaNejNejNej
Fungerar med PR_SET_NO_NEW_PRIVS=1NejNej (newuidmap misslyckas)DelvisJa
Fungerar när unshare(CLONE_NEWUSER) är blockerad (EPERM)Ej tillämpligtNejBuildah: Nej / Kaniko: JaJa
Fungerar utan /dev/fuseJa (använder kärnans overlay2)Endast via långsam vfs-kopieringJaJa (overlayfs på ext4lite)
Stöder docker build (BuildKit)JaJa (om userns tillåts)Endast bygge av images (ingen körning)Ja
Stöder docker run och docker compose upJaJa (om userns tillåts)NejJa
Körs på Windows och macOS utan Linux-VMNej (kräver WSL2 / VM)Nej (kräver Linux-VM)NejJa (whpx, hvf, systrap)

Exakta krav på värdens kärna i Linux

gvx kräver noll förhöjda förmågor (CapEff: 0000000000000000) och noll användarnamnrymder. På en Linux-värd förlitar sig rungv endast på tre standardprimitiver i icke-privilegierad kärna:

  1. ptrace(PTRACE_ATTACH) på sin egen underordnade tråd en gång under initieringen: systrap startar en lokal stubbhjälptråd vid uppstart, kopplar sig till den för att initiera det delade minneskontexten och kopplar sedan loss. Om värden sätter kernel.yama.ptrace_scope = 3 (vilket inaktiverar all ptrace systemomfattande) eller blockerar ptrace i ett yttre seccomp-filter, kan initieringen av systrap inte slutföras. (Under yama.ptrace_scope = 1, standard på Ubuntu och Debian, lyckas gvx eftersom spårning från förälder till barn är tillåten.)
  2. seccomp(SECCOMP_SET_MODE_FILTER): Används för att fånga gästens systemanrop från containerns stubbtrådar. Som beskrivs i del 3 av denna serie fungerar gvx både när SECCOMP_FILTER_FLAG_NEW_LISTENER är tillgänglig och när det returnerar EBUSY inuti en kapslad sandlåda.
  3. mmap(..., PROT_EXEC, ...): Används för att mappa gästens ELF-segment och det delade minnets trampolin för systrap.

Exempel från start till slut: Bygga och testa en app med flera containers utan root

Följande skript kör ett komplett docker build och ett integrationstest med två containers (redis + applikationscontainer) inom en icke-privilegierad användarsession:

 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# Bygg imagen med overlayfs på ext4lite
20docker build -t demo-ping:latest .
21
22# Skapa ett bryggnätverk, starta Redis och verifiera anslutningen mellan containers
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# Utdata: PONG
27
28# Rensa arbetscontainers och stoppa sandlådan
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID

Vidare läsning i denna serie

  1. gvx: Köra Linux-containers, Docker och Kubernetes i en enda icke-privilegierad process: Arkitekturöversikt över gvx, dess plattformsoberoende exekveringsmotorer (whpx, hvf, systrap), VFS2 (erofs, ext4lite, hostfs v2) och netstack.
  2. Köra Docker, docker build och docker compose utan root eller användarnamnrymder (detta inlägg)
  3. Köra Docker-in-Docker inuti gVisor och kapslade sandlådor med gvx: Hur gvx löser modprobe, iptables-nft, overlay2 på gofer och kapslad SECCOMP_FILTER_FLAG_NEW_LISTENER (EBUSY) inuti gVisor (runsc).
  4. Snabba isolerade Docker-miljöer för RL-rollouts av AI-agenter med gvx: Skalning av parallella SWE-bench- och RL-rollout-miljöer med återställningar av .ext4-volymer med Copy-on-Write på 100 millisekunder.