Docker, docker build en docker compose uitvoeren zonder root of user namespaces

Snelle oplossing: veilig onbevoorrecht Docker in drie stappen
Als u werkt binnen een afgeschermde Linux-container, CI-runner, Kubernetes Pod of AI-agentsandbox waarin root, sudo, user namespaces (unshare(CLONE_NEWUSER)), mount(2) en /dev/fuse zijn geblokkeerd, is gvx (rungv) de veilige oplossing met defense-in-depth om een volledige docker:dind-engine in user space uit te voeren.
Waarom
gvxveilig is by design (geen omweg voor privilege-escalatie): In tegenstelling tot het koppelen van de host-socket/var/run/docker.sockof het uitvoeren vandocker run --privileged(die root-equivalente controle over de host-kernel verlenen), omzeilt of verzwaktgvxgeen enkel beveiligingsbeleid van de host (PR_SET_NO_NEW_PRIVS,seccomp, AppArmor, of beperkingen van niet-root UID). In plaats daarvan vergrendeltrungvzijn eigen hostproces (PR_SET_NO_NEW_PRIVS+ strikte BPFseccomp-allowlist) en draaitdockerdbinnen Google's geheugenveilige gVisor (Sentry) user-space kernel. AlleUID 0-capabilities van de gast,overlayfs-mounts (ext4lite) eniptables-regels blijven strikt geïsoleerd binnen het sandboxproces.
1# 1. Installeer rungv in user space (vooraf gebouwde SLSA-geattesteerde release of go install)
2# Optie A: verifieer en pak de vooraf gebouwde SLSA-geattesteerde binary uit 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# Optie B: of installeer rechtstreeks vanuit de broncode met Go
9CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest
10
11# 2. Start docker:dind binnen gvx met een ext4lite-volume van één bestand
12# Stuurt ~/.gvx/docker.sock (modus 0600) door naar gastpoort 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. Richt de standaard Docker CLI en Docker Compose op de user-space socket
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# Uitvoer: overlayfs Alpine Linux ... (gecontaineriseerd)
26
27docker build -t app:test .
28docker run --rm app:test
29docker compose up -d
Waarom standaard rootless-tools falen in beperkte sandboxes
Wanneer een engineer of autonome codeeragent docker build, docker run of docker compose moet uitvoeren binnen een geharde container of multi-tenant runner, falen standaardomwegen bij vijf afzonderlijke kernelbarrières.
1. Geen host-Docker-daemonsocket
docker ps of docker build faalt onmiddellijk wanneer de host-socket /var/run/docker.sock niet in de omgeving is gekoppeld:
1Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
2. PR_SET_NO_NEW_PRIVS blokkeert sudo en setuid-helpers
Geharde containers (securityContext.allowPrivilegeEscalation: false in Kubernetes, --security-opt=no-new-privileges in Docker, of exebox- of bwrap-sandboxes) zetten de Linux-bit PR_SET_NO_NEW_PRIVS in de procesboom. Een poging tot escalatie via sudo of setuid-binaries (newuidmap, newgidmap, fusermount3) mislukt:
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- en sysctl-beleid blokkeert unshare(CLONE_NEWUSER)
Tools die als "rootless" worden aangeprezen (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap) vertrouwen nog steeds op Linux user namespaces (CLONE_NEWUSER). Ze falen wanneer:
- Het standaard seccomp-profiel van de buitenste container
unshare(2)ofclone(CLONE_NEWUSER)blokkeert. - Ubuntu 24.04+
kernel.apparmor_restrict_unprivileged_userns = 1inschakelt. - Debian- of RHEL-nodes
kernel.unprivileged_userns_clone = 0ofuser.max_user_namespaces = 0instellen. newuidmapniet naar/proc/self/uid_mapkan schrijven omdatPR_SET_NO_NEW_PRIVSactief is.
De resulterende fouten zien er als volgt uit:
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) is geblokkeerd zonder host-CAP_SYS_ADMIN
Ook als een omgeving CLONE_NEWUSER toestaat, mislukt het koppelen van een nieuwe /proc binnen een geneste mount-namespace wanneer de buitenste container /proc-subbomen heeft gemaskeerd (/proc/acpi, /proc/kcore, /proc/keys) of mount(2) in seccomp heeft laten vallen:
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. Ontbrekende /dev/fuse breekt fuse-overlayfs
Rootless container-engines die geen kernel-overlayfs kunnen koppelen, vallen terug op fuse-overlayfs. In minimale containers of Kubernetes-pods zonder doorgifte van het /dev/fuse-apparaat faalt fuse-overlayfs onmiddellijk:
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
Hoe gvx docker:dind volledig in user space uitvoert
gvx omzeilt alle vijf kernelbarrières omdat het de host-Linux-kernel niet vraagt om namespaces aan te maken, bestandssystemen te koppelen of netwerkinterfaces te configureren. In plaats daarvan draait rungv een interne Linux-kernel in user space (de gVisor Sentry) en een netwerkstack in user space (netstack) binnen één gewoon onbevoorrecht proces.

1. Namespaces en mounttabel in geheugen (VFS2)
Wanneer dockerd, containerd en runc binnen rungv worden uitgevoerd, onderschept systrap hun systeemaanroepen en leidt deze om naar de in-process Sentry-kernel in plaats van naar de host-kernel:
unshare(2),clone(2),pivot_root(2),chroot(2)enmount(2)werken op de in-memory taakhiërarchie en deVFS2-mounttabel van de Sentry (procfs,sysfs,tmpfs,devpts,cgroupfsv2), terwijl het host-rungv-proces draait onder uw onbevoorrechte host-UID.
2. Native overlayfs op ext4lite-volumes van één bestand
Wanneer u -v gvx-docker:/var/lib/docker (of --mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker) doorgeeft, koppelt gvx het ingebouwde ext4lite-bestandssysteem aan /var/lib/docker.
Omdat ext4lite een JBD2-journaled ext4-bestandssysteem implementeert binnen één host-bestand (gvx-docker.ext4) met ondersteuning voor trusted.overlay.*-extended attributes, character whiteout-apparaten (0:0), d_type en RENAME_EXCHANGE, gebruikt dockerd in de gast zijn eigen overlayfs-opslagdriver. Meertraps-docker build-bewerkingen delen Copy-on-Write-lagen zonder /dev/fuse en zonder de explosie van de schijfruimte die gepaard gaat met de vfs-terugvaldriver van Docker.
3. docker0-bridge in user space en iptables-legacy
Wanneer docker:dind binnen gvx start:
- De
dockerdin de gast maakt dedocker0-bridge aan (172.17.0.1/16), koppelt virtueleveth-paren voor geneste containers en programmeert deNAT- (DOCKER,POSTROUTING) enFILTER-chains metaddrtype- enconntrack-matches binnen denetstackvangvxin user space. - Het doorgeven van
-p unix:~/.gvx/docker.sock:2375bindt een host-Unix-domeinsocket (modus0600) en stuurt verbindingen rechtstreeks door naar poort2375vandockerdbinnen de sandbox.
Vergelijking met bestaande containertools
| Capaciteit / beperking | Standaard Docker (dockerd) | Rootless Docker / Podman | Kaniko / Buildah (onbevoorrecht) | gvx (rungv + docker:dind) |
|---|---|---|---|---|
Host-root of sudo vereist | Ja | Nee | Nee | Nee |
Werkt met PR_SET_NO_NEW_PRIVS=1 | Nee | Nee (newuidmap faalt) | Gedeeltelijk | Ja |
Werkt wanneer unshare(CLONE_NEWUSER) is geblokkeerd (EPERM) | N.v.t. | Nee | Buildah: Nee / Kaniko: Ja | Ja |
Werkt zonder /dev/fuse | Ja (gebruikt kernel-overlay2) | Alleen via trage vfs-kopie | Ja | Ja (overlayfs op ext4lite) |
Ondersteunt docker build (BuildKit) | Ja | Ja (als userns is toegestaan) | Alleen imagebouw (geen runtime) | Ja |
Ondersteunt docker run en docker compose up | Ja | Ja (als userns is toegestaan) | Nee | Ja |
| Draait op Windows en macOS zonder Linux-VM | Nee (vereist WSL2 / VM) | Nee (vereist Linux-VM) | Nee | Ja (whpx, hvf, systrap) |
Exacte host-kernelvereisten op Linux
gvx vereist nul verhoogde capabilities (CapEff: 0000000000000000) en nul user namespaces. Op een Linux-host vertrouwt rungv op slechts drie standaard onbevoorrechte kernelprimitieven:
ptrace(PTRACE_ATTACH)op de eigen childthread, eenmalig tijdens de initialisatie:systrapstart bij het opstarten een lokale stub-helperthread, koppelt eraan vast om de gedeelde-geheugencontext te initialiseren, en maakt de koppeling weer ongedaan. Als de hostkernel.yama.ptrace_scope = 3instelt (wat alleptracesysteembreed uitschakelt) ofptraceblokkeert in een buitenste seccomp-filter, kan de initialisatie vansystrapniet worden voltooid. (Onderyama.ptrace_scope = 1, de standaard op Ubuntu en Debian, slaagtgvxomdat tracering van ouder naar kind is toegestaan.)seccomp(SECCOMP_SET_MODE_FILTER): wordt gebruikt om systeemaanroepen van de gast te onderscheppen vanuit containerstub-threads. Zoals behandeld in deel 3 van deze reeks, werktgvxzowel wanneerSECCOMP_FILTER_FLAG_NEW_LISTENERbeschikbaar is als wanneer hetEBUSYteruggeeft binnen een geneste sandbox.mmap(..., PROT_EXEC, ...): wordt gebruikt om ELF-segmenten van de gast en desystrapshared-memory-trampoline in kaart te brengen.
End-to-end voorbeeld: een multi-container-app bouwen en testen zonder root
Het volgende script voert een volledige docker build en een integratietest met twee containers (redis + applicatiecontainer) uit binnen een onbevoorrechte gebruikerssessie:
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# Bouw de image met overlayfs op ext4lite
20docker build -t demo-ping:latest .
21
22# Maak een bridge-netwerk aan, start Redis en controleer de connectiviteit tussen de 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# Uitvoer: PONG
27
28# Ruim de workloadcontainers op en stop de sandbox
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID
Verder lezen in deze reeks
- gvx: Linux-containers, Docker en Kubernetes uitvoeren in één onbevoorrecht proces: Architectuuroverzicht van
gvx, de platformoverschrijdende uitvoeringsengines (whpx,hvf,systrap),VFS2(erofs,ext4lite,hostfsv2) ennetstack. - Docker, docker build en docker compose uitvoeren zonder root of user namespaces (dit bericht)
- Docker-in-Docker uitvoeren binnen gVisor en geneste sandboxes met gvx: Hoe
gvxmodprobe,iptables-nft,overlay2opgoferen genesteSECCOMP_FILTER_FLAG_NEW_LISTENER(EBUSY) binnengVisor(runsc) oplost. - Snelle geïsoleerde Docker-omgevingen voor AI-agent RL-rollouts met gvx: Schalen van parallelle
SWE-bench- en RL-rolloutomgevingen met copy-on-write.ext4-volumereset van 100 milliseconden.