GoSuda

Kjøring av Docker, docker build og docker compose uten Root eller User Namespaces

By Lemon Mint views ...

Rask løsning: Sikker, uprivilegert Docker i tre steg

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

Hvorfor gvx er trygt by design (ikke en omgåelse av rettighetseskalering): I motsetning til å montere vertens /var/run/docker.sock eller kjøre docker run --privileged (som gir root-tilsvarende 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)-kjerne i brukerrom. Alle gjestens UID 0-egenskaper, overlayfs-monteringer (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 i gvx med et enfils ext4lite-volum
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 socketen i brukerrom
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 ... (containerisert)
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 en autonom kodeagent trenger å kjøre docker build, docker run eller docker compose inne i en herdet container eller en flerbrukerrunner, feiler standardløsninger ved fem distinkte kjernebarrierer.

1. Ingen vertens Docker-daemon-socket

Kommandoene docker ps og docker build feiler umiddelbart når vertens /var/run/docker.sock ikke er montert inn i miljøet:

1Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

2. PR_SET_NO_NEW_PRIVS blokkerer sudo og setuid-hjelpere

Herdede containere (securityContext.allowPrivilegeEscalation: false i Kubernetes, --security-opt=no-new-privileges i Docker, eller exebox- eller 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 som markedsføres 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.

Feilmeldingene 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) er blokkert uten vertens CAP_SYS_ADMIN

Selv om et miljø tillater CLONE_NEWUSER, feiler montering av en ny /proc inne i et nestet monteringsnavnerom hvis den ytre containeren har maskert /proc-undertrær (/proc/acpi, /proc/kcore, /proc/keys) eller fjernet mount(2) fra 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. Manglende /dev/fuse ødelegger fuse-overlayfs

Rootless-containermotorer som ikke kan montere kjerne-overlayfs, faller tilbake til fuse-overlayfs. I minimale containere eller Kubernetes-pods uten videresending 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 uprivilegert Docker-in-Docker med gvx

1. Navnerom i minnet og monteringstabell (VFS2)

Når dockerd, containerd og runc kjører inne i rungv, fanger systrap opp systemkallene deres 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å Sentry sin oppgavehierarki i minnet og VFS2-monteringstabellen (procfs, sysfs, tmpfs, devpts, cgroupfs v2), mens vertsprosessen rungv kjører under din uprivilegerte UID på verten.

2. Native overlayfs på enfils ext4lite-volumer

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 enkelt 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 native overlayfs-lagringsdriver inne i gjesten. Flertrinns docker build-operasjoner deler Copy-on-Write-lag uten /dev/fuse og uten den eksplosive økningen i diskbruk som kommer med Dockers vfs-reservedriver.

3. Brukerrom-docker0-bro og iptables-legacy

Når docker:dind starter inne i gvx:

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

Sammenligning med eksisterende containerverktøy

Egenskap / BegrensningStandard Docker (dockerd)Rootless Docker / PodmanKaniko / Buildah (uprivilegert)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)N/ANeiBuildah: 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øring)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)

Eksakte vertskjernekrav på Linux

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

  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 seg 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 beskrevet i del 3 av denne serien, fungerer gvx både når SECCOMP_FILTER_FLAG_NEW_LISTENER er tilgjengelig og når den returnerer EBUSY inne i en nestet sandkasse.
  3. mmap(..., PROT_EXEC, ...): Brukes til å mappe gjestens ELF-segmenter og systrap-trampolinen for delt minne.

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

Følgende skript kjører en fullstendig 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ør Linux-containere, Docker og Kubernetes i én uprivilegert prosess: Arkitekturoversikt over gvx, dets 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 nestede sandkasser med gvx: Hvordan gvx løser modprobe, iptables-nft, overlay2 på gofer, og nestet SECCOMP_FILTER_FLAG_NEW_LISTENER (EBUSY) inne i gVisor (runsc).
  4. Raske, isolerte Docker-miljøer for RL-utrullinger 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.