GoSuda

Kørsel af Docker, docker build og docker compose uden root eller brugernavneområder

By Lemon Mint views ...

Hurtig løsning: Sikker unprivilegeret Docker i tre trin

Hvis du arbejder i en låst Linux-container, CI-runner, Kubernetes Pod eller AI-agent-sandbox, hvor root, sudo, brugernamespaces (unshare(CLONE_NEWUSER)), mount(2) og /dev/fuse er blokeret, er gvx (rungv) den sikre løsning med dyb forsvarsopbygning til at køre en fuld docker:dind-motor i brugerrum.

Hvorfor gvx er sikker by design (ikke en omgåelse af privilegieeskalering): I modsætning til at montere den værts-/var/run/docker.sock eller køre docker run --privileged (som giver root-tilsvarende kontrol over værtskernen), omgår eller svækker gvx ikke nogen sikkerhedspolitik på værten (PR_SET_NO_NEW_PRIVS, seccomp, AppArmor eller begrænsninger for ikke-root UID). I stedet låser rungv sin egen værtsproces (PR_SET_NO_NEW_PRIVS + streng BPF seccomp-tilladelsesliste) og kører dockerd inde i Googles hukommelsessikre gVisor (Sentry) brugerrumskerne. Alle gæst-UID 0-kapaciteter, overlayfs-monteringer (ext4lite) og iptables-regler forbliver strengt isoleret inde i sandbox-processen.

 1# 1. Installer rungv i brugerrum (forudbygget SLSA-attesteret udgivelse eller go install)
 2# Mulighed A: Verificer og udpak den SLSA-attesterede forudbyggede binær 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# Mulighed 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 inde i gvx med et enkeltfil-ext4lite-volumen
12#    Videresender ~/.gvx/docker.sock (tilstand 0600) til 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. Peg den standard Docker CLI og Docker Compose på brugerrums-socket'en
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# Output: overlayfs Alpine Linux ... (containeriseret)
26
27docker build -t app:test .
28docker run --rm app:test
29docker compose up -d

Hvorfor standard rootless-værktøjer fejler i begrænsede sandboxes

Når en ingeniør eller en autonom kodningsagent skal køre docker build, docker run eller docker compose inde i en hærdet container eller et multi-tenant runner-miljø, fejler standardløsninger ved fem særskilte kernelbarrierer.

1. Ingen værts-Docker-daemon-socket

Kørsel af docker ps eller docker build fejler straks, når værtens /var/run/docker.sock ikke er monteret 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 blokerer sudo og setuid-hjælpere

Hærdede containere (securityContext.allowPrivilegeEscalation: false i Kubernetes, --security-opt=no-new-privileges i Docker, eller exebox- / bwrap-sandboxes) sætter Linux-bitten PR_SET_NO_NEW_PRIVS på procestræet. Forsøg på at eskalere via sudo eller setuid-binære filer (newuidmap, newgidmap, fusermount3) fejler:

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-politikker blokerer unshare(CLONE_NEWUSER)

Værktøjer, der markedsføres som "rootless" (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap), er stadig afhængige af Linux brugernamespaces (CLONE_NEWUSER). De fejler, når:

  • Den ydre containers standard seccomp-profil blokerer unshare(2) eller clone(CLONE_NEWUSER).
  • Ubuntu 24.04+ aktiverer kernel.apparmor_restrict_unprivileged_userns = 1.
  • Debian- eller RHEL-noder sætter kernel.unprivileged_userns_clone = 0 eller user.max_user_namespaces = 0.
  • newuidmap kan ikke skrive til /proc/self/uid_map, fordi PR_SET_NO_NEW_PRIVS er aktiv.

De resulterende fejl ser sådan ud:

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 blokeret uden værtens CAP_SYS_ADMIN

Selvom et miljø tillader CLONE_NEWUSER, fejler montering af en frisk /proc inde i et indlejret mount-namespace, hvis den ydre container har maskeret /proc-undertræer (/proc/acpi, /proc/kcore, /proc/keys) eller fjernet mount(2) i 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 ødelægger fuse-overlayfs

Rootless container-motorer, der ikke kan montere kernens overlayfs, falder tilbage til fuse-overlayfs. I minimale containere eller Kubernetes-pods uden videreførelse af /dev/fuse-enheden fejler fuse-overlayfs straks:

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

Sådan kører gvx docker:dind helt i brugerrum

gvx omgår alle fem kernelbarrierer, fordi den ikke beder værtens Linux-kerne om at oprette namespaces, montere filsystemer eller konfigurere netværksgrænseflader. I stedet kører rungv en intern Linux-kerne i brugerrum (gVisor Sentry) og en netværksstak i brugerrum (netstack) inde i én almindelig unprivilegeret proces.

Kørsel af unprivilegeret Docker-in-Docker med gvx

1. Namespaces og monteringstabel i hukommelsen (VFS2)

Når dockerd, containerd og runc udføres inde i rungv, opfanger systrap deres systemkald og sender dem til den in-process Sentry-kerne i stedet for værtskernen:

  • unshare(2), clone(2), pivot_root(2), chroot(2) og mount(2) opererer på Sentry'ens opgavehierarki i hukommelsen og VFS2-monteringstabellen (procfs, sysfs, tmpfs, devpts, cgroupfs v2), mens værtsprocessen rungv kører under din unprivilegerede værts-UID.

2. Indbygget overlayfs på enkeltfils-ext4lite-volumener

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

Da ext4lite implementerer et JBD2-journaliseret ext4-filsystem inde i en enkelt værtsfil (gvx-docker.ext4) med understøttelse af trusted.overlay.*-udvidede attributter, tegn-whiteout-enheder (0:0), d_type og RENAME_EXCHANGE, bruger dockerd sin indbyggede overlayfs-lagringsdriver inde i gæsten. Flertrins docker build-operationer deler Copy-on-Write-lag uden /dev/fuse og uden den diskplads-eksplosion, som Dockers vfs-fallback-driver medfører.

3. docker0-bro i brugerrum og iptables-legacy

Når docker:dind starter inde i gvx:

  • Gæst-dockerd opretter docker0-broen (172.17.0.1/16), tilslutter virtuelle veth-par til indlejrede containere og programmerer NAT- (DOCKER, POSTROUTING) og FILTER-kæder med addrtype- og conntrack-match inde i gvx's netstack i brugerrum.
  • Angivelse af -p unix:~/.gvx/docker.sock:2375 binder et Unix-domænesocket på værten (tilstand 0600) og videresender forbindelser direkte til port 2375 på dockerd inde i sandboxen.

Sammenligning med eksisterende containerværktøjer

Egenskab / begrænsningStandard Docker (dockerd)Rootless Docker / PodmanKaniko / Buildah (unprivilegeret)gvx (rungv + docker:dind)
Kræver værts-root eller sudoJaNejNejNej
Virker med PR_SET_NO_NEW_PRIVS=1NejNej (newuidmap fejler)DelvisJa
Virker når unshare(CLONE_NEWUSER) er blokeret (EPERM)Ikke relevantNejBuildah: Nej / Kaniko: JaJa
Virker uden /dev/fuseJa (bruger kernens overlay2)Kun via langsom vfs-kopieringJaJa (overlayfs på ext4lite)
Understøtter docker build (BuildKit)JaJa (hvis userns er tilladt)Kun image-build (ingen runtime)Ja
Understøtter docker run og docker compose upJaJa (hvis userns er tilladt)NejJa
Kører på Windows og macOS uden en Linux-VMNej (kræver WSL2 / VM)Nej (kræver Linux-VM)NejJa (whpx, hvf, systrap)

Præcise krav til værtskernen på Linux

gvx kræver ingen forhøjede kapabiliteter (CapEff: 0000000000000000) og ingen brugernamespaces. På en Linux-vært er rungv afhængig af kun tre standard unprivilegerede kernelprimitiver:

  1. ptrace(PTRACE_ATTACH) på sin egen underordnede tråd én gang under initialisering: systrap starter ved opstart en lokal stub-hjælpetråd, tilslutter sig den for at initialisere den delte hukommelseskontekst og afkobler sig igen. Hvis værten sætter kernel.yama.ptrace_scope = 3 (som deaktiverer al ptrace systemvidt) eller blokerer ptrace i et ydre seccomp-filter, kan systrap-initialiseringen ikke gennemføres. (Under yama.ptrace_scope = 1, standarden på Ubuntu og Debian, lykkes gvx, fordi sporing fra forælder til barn er tilladt.)
  2. seccomp(SECCOMP_SET_MODE_FILTER): Bruges til at opfange gæstens systemkald fra containerens stub-tråde. Som beskrevet i del 3 af denne serie virker gvx både når SECCOMP_FILTER_FLAG_NEW_LISTENER er tilgængelig, og når den returnerer EBUSY inde i en indlejret sandbox.
  3. mmap(..., PROT_EXEC, ...): Bruges til at mappe gæstens ELF-segmenter og systraps delte hukommelses-trampolin.

End-to-end-eksempel: Build og test af en app med flere containere uden root

Følgende script kører en komplet docker build og en integrationstest med to containere (redis + applikationscontainer) inde i en unprivilegeret brugersession:

 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# Byg imaget ved hjælp af overlayfs på ext4lite
20docker build -t demo-ping:latest .
21
22# Opret et bro-netværk, start Redis, og verificer forbindelsen mellem 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# Output: PONG
27
28# Ryd op i arbejdsbelastningscontainerne og stop sandboxen
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID

Yderligere læsning i denne serie

  1. gvx: Kør Linux-containere, Docker og Kubernetes i én unprivilegeret proces: Arkitekturoversigt over gvx, dens tværplatforms eksekveringsmotorer (whpx, hvf, systrap), VFS2 (erofs, ext4lite, hostfs v2) og netstack.
  2. Kør Docker, docker build og docker compose uden root eller brugernamespaces (dette indlæg)
  3. Kør Docker-in-Docker inde i gVisor og indlejrede sandboxes med gvx: Hvordan gvx løser modprobe, iptables-nft, overlay2 på gofer og indlejret SECCOMP_FILTER_FLAG_NEW_LISTENER (EBUSY) inde i gVisor (runsc).
  4. Hurtige isolerede Docker-miljøer til RL-udrulninger af AI-agenter med gvx: Skalering af parallelle SWE-bench- og RL-udrulningsmiljøer med copy-on-write .ext4-volumen-nulstillinger på 100 millisekunder.