Köra Docker, docker build och docker compose utan root eller användarnamnrymder

Snabblösning: Säkra Docker utan privilegier i tre steg
Om du arbetar i en låst Linux-container, CI-runner, Kubernetes Pod eller AI-agentsandlåda där root, sudo, användarnamnrymder (unshare(CLONE_NEWUSER)), mount(2) och /dev/fuse är blockerade, är gvx (rungv) den säkra lösningen med försvar på flera nivåer för att köra en fullständig docker:dind-motor i användarutrymme.
Varför
gvxär säkert by design (inte en lösning för privilegieeskalering): Till skillnad från att montera värdens/var/run/docker.sockeller köradocker run --privileged(vilket ger root-motsvarande kontroll över värdens kärna) kringgår eller försvagar integvxnågon säkerhetspolicy på värden (PR_SET_NO_NEW_PRIVS,seccomp, AppArmor eller begränsningar för icke-root-UID). I stället låserrungvsin egen värdprocess (PR_SET_NO_NEW_PRIVS+ en strikt BPF-seccomp-tillåtelselista) och kördockerdinuti Googles minnessäkra gVisor (Sentry)-kärna i användarutrymme. Alla gäst-UID 0-förmågor,overlayfs-monteringar (ext4lite) ochiptables-regler förblir strikt isolerade inom sandlådeprocessen.
1# 1. Installera rungv i användarutrymme (förkompilerad SLSA-attesterad release eller go install)
2# Alternativ A: Verifiera och extrahera den SLSA-attesterade förkompilerade binären från 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 installera direkt från källkod med Go
9CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest
10
11# 2. Starta docker:dind inuti gvx med en enfils-ext4lite-volym
12# Vidarebefordrar ~/.gvx/docker.sock (läge 0600) till 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. Peka den vanliga Docker CLI och Docker Compose mot användarutrymmets 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# Utdata: overlayfs Alpine Linux ... (containerized)
26
27docker build -t app:test .
28docker run --rm app:test
29docker compose up -d
Varför vanliga verktyg för rootless-körning misslyckas i begränsade sandlådor
När en ingenjör eller en autonom kodningsagent behöver köra docker build, docker run eller docker compose inuti en härdad container eller en multi-tenant-runner, misslyckas vanliga lösningar vid fem separata kärnbarriärer.
1. Ingen värd-socket för Docker-daemonen
Att köra docker ps eller docker build misslyckas omedelbart när värdens /var/run/docker.sock inte är monterad i miljön:
1Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
2. PR_SET_NO_NEW_PRIVS blockerar sudo och setuid-hjälpprogram
Härdade containrar (securityContext.allowPrivilegeEscalation: false i Kubernetes, --security-opt=no-new-privileges i Docker, eller exebox- / bwrap-sandlådor) sätter biten PR_SET_NO_NEW_PRIVS i Linux på processträdet. Försök att eskalera via sudo eller setuid-binärer (newuidmap, newgidmap, fusermount3) misslyckas:
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- och sysctl-policyer blockerar unshare(CLONE_NEWUSER)
Verktyg som marknadsförs som "rootless" (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap) förlitar sig fortfarande på Linux användarnamnrymder (CLONE_NEWUSER). De misslyckas när:
- Den yttre containerns standardprofil för seccomp blockerar
unshare(2)ellerclone(CLONE_NEWUSER). - Ubuntu 24.04+ aktiverar
kernel.apparmor_restrict_unprivileged_userns = 1. - Debian- eller RHEL-noder sätter
kernel.unprivileged_userns_clone = 0elleruser.max_user_namespaces = 0. newuidmapinte kan skriva till/proc/self/uid_mapeftersomPR_SET_NO_NEW_PRIVSär aktivt.
De resulterande felen ser ut så här:
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) är blockerad utan värdens CAP_SYS_ADMIN
Även om en miljö tillåter CLONE_NEWUSER misslyckas montering av en ny /proc inuti en kapslad monteringsnamnrymd om den yttre containern har maskerat /proc-delträd (/proc/acpi, /proc/kcore, /proc/keys) eller tagit bort mount(2) från 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. Saknad /dev/fuse bryter fuse-overlayfs
Rootless-containermotorer som inte kan montera kärnans overlayfs faller tillbaka till fuse-overlayfs. I minimala containrar eller Kubernetes-pods utan genomslussning av enheten /dev/fuse misslyckas fuse-overlayfs omedelbart:
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
Hur gvx kör docker:dind helt i användarutrymme
gvx kringgår alla fem kärnbarriärerna eftersom den inte ber värdens Linux-kärna att skapa namnrymder, montera filsystem eller konfigurera nätverksgränssnitt. I stället kör rungv en intern Linux-kärna i användarutrymme (gVisor Sentry) och en nätverksstack i användarutrymme (netstack) inuti en enda vanlig process utan privilegier.

1. Namnrymder och monteringstabell i minnet (VFS2)
När dockerd, containerd och runc körs inuti rungv fångar systrap upp deras systemanrop och dirigerar dem till den processinterna Sentry-kärnan i stället för till värdens kärna:
unshare(2),clone(2),pivot_root(2),chroot(2)ochmount(2)arbetar mot Sentrys uppgiftshierarki i minnet och monteringstabellenVFS2(procfs,sysfs,tmpfs,devpts,cgroupfsv2), medan värdprocessenrungvkörs under ditt UID på värden utan privilegier.
2. Native overlayfs på enfils-volymer av typen ext4lite
När du anger -v gvx-docker:/var/lib/docker (eller --mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker) monterar gvx sitt inbyggda filsystem ext4lite på /var/lib/docker.
Eftersom ext4lite implementerar ett JBD2-journalfört ext4-filsystem inuti en enda fil på värden (gvx-docker.ext4) med stöd för utökade attribut trusted.overlay.*, teckenbaserade whiteout-enheter (0:0), d_type och RENAME_EXCHANGE, använder dockerd sin inbyggda lagringsdrivrutin overlayfs inuti gästen. Flerstegsoperationer för docker build delar Copy-on-Write-lager utan /dev/fuse och utan den diskutrymmesexplosion som följer av Dockers reservdrivrutin vfs.
3. Användarutrymmets docker0-brygga och iptables-legacy
När docker:dind startar inuti gvx:
- Gästens
dockerdskapar bryggandocker0(172.17.0.1/16), kopplar virtuellaveth-par för kapslade containrar och programmerar kedjornaNAT(DOCKER,POSTROUTING) ochFILTERmed matchningar föraddrtypeochconntrackinutigvx:snetstacki användarutrymme. - Med
-p unix:~/.gvx/docker.sock:2375binds en Unix-domänsocket på värden (läge0600) och anslutningar vidarebefordras direkt till port2375fördockerdinuti sandlådan.
Jämförelse med befintliga containerverktyg
| Egenskap / Begränsning | Standard Docker (dockerd) | Rootless Docker / Podman | Kaniko / Buildah (utan privilegier) | gvx (rungv + docker:dind) |
|---|---|---|---|---|
Värd-root eller sudo krävs | Ja | Nej | Nej | Nej |
Fungerar med PR_SET_NO_NEW_PRIVS=1 | Nej | Nej (newuidmap misslyckas) | Delvis | Ja |
Fungerar när unshare(CLONE_NEWUSER) är blockerad (EPERM) | N/A | Nej | Buildah: Nej / Kaniko: Ja | Ja |
Fungerar utan /dev/fuse | Ja (använder kärnans overlay2) | Endast via långsam vfs-kopiering | Ja | Ja (overlayfs på ext4lite) |
Stöder docker build (BuildKit) | Ja | Ja (om userns tillåts) | Endast bildbygge (ingen körtid) | Ja |
Stöder docker run och docker compose up | Ja | Ja (om userns tillåts) | Nej | Ja |
| Fungerar på Windows och macOS utan Linux-VM | Nej (kräver WSL2 / VM) | Nej (kräver Linux-VM) | Nej | Ja (whpx, hvf, systrap) |
Exakta krav på värdens Linux-kärna
gvx kräver noll förhöjda förmågor (CapEff: 0000000000000000) och noll användarnamnrymder. På en Linux-värd förlitar sig rungv endast på tre standardprimitiver i kärnan som kan användas utan privilegier:
ptrace(PTRACE_ATTACH)på sin egen underordnade tråd en gång under initieringen:systrapstartar en lokal stubbhjälptråd vid uppstart, kopplar sig till den för att initiera det delade minneskontexten och kopplar sedan loss. Om värden sätterkernel.yama.ptrace_scope = 3(vilket inaktiverar allptracesystemomfattande) eller blockerarptracei ett yttre seccomp-filter kan inte initieringen avsystrapslutföras. (Medyama.ptrace_scope = 1, standard på Ubuntu och Debian, lyckasgvxeftersom spårning från förälder till barn är tillåten.)seccomp(SECCOMP_SET_MODE_FILTER): Används för att fånga gästens systemanrop från containerstubbtrådar. Som beskrivs i del 3 av denna serie fungerargvxbåde närSECCOMP_FILTER_FLAG_NEW_LISTENERär tillgänglig och när den returnerarEBUSYinuti en kapslad sandlåda.mmap(..., PROT_EXEC, ...): Används för att mappa gästens ELF-segment ochsystrap:s delade minnestrampolin.
Exempel från början till slut: Bygga och testa en multi-container-app utan root
Följande skript kör ett komplett docker build och ett integrationstest med två containrar (redis + applikationscontainer) inuti en användarsession utan privilegier:
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 avbildningen med overlayfs på ext4lite
20docker build -t demo-ping:latest .
21
22# Skapa ett bryggnätverk, starta Redis och verifiera anslutningen mellan containrarna
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# Rensa arbetscontainrarna och stoppa sandlådan
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID
Fortsatt läsning i den här serien
- gvx: Köra Linux-containrar, Docker och Kubernetes i en enda process utan privilegier: Arkitekturöversikt av
gvx, dess plattformsoberoende exekveringsmotorer (whpx,hvf,systrap),VFS2(erofs,ext4lite,hostfsv2) ochnetstack. - Köra Docker, docker build och docker compose utan root eller användarnamnrymder (detta inlägg)
- Köra Docker-in-Docker inuti gVisor och kapslade sandlådor med gvx: Hur
gvxlösermodprobe,iptables-nft,overlay2pågoferoch kapsladSECCOMP_FILTER_FLAG_NEW_LISTENER(EBUSY) inutigVisor(runsc). - Snabba isolerade Docker-miljöer för AI-agenters RL-rollouts med gvx: Skalning av parallella
SWE-bench- och RL-rollout-miljöer med återställning av.ext4-volymer med Copy-on-Write på 100 millisekunder.