---
id: 40e9d44f33fa847eb498f09bac5bf144
author: Lemon Mint
title: Ausführen von Docker, docker build und docker compose ohne Root-Rechte oder User Namespaces
description: Wie man eine vollständige Docker Engine (dockerd, BuildKit, overlayfs und docker compose) innerhalb einer isolierten gVisor-Sandbox im User-Space sicher als unprivilegierter Benutzer ausführt, wenn root, sudo, unshare(CLONE_NEWUSER), mount(2) und /dev/fuse blockiert sind.
language: de
date: 2026-11-06T12:02:56.050897958Z
path: /blog/posts/run-docker-without-root-or-userns-gvx-z48916d02
go_package: z.gfire.dev/gvx
go_repourl: https://github.com/gfire-sigs/gvx.git
category: ops
---

## 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](https://github.com/gfire-sigs/gvx)** (`rungv`) die **sichere Lösung mit mehrschichtiger Verteidigung**, um eine vollständige `docker:dind`-Engine im User Space auszuführen.

> **Warum `gvx` by Design sicher ist (keine Umgehung der Rechteausweitung)**: Anders als das Einbinden des Host-Sockets `/var/run/docker.sock` oder das Ausführen von `docker run --privileged` (die eine root-äquivalente Kontrolle über den Host-Kernel gewähren), umgeht oder schwächt `gvx` keine Sicherheitsrichtlinie des Hosts (`PR_SET_NO_NEW_PRIVS`, `seccomp`, AppArmor oder die Beschränkungen für nicht-root UIDs). Stattdessen sperrt `rungv` seinen eigenen Host-Prozess ab (`PR_SET_NO_NEW_PRIVS` + strikte BPF-`seccomp`-Allowlist) und führt `dockerd` im speichersicheren User-Space-Kernel **gVisor (`Sentry`)** von Google aus. Alle Capabilities der Gast-`UID 0`, `overlayfs`-Mounts (`ext4lite`) und `iptables`-Regeln bleiben streng innerhalb des Sandbox-Prozesses isoliert.

```sh
# 1. rungv im User Space installieren (vorkompiliertes, SLSA-attestiertes Release oder go install)
# Option A: Das SLSA-attestierte vorkompilierte Binary aus den GitHub Releases verifizieren und extrahieren
gh attestation verify rungv-v0.2.0-linux-amd64.tar.gz --repo gfire-sigs/gvx \
  --signer-workflow gfire-sigs/gvx/.github/workflows/release.yml \
  --source-ref refs/tags/v0.2.0 --deny-self-hosted-runners
tar -xzf rungv-v0.2.0-linux-amd64.tar.gz && install -m 0755 rungv-v0.2.0-linux-amd64/rungv ~/.local/bin/rungv

# Option B: Oder direkt aus dem Quellcode mit Go installieren
CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest

# 2. docker:dind innerhalb von gvx mit einem Single-File-ext4lite-Volume starten
#    Leitet ~/.gvx/docker.sock (Modus 0600) an Port 2375 des Gasts weiter
mkdir -p ~/.gvx
~/.local/bin/rungv run \
  -p unix:~/.gvx/docker.sock:2375 \
  -p 127.0.0.1:9000-9019:9000-9019 \
  -v gvx-docker:/var/lib/docker \
  -e DOCKER_TLS_CERTDIR= \
  docker:dind --tls=false &

# 3. Die Standard-Docker-CLI und Docker Compose auf den User-Space-Socket ausrichten
export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
until docker info >/dev/null 2>&1; do sleep 0.5; done
docker info --format '{{.Driver}} {{.OperatingSystem}}'
# Ausgabe: overlayfs Alpine Linux ... (containerized)

docker build -t app:test .
docker run --rm app:test
docker 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:

```text
Cannot 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:

```text
sudo: The "no new privileges" flag is set, which prevents sudo from running as root.
sudo: 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)` oder `clone(CLONE_NEWUSER)` blockiert.
- Ubuntu 24.04+ `kernel.apparmor_restrict_unprivileged_userns = 1` aktiviert.
- Debian- oder RHEL-Knoten `kernel.unprivileged_userns_clone = 0` oder `user.max_user_namespaces = 0` setzen.
- `newuidmap` nicht in `/proc/self/uid_map` schreiben kann, weil `PR_SET_NO_NEW_PRIVS` aktiv ist.

Die resultierenden Fehler sehen so aus:

```text
unshare: unshare failed: Operation not permitted
cannot clone: Operation not permitted
Error: cannot re-exec process to join the existing user namespace
newuidmap: write to uid_map failed: Operation not permitted
RootlessKit: 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:

```text
mount("/proc", "/proc", "proc", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL): Operation not permitted
OCI 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
bwrap: 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:

```text
fuse: device not found, try 'modprobe fuse' first
fusermount3: failed to open /dev/fuse: No such file or directory
error 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.

![Ausführung von unprivilegiertem Docker-in-Docker mit gvx](/assets/images/gvx/gvx-unprivileged-dind.png)

### 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)` und `mount(2)` operieren auf der speicherinternen Task-Hierarchie des Sentry und der `VFS2`-Mount-Tabelle (`procfs`, `sysfs`, `tmpfs`, `devpts`, `cgroupfs` v2), während der Host-Prozess `rungv` unter 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-`dockerd` die Bridge `docker0` (`172.17.0.1/16`), hängt virtuelle `veth`-Paare für verschachtelte Container an und programmiert die Ketten `NAT` (`DOCKER`, `POSTROUTING`) und `FILTER` mit `addrtype`- und `conntrack`-Matches innerhalb des User-Space-`netstack` von `gvx`.
- Bindet die Angabe von `-p unix:~/.gvx/docker.sock:2375` einen Host-Unix-Domain-Socket (Modus `0600`) und leitet Verbindungen direkt an Port `2375` des `dockerd` innerhalb 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:

1. **`ptrace(PTRACE_ATTACH)` auf den eigenen Kindthread, einmalig während der Initialisierung**: `systrap` startet 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 Host `kernel.yama.ptrace_scope = 3` setzt (wodurch `ptrace` systemweit deaktiviert wird) oder `ptrace` in einem äußeren seccomp-Filter blockiert, kann die Initialisierung von `systrap` nicht abgeschlossen werden. (Unter `yama.ptrace_scope = 1`, dem Standard auf Ubuntu und Debian, funktioniert `gvx`, da das Tracing von Elternprozess zu Kindprozess erlaubt ist.)
2. **`seccomp(SECCOMP_SET_MODE_FILTER)`**: Wird verwendet, um Gast-Systemaufrufe von Container-Stub-Threads abzufangen. Wie in [Teil 3 dieser Serie](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03) beschrieben, funktioniert `gvx` sowohl dann, wenn `SECCOMP_FILTER_FLAG_NEW_LISTENER` verfügbar ist, als auch dann, wenn es innerhalb einer verschachtelten Sandbox `EBUSY` zurückgibt.
3. **`mmap(..., PROT_EXEC, ...)`**: Wird verwendet, um ELF-Segmente des Gasts und das gemeinsam genutzte Speicher-Trampolin von `systrap` abzubilden.

---

## 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:

```sh
mkdir -p ~/.gvx
rungv run \
  -p unix:~/.gvx/docker.sock:2375 \
  -v gvx-docker:/var/lib/docker \
  -e DOCKER_TLS_CERTDIR= \
  docker:dind --tls=false &
DIND_PID=$!

export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
until docker info >/dev/null 2>&1; do sleep 0.5; done

mkdir -p /tmp/demo-app && cd /tmp/demo-app
cat <<'EOF' > Dockerfile
FROM alpine:3.20
RUN apk add --no-cache redis
CMD ["redis-cli", "-h", "cache", "ping"]
EOF

# Image mit overlayfs auf ext4lite erstellen
docker build -t demo-ping:latest .

# Bridge-Netzwerk erstellen, Redis starten und die Konnektivität zwischen den Containern prüfen
docker network create demo-net
docker run -d --name cache --network demo-net redis:7-alpine
docker run --rm --network demo-net demo-ping:latest
# Ausgabe: PONG

# Workload-Container entfernen und die Sandbox beenden
docker rm -f cache
docker network rm demo-net
kill -INT $DIND_PID
```

---

## Weiterführende Lektüre in dieser Serie

1. **[gvx: Linux-Container, Docker und Kubernetes in einem unprivilegierten Prozess ausführen](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)**: Architekturüberblick über `gvx`, seine plattformübergreifenden Ausführungs-Engines (`whpx`, `hvf`, `systrap`), `VFS2` (`erofs`, `ext4lite`, `hostfs` v2) und `netstack`.
2. **[Docker, docker build und docker compose ohne Root oder User Namespaces ausführen](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)** (dieser Beitrag)
3. **[Docker-in-Docker innerhalb von gVisor und verschachtelten Sandboxes mit gvx ausführen](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)**: Wie `gvx` `modprobe`, `iptables-nft`, `overlay2` auf `gofer` und das verschachtelte `SECCOMP_FILTER_FLAG_NEW_LISTENER` (`EBUSY`) innerhalb von `gVisor` (`runsc`) löst.
4. **[Schnelle isolierte Docker-Umgebungen für AI-Agent-RL-Rollouts mit gvx](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)**: Skalierung paralleler `SWE-bench`- und RL-Rollout-Umgebungen mit 100-Millisekunden-Copy-on-Write-Resets von `.ext4`-Volumes.
