GoSuda

Kjøre Docker, docker build og docker compose uten root eller brukernavnrom

By Lemon Mint views ...

Rask løsning: Sikker unprivilegert Docker i tre trinn

Hvis du arbeider i en låst Linux-container, CI-runner, Kubernetes Pod eller et sandkassemiljø for AI-kodeagenter (for eksempel et compute-miljø for skyagenter) der root, sudo, brukernavnrom (unshare(CLONE_NEWUSER)), mount(2) og /dev/fuse er blokkert, er gvx (rungv) den sikre, lavinnsats- og tokeneffektive løsningen for å kjøre en fullstendig docker:dind-motor i brukerrom.

Lavinnsats- og tokeneffektiv løsning for AI-kodeagenter: Når en autonom kodeagent får Cannot connect to the Docker daemon, vil forsøk på å installere og patche docker.io, podman eller rootlesskit i en begrenset container typisk kaste bort 15 til 30 verktøykall-runder og titusenvis av kontekst-tokens på å sykle gjennom feil fra systemctl, ulimit, iptables, unshare og runc, bare for å mislykkes eller falle tilbake til manuell omskriving av Dockerfile-filer. Med gvx (rungv) kjører agenten én deterministisk 3-trinns oppstart i en enkelt runde og kjører deretter umiddelbart standard docker build, docker run og docker compose-kommandoer uendret.

Hvorfor gvx er trygt by design (ikke en omgåelse av privilegieeskalering): I motsetning til å montere vertens /var/run/docker.sock eller kjøre docker run --privileged (som gir rotekvivalent kontroll over vertskjernen), omgår eller svekker ikke gvx noen sikkerhetspolicy på verten (PR_SET_NO_NEW_PRIVS, seccomp, AppArmor eller begrensninger for ikke-root-UID). I stedet låser rungv sin egen vertsprosess (PR_SET_NO_NEW_PRIVS + streng BPF seccomp-tillatelsesliste) og kjører dockerd inne i Googles minnesikre gVisor (Sentry) brukerrom-kjerne. Alle gjestens UID 0-egenskaper, overlayfs-montasjer (ext4lite) og iptables-regler forblir strengt isolert inne i sandkasseprosessen.

 1# 1. Installer rungv i brukerrom (forhåndsbygd SLSA-attestert utgivelse eller go install)
 2# Alternativ A: Verifiser og pakk ut den SLSA-attesterte forhåndsbygde binærfilen fra 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 installer direkte fra kildekode med Go
 9CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest
10
11# 2. Start docker:dind inne i gvx med et enkelt ext4lite-volum (én fil)
12#    Videresender ~/.gvx/docker.sock (modus 0600) til gjestens 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. Pek standard Docker-CLI og Docker Compose mot brukerrom-socketen
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

Hvorfor standard rootless-verktøy feiler i begrensede sandkasser

Når en ingeniør eller autonom kodeagent trenger å kjøre docker build, docker run eller docker compose inne i en herdet container eller multi-tenant runner, feiler standard omgåelser ved fem distinkte kjernebarrierer.

1. Ingen vert-Docker-daemon-socket (systemctl og service docker start feiler også)

Kjøring av docker ps eller docker build feiler umiddelbart når vertens /var/run/docker.sock ikke er montert inn i miljøet, og forsøk på å starte docker.io via systemctl eller service docker start feiler fordi PID 1 ikke er systemd, og /etc/init.d/docker treffer containerens harde 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 blokkerer sudo og setuid-hjelpeprogrammer

Herdede containere (securityContext.allowPrivilegeEscalation: false i Kubernetes, --security-opt=no-new-privileges i Docker, eller exebox- / bwrap-sandkasser) setter Linux-biten PR_SET_NO_NEW_PRIVS på prosesstreet. Forsøk på å eskalere via sudo eller setuid-binærfiler (newuidmap, newgidmap, fusermount3) feiler:

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- og sysctl-policyer blokkerer unshare(CLONE_NEWUSER)

Verktøy markedsført som "rootless" (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap) er fortsatt avhengige av Linux-brukernavnrom (CLONE_NEWUSER). De feiler når:

  • Den ytre containerens standard seccomp-profil blokkerer unshare(2) eller clone(CLONE_NEWUSER).
  • Ubuntu 24.04+ aktiverer kernel.apparmor_restrict_unprivileged_userns = 1.
  • Debian- eller RHEL-noder setter kernel.unprivileged_userns_clone = 0 eller user.max_user_namespaces = 0.
  • newuidmap ikke kan skrive til /proc/self/uid_map fordi PR_SET_NO_NEW_PRIVS er aktiv.

De resulterende feilene ser slik ut:

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) og pivot_root(2) er blokkert under docker build uten vertens CAP_SYS_ADMIN

Selv om et miljø tillater CLONE_NEWUSER, eller dockerd tvinges til å starte med --iptables=false --bridge=none --storage-driver=vfs, feiler docker build ved den første RUN-instruksjonen, fordi montering av en ny /proc eller kall til pivot_root(2) inne i et nøstet monteringsnavnerom er blokkert:

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. Manglende /dev/fuse ødelegger fuse-overlayfs

Rootless container-motorer som ikke kan montere kjerne-overlayfs, faller tilbake til fuse-overlayfs. I minimale containere eller Kubernetes-pods uten videreføring av enheten /dev/fuse feiler fuse-overlayfs umiddelbart:

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

Hvordan gvx kjører docker:dind helt i brukerrom

gvx omgår alle fem kjernebarrierene fordi den ikke ber vertens Linux-kjerne om å opprette navnerom, montere filsystemer eller konfigurere nettverksgrensesnitt. I stedet kjører rungv en intern Linux-kjerne i brukerrom (gVisor Sentry) og en nettverksstakk i brukerrom (netstack) inne i én vanlig uprivilegert prosess.

Kjører unprivilegert Docker-in-Docker med gvx

1. Navnerom og monteringstabell i minnet (VFS2)

Når dockerd, containerd og runc kjører inne i rungv, avskjærer systrap deres systemkall og sender dem til den prosessinterne Sentry-kjernen i stedet for vertskjernen:

  • unshare(2), clone(2), pivot_root(2), chroot(2) og mount(2) opererer på Sentrys oppgavehierarki i minnet og VFS2-monteringstabellen (procfs, sysfs, tmpfs, devpts, cgroupfs v2), mens vertsprosessen rungv kjører under din uprivilegerte vert-UID.

2. Innebygd overlayfs på ext4lite-volum i én fil

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

Fordi ext4lite implementerer et JBD2-journalført ext4-filsystem inne i én vertsfil (gvx-docker.ext4) med støtte for trusted.overlay.*-utvidede attributter, tegn-whiteout-enheter (0:0), d_type og RENAME_EXCHANGE, bruker dockerd sin innebygde overlayfs-lagringsdriver inne i gjesten. Flertrinns docker build-operasjoner deler Copy-on-Write-lag uten /dev/fuse og uten diskplass-eksplosjonen som følger med Dockers vfs-reservedriver.

3. Brukerrom-docker0-bro og iptables-legacy

Når docker:dind starter inne i gvx:

  • Gjest-dockerd oppretter docker0-broen (172.17.0.1/16), kobler virtuelle veth-par for nøstede containere og programmerer NAT- (DOCKER, POSTROUTING) og FILTER-kjeder med addrtype- og conntrack-treff inne i gvx sin brukerrom-netstack.
  • Ved å sende -p unix:~/.gvx/docker.sock:2375 binder man et vert-Unix-domenesocket (modus 0600) og videresender tilkoblinger direkte til port 2375 på dockerd inne i sandkassen.

Sammenligning med eksisterende container-verktøy

Evne / BegrensningStandard Docker (dockerd)Rootless Docker / PodmanKaniko / Buildah (unprivilegert)gvx (rungv + docker:dind)
Krever vert root eller sudoJaNeiNeiNei
Fungerer med PR_SET_NO_NEW_PRIVS=1NeiNei (newuidmap feiler)DelvisJa
Fungerer når unshare(CLONE_NEWUSER) er blokkert (EPERM)Ikke relevantNeiBuildah: Nei / Kaniko: JaJa
Fungerer uten /dev/fuseJa (bruker kjerne-overlay2)Kun via treg vfs-kopieringJaJa (overlayfs på ext4lite)
Støtter docker build (BuildKit)JaJa (hvis userns er tillatt)Kun bygging av images (ingen kjøretid)Ja
Støtter docker run og docker compose upJaJa (hvis userns er tillatt)NeiJa
Kjører på Windows og macOS uten Linux-VMNei (krever WSL2 / VM)Nei (krever Linux-VM)NeiJa (whpx, hvf, systrap)

Nøyaktige krav til vertskjernen på Linux

gvx krever null forhøyede egenskaper (CapEff: 0000000000000000) og null brukernavnrom. På en Linux-vert er rungv avhengig av kun tre standard uprivilegerte kjerne-primitiver:

  1. ptrace(PTRACE_ATTACH) på sin egen barnetråd én gang under initialisering: systrap starter en lokal stub-hjelpertråd ved oppstart, kobler seg til den for å initialisere den delte minnekonteksten og kobler fra. Hvis verten setter kernel.yama.ptrace_scope = 3 (som deaktiverer all ptrace systemomfattende) eller blokkerer ptrace i et ytre seccomp-filter, kan ikke systrap-initialiseringen fullføres. (Under yama.ptrace_scope = 1, standard på Ubuntu og Debian, lykkes gvx, fordi sporing fra forelder til barn er tillatt.)
  2. seccomp(SECCOMP_SET_MODE_FILTER): Brukes til å fange gjestens systemkall fra containerens stub-tråder. Som dekket i del 3 av denne serien, fungerer gvx både når SECCOMP_FILTER_FLAG_NEW_LISTENER er tilgjengelig, og når det returnerer EBUSY inne i et nøstet sandkassemiljø.
  3. mmap(..., PROT_EXEC, ...): Brukes til å mappe gjestens ELF-segmenter og systrap sin delte minne-trampoline.

Ende-til-ende-eksempel: Bygge og teste en flerkontainer-app uten root

Følgende skript kjører en komplett docker build og en integrasjonstest med to containere (redis + applikasjonscontainer) inne i en uprivilegert brukerøkt:

 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 imaget med overlayfs på ext4lite
20docker build -t demo-ping:latest .
21
22# Opprett et bro-nettverk, start Redis og verifiser tilkobling mellom containerne
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# Rydd opp i arbeidscontainerne og stopp sandkassen
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID

Videre lesning i denne serien

  1. gvx: Kjøre Linux-containere, Docker og Kubernetes i én unprivilegert prosess: Arkitekturoversikt over gvx, dens plattformuavhengige kjøremotorer (whpx, hvf, systrap), VFS2 (erofs, ext4lite, hostfs v2) og netstack.
  2. Kjøre Docker, docker build og docker compose uten root eller brukernavnrom (dette innlegget)
  3. Kjøre Docker-in-Docker inne i gVisor og nøstede sandkasser med gvx: Hvordan gvx løser modprobe, iptables-nft, overlay2 på gofer og nøstet SECCOMP_FILTER_FLAG_NEW_LISTENER (EBUSY) inne i gVisor (runsc).
  4. Raske isolerte Docker-miljøer for RL-utrulling av AI-agenter med gvx: Skalering av parallelle SWE-bench- og RL-utrullingsmiljøer med tilbakestillinger av .ext4-volumer med copy-on-write på 100 millisekunder.