GoSuda

Kørsel af Docker, docker build og docker compose uden root eller user namespaces

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 sandkasse til en AI-kodeagent (f.eks. et cloud-agentberegningsmiljø), hvor root, sudo, brugernavnerum (unshare(CLONE_NEWUSER)), mount(2) og /dev/fuse er blokeret, er gvx (rungv) den sikre, lavindsatsbaserede og token-effektive løsning til at køre en fuld docker:dind-motor i brugerrum.

Lavindsats- og token-effektiv løsning til AI-kodeagenter: Når en autonom kodeagent støder på Cannot connect to the Docker daemon, spilder forsøg på at installere og patche docker.io, podman eller rootlesskit inde i en begrænset container typisk 15 til 30 værktøjskald og titusindvis af kontekst-tokens på at cykle gennem systemctl-, ulimit-, iptables-, unshare- og runc-fejl, kun for at fejle eller falde tilbage til manuel omskrivning af Dockerfileer. Med gvx (rungv) udfører agenten én deterministisk 3-trins opstart i en enkelt tur og kører straks standardkommandoerne docker build, docker run og docker compose uændret.

Hvorfor gvx er sikker by design (ikke en omgåelse af privilegier): I modsætning til at montere værtens /var/run/docker.sock eller køre docker run --privileged (som giver root-ækvivalent 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 + en streng BPF seccomp-tilladelsesliste) og kører dockerd inde i Googles hukommelsessikre gVisor (Sentry) brugerrumskerne. Alle gæstens UID 0-capabilities, overlayfs-monteringer (ext4lite) og iptables-regler forbliver strengt isolerede inde i sandkasseprocessen.

 1# 1. Installer rungv i brugerrum (forudbygget SLSA-attesteret release eller go install)
 2# Mulighed A: Verificer og udpak den SLSA-attesterede forudbyggede binærfil 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æsteport 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 mod brugerrums-socketen
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 sandkasser

Når en ingeniør eller en autonom kodeagent skal køre docker build, docker run eller docker compose inde i en hærdet container eller en multi-tenant runner, fejler standardløsningerne ved fem forskellige kernebarrierer.

1. Ingen værts-Docker-daemon-socket (systemctl og service docker start fejler også)

Kørsel af docker ps eller docker build fejler straks, når værtens /var/run/docker.sock ikke er monteret i miljøet, og forsøg på at starte docker.io via systemctl eller service docker start fejler, fordi PID 1 ikke er systemd, og /etc/init.d/docker rammer containerens hårde ulimit -n-loft:

1Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
2ERROR: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
3System has not been booted with systemd as init system (PID 1). Can't operate.
4Failed to connect to bus: Host is down
5/etc/init.d/docker: 62: ulimit: error setting limit (Operation not permitted)

2. PR_SET_NO_NEW_PRIVS blokerer sudo og setuid-hjælpeprogrammer

Hærdede containere (securityContext.allowPrivilegeEscalation: false i Kubernetes, --security-opt=no-new-privileges i Docker, eller exebox- / bwrap-sandkasser) sætter Linux-bitten PR_SET_NO_NEW_PRIVS på procestræet. Forsøg på at eskalere via sudo eller setuid-binærfiler (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-brugernavnerum (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 ikke kan skrive til /proc/self/uid_map, fordi PR_SET_NO_NEW_PRIVS er aktiv.

De resulterende fejl ser således 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) og pivot_root(2) er blokeret under docker build uden værtens CAP_SYS_ADMIN

Selv hvis et miljø tillader CLONE_NEWUSER, eller dockerd tvinges til at starte med --iptables=false --bridge=none --storage-driver=vfs, fejler docker build ved den første RUN-instruktion, fordi montering af en ny /proc eller kald af pivot_root(2) inde i et indlejret mount-namespace er blokeret:

1ERROR: failed to solve: process "/bin/sh -c ..." did not complete successfully:
2mount("/proc", "/proc", "proc", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL): Operation not permitted
3OCI 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
4OCI runtime create failed: runc create failed: unable to start container process: error during container init: error jailing process inside rootfs: pivot_root .: invalid argument
5bwrap: Creating new namespace failed: Operation not permitted

5. Manglende /dev/fuse ødelægger fuse-overlayfs

Rootless container-motorer, der ikke kan montere kernel-overlayfs, falder tilbage til fuse-overlayfs. I minimale containere eller Kubernetes-pods uden /dev/fuse-enhedsgennemgang 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 kernebarrierer, 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 router dem til den interne Sentry-kerne i stedet for værtskernen:

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

2. Native overlayfs på ext4lite-volumener i én fil

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-journaleret 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 native overlayfs-lagerdriver inde i gæsten. Flertrins-docker build-operationer deler Copy-on-Write-lag uden /dev/fuse og uden den diskplads-eksplosion, som Dockers vfs-reservedriver medfører.

3. docker0-bro i brugerrum og iptables-legacy

Når docker:dind starter inde i gvx:

  • Gæste-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 for dockerd i sandkassen.

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)DelvistJa
Virker når unshare(CLONE_NEWUSER) er blokeret (EPERM)Ikke relevantNejBuildah: Nej / Kaniko: JaJa
Virker uden /dev/fuseJa (bruger kernel-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ærtens kernel på Linux

gvx kræver nul forhøjede capabilities (CapEff: 0000000000000000) og nul brugernavnerum. På en Linux-vært er rungv afhængig af kun tre standard unprivilegerede kerne-primitiver:

  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 frakobler 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 fange gæstesystemkald fra container-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 sandkasse.
  3. mmap(..., PROT_EXEC, ...): Bruges til at mappe gæste-ELF-segmenter og systraps trampolin til delt hukommelse.

Eksempel fra start til slut: Bygning og test af en multi-container-app 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 image'et 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 sandkassen
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID

Yderligere læsning i denne serie

  1. gvx: Kørsel af Linux-containere, Docker og Kubernetes i én unprivilegeret proces: Arkitekturoversigt over gvx, dets tværplatforms eksekveringsmotorer (whpx, hvf, systrap), VFS2 (erofs, ext4lite, hostfs v2) og netstack.
  2. Kørsel af Docker, docker build og docker compose uden root eller brugernavnerum (dette indlæg)
  3. Kørsel af Docker-in-Docker inde i gVisor og indlejrede sandkasser 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 AI-agenters RL-rollouts med gvx: Skalering af parallelle SWE-bench- og RL-rollout-miljøer med copy-on-write .ext4-volumen-nulstillinger på 100 millisekunder.