Ausführen von Docker, docker build und docker compose ohne Root-Rechte oder User Namespaces

Schnelllösung: Unprivilegiertes Docker in drei Schritten
Wenn Sie in einem abgeriegelten Linux-Container, einem CI-Runner, einem Kubernetes-Pod oder einer AI-Agent-Sandbox arbeiten, in der root, sudo, User Namespaces (unshare(CLONE_NEWUSER)), mount(2) und /dev/fuse blockiert sind, ist gvx (rungv) die sichere Lösung mit mehrschichtiger Verteidigung, um eine vollständige docker:dind-Engine im User Space auszuführen.
Warum
gvxby Design sicher ist (keine Umgehung der Rechteausweitung): Anders als das Einbinden des Host-Sockets/var/run/docker.sockoder das Ausführen vondocker run --privileged(die eine root-äquivalente Kontrolle über den Host-Kernel gewähren), umgeht oder schwächtgvxkeine Sicherheitsrichtlinie des Hosts (PR_SET_NO_NEW_PRIVS,seccomp, AppArmor oder die Beschränkungen für nicht-root UIDs). Stattdessen sperrtrungvseinen eigenen Host-Prozess ab (PR_SET_NO_NEW_PRIVS+ strikte BPF-seccomp-Allowlist) und führtdockerdim speichersicheren User-Space-Kernel gVisor (Sentry) von Google aus. Alle Capabilities der Gast-UID 0,overlayfs-Mounts (ext4lite) undiptables-Regeln bleiben streng innerhalb des Sandbox-Prozesses isoliert.
1# 1. rungv im User Space installieren (vorkompiliertes, SLSA-attestiertes Release oder go install)
2# Option A: Das SLSA-attestierte vorkompilierte Binary aus den GitHub Releases verifizieren und extrahieren
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# Option B: Oder direkt aus dem Quellcode mit Go installieren
9CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest
10
11# 2. docker:dind innerhalb von gvx mit einem Single-File-ext4lite-Volume starten
12# Leitet ~/.gvx/docker.sock (Modus 0600) an Port 2375 des Gasts weiter
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. Die Standard-Docker-CLI und Docker Compose auf den User-Space-Socket ausrichten
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# Ausgabe: overlayfs Alpine Linux ... (containerized)
26
27docker build -t app:test .
28docker run --rm app:test
29docker compose up -d
Warum Standard-Rootless-Tools in eingeschränkten Sandboxes scheitern
Wenn ein Ingenieur oder ein autonomer Coding-Agent docker build, docker run oder docker compose innerhalb eines gehärteten Containers oder eines Multi-Tenant-Runners ausführen muss, scheitern übliche Workarounds an fünf unterschiedlichen Kernel-Barrieren.
1. Kein Host-Docker-Daemon-Socket
Der Befehl docker ps oder docker build schlägt sofort fehl, wenn der Host-Socket /var/run/docker.sock nicht in die Umgebung eingebunden ist:
1Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
2. PR_SET_NO_NEW_PRIVS blockiert sudo und Setuid-Helfer
Gehärtete Container (securityContext.allowPrivilegeEscalation: false in Kubernetes, --security-opt=no-new-privileges in Docker oder exebox- bzw. bwrap-Sandboxes) setzen das Linux-Bit PR_SET_NO_NEW_PRIVS im Prozessbaum. Der Versuch, über sudo oder Setuid-Binaries (newuidmap, newgidmap, fusermount3) Rechte zu erweitern, schlägt fehl:
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- und Sysctl-Richtlinien blockieren unshare(CLONE_NEWUSER)
Als „rootless“ vermarktete Werkzeuge (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap) setzen weiterhin auf Linux-User Namespaces (CLONE_NEWUSER). Sie schlagen immer dann fehl, wenn:
- das Standard-seccomp-Profil des äußeren Containers
unshare(2)oderclone(CLONE_NEWUSER)blockiert. - Ubuntu 24.04+
kernel.apparmor_restrict_unprivileged_userns = 1aktiviert. - Debian- oder RHEL-Knoten
kernel.unprivileged_userns_clone = 0oderuser.max_user_namespaces = 0setzen. newuidmapnicht in/proc/self/uid_mapschreiben kann, weilPR_SET_NO_NEW_PRIVSaktiv ist.
Die resultierenden Fehler sehen so aus:
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) ist ohne Host-CAP_SYS_ADMIN blockiert
Selbst wenn eine Umgebung CLONE_NEWUSER erlaubt, schlägt das Mounten eines neuen /proc innerhalb eines verschachtelten Mount-Namespaces fehl, wenn der äußere Container /proc-Teilbäume maskiert hat (/proc/acpi, /proc/kcore, /proc/keys) oder mount(2) in seccomp verworfen hat:
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. Fehlendes /dev/fuse bricht fuse-overlayfs
Rootless-Container-Engines, die kein Kernel-overlayfs mounten können, weichen auf fuse-overlayfs aus. In minimalen Containern oder Kubernetes-Pods ohne /dev/fuse-Device-Passthrough schlägt fuse-overlayfs sofort fehl:
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
Wie gvx docker:dind vollständig im User Space ausführt
gvx umgeht alle fünf Kernel-Barrieren, weil es den Host-Linux-Kernel nicht bittet, Namespaces zu erzeugen, Dateisysteme zu mounten oder Netzwerkschnittstellen zu konfigurieren. Stattdessen führt rungv einen internen Linux-Kernel im User Space (den gVisor-Sentry) und einen Netzwerkstack im User Space (netstack) innerhalb eines gewöhnlichen, unprivilegierten Prozesses aus.

1. Speicherinterne Namespaces und Mount-Tabelle (VFS2)
Wenn dockerd, containerd und runc innerhalb von rungv ausgeführt werden, fängt systrap deren Systemaufrufe ab und leitet sie an den prozessinternen Sentry-Kernel statt an den Host-Kernel weiter:
unshare(2),clone(2),pivot_root(2),chroot(2)undmount(2)operieren auf der speicherinternen Task-Hierarchie des Sentry und derVFS2-Mount-Tabelle (procfs,sysfs,tmpfs,devpts,cgroupfsv2), während der Host-Prozessrungvunter Ihrer unprivilegierten Host-UID läuft.
2. Natives overlayfs auf Single-File-ext4lite-Volumes
Wenn Sie -v gvx-docker:/var/lib/docker (oder --mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker) übergeben, mountet gvx sein integriertes Dateisystem ext4lite unter /var/lib/docker.
Da ext4lite ein JBD2-journalisiertes ext4-Dateisystem innerhalb einer einzelnen Host-Datei (gvx-docker.ext4) implementiert, mit Unterstützung für trusted.overlay.*-erweiterte Attribute, Character-Whiteout-Devices (0:0), d_type und RENAME_EXCHANGE, verwendet dockerd im Gast seinen nativen Storage-Treiber overlayfs. Mehrstufige docker build-Vorgänge teilen sich Copy-on-Write-Layer, ohne /dev/fuse und ohne die Explosion des Speicherplatzbedarfs, die mit dem vfs-Fallback-Treiber von Docker einhergeht.
3. User-Space-Bridge docker0 und iptables-legacy
Wenn docker:dind innerhalb von gvx startet:
- Erstellt der Gast-
dockerddie Bridgedocker0(172.17.0.1/16), hängt virtuelleveth-Paare für verschachtelte Container an und programmiert die KettenNAT(DOCKER,POSTROUTING) undFILTERmitaddrtype- undconntrack-Matches innerhalb des User-Space-netstackvongvx. - Bindet die Angabe von
-p unix:~/.gvx/docker.sock:2375einen Host-Unix-Domain-Socket (Modus0600) und leitet Verbindungen direkt an Port2375desdockerdinnerhalb der Sandbox weiter.
Vergleich mit bestehenden Container-Werkzeugen
| Fähigkeit / Einschränkung | Standard-Docker (dockerd) | Rootless Docker / Podman | Kaniko / Buildah (unprivilegiert) | gvx (rungv + docker:dind) |
|---|---|---|---|---|
Host-root oder sudo erforderlich | Ja | Nein | Nein | Nein |
Funktioniert mit PR_SET_NO_NEW_PRIVS=1 | Nein | Nein (newuidmap schlägt fehl) | Teilweise | Ja |
Funktioniert, wenn unshare(CLONE_NEWUSER) blockiert ist (EPERM) | N/A | Nein | Buildah: Nein / Kaniko: Ja | Ja |
Funktioniert ohne /dev/fuse | Ja (nutzt Kernel-overlay2) | Nur über langsame vfs-Kopie | Ja | Ja (overlayfs auf ext4lite) |
Unterstützt docker build (BuildKit) | Ja | Ja (falls userns erlaubt) | Nur Image-Build (keine Runtime) | Ja |
Unterstützt docker run und docker compose up | Ja | Ja (falls userns erlaubt) | Nein | Ja |
| Läuft unter Windows und macOS ohne Linux-VM | Nein (benötigt WSL2 / VM) | Nein (benötigt Linux-VM) | Nein | Ja (whpx, hvf, systrap) |
Genaue Kernel-Anforderungen des Hosts unter Linux
gvx benötigt keinerlei erhöhte Capabilities (CapEff: 0000000000000000) und keine User Namespaces. Auf einem Linux-Host stützt sich rungv nur auf drei standardmäßige unprivilegierte Kernel-Primitiven:
ptrace(PTRACE_ATTACH)auf den eigenen Kindthread, einmalig während der Initialisierung:systrapstartet beim Start einen lokalen Stub-Helferthread, hängt sich an ihn an, um den gemeinsam genutzten Speicherkontext zu initialisieren, und löst sich anschließend wieder. Wenn der Hostkernel.yama.ptrace_scope = 3setzt (wodurchptracesystemweit deaktiviert wird) oderptracein einem äußeren seccomp-Filter blockiert, kann die Initialisierung vonsystrapnicht abgeschlossen werden. (Unteryama.ptrace_scope = 1, dem Standard auf Ubuntu und Debian, funktioniertgvx, da das Tracing von Elternprozess zu Kindprozess erlaubt ist.)seccomp(SECCOMP_SET_MODE_FILTER): Wird verwendet, um Gast-Systemaufrufe von Container-Stub-Threads abzufangen. Wie in Teil 3 dieser Serie beschrieben, funktioniertgvxsowohl dann, wennSECCOMP_FILTER_FLAG_NEW_LISTENERverfügbar ist, als auch dann, wenn es innerhalb einer verschachtelten SandboxEBUSYzurückgibt.mmap(..., PROT_EXEC, ...): Wird verwendet, um ELF-Segmente des Gasts und das gemeinsam genutzte Speicher-Trampolin vonsystrapabzubilden.
End-to-End-Beispiel: Erstellen und Testen einer Multi-Container-App ohne Root
Das folgende Skript führt einen vollständigen docker build und einen Integrationstest mit zwei Containern (redis + Anwendungscontainer) in einer unprivilegierten Benutzersitzung aus:
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# Image mit overlayfs auf ext4lite erstellen
20docker build -t demo-ping:latest .
21
22# Bridge-Netzwerk erstellen, Redis starten und die Konnektivität zwischen den Containern prüfen
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# Ausgabe: PONG
27
28# Workload-Container entfernen und die Sandbox beenden
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID
Weiterführende Lektüre in dieser Serie
- gvx: Linux-Container, Docker und Kubernetes in einem unprivilegierten Prozess ausführen: Architekturüberblick über
gvx, seine plattformübergreifenden Ausführungs-Engines (whpx,hvf,systrap),VFS2(erofs,ext4lite,hostfsv2) undnetstack. - Docker, docker build und docker compose ohne Root oder User Namespaces ausführen (dieser Beitrag)
- Docker-in-Docker innerhalb von gVisor und verschachtelten Sandboxes mit gvx ausführen: Wie
gvxmodprobe,iptables-nft,overlay2aufgoferund das verschachtelteSECCOMP_FILTER_FLAG_NEW_LISTENER(EBUSY) innerhalb vongVisor(runsc) löst. - Schnelle isolierte Docker-Umgebungen für AI-Agent-RL-Rollouts mit gvx: Skalierung paralleler
SWE-bench- und RL-Rollout-Umgebungen mit 100-Millisekunden-Copy-on-Write-Resets von.ext4-Volumes.