Rularea Docker, docker build și docker compose fără root sau user namespaces

Soluție rapidă: Docker neprivilegiat securizat în trei pași
Dacă lucrați într-un container Linux restricționat, un runner CI, un Kubernetes Pod sau un mediu sandbox pentru agenți de programare bazați pe AI (cum ar fi un mediu de calcul cloud pentru agenți) în care root, sudo, spațiile de nume ale utilizatorilor (unshare(CLONE_NEWUSER)), mount(2) și /dev/fuse sunt blocate, gvx (rungv) reprezintă soluția sigură, cu efort redus și eficientă din punct de vedere al tokenilor pentru a rula un motor docker:dind complet în spațiul utilizatorului.
Soluție cu efort redus și eficientă din punct de vedere al tokenilor pentru agenții de programare bazați pe AI: Atunci când un agent de programare autonom întâlnește eroarea
Cannot connect to the Docker daemon, încercarea de a instala și de a modificadocker.io,podmansaurootlesskitîntr-un container restricționat consumă, de regulă, între 15 și 30 de runde de apeluri de instrumente și zeci de mii de tokeni de context prin erori succesive provenite de lasystemctl,ulimit,iptables,unshareșirunc, fără a reuși sau ajungând să rescrie manualDockerfile-urile. Cugvx(rungv), agentul execută un singur bootstrap determinist în 3 pași, într-o singură rundă, și rulează imediat comenzile standarddocker build,docker runșidocker composefără modificări.
De ce
gvxeste sigur prin design (și nu un mecanism de escaladare a privilegiilor): Spre deosebire de montarea socket-ului gazdei/var/run/docker.socksau de rularea cudocker run --privileged(care acordă un control echivalent cu cel de root asupra nucleului gazdei),gvxnu ocolește și nu slăbește nicio politică de securitate a gazdei (PR_SET_NO_NEW_PRIVS,seccomp, AppArmor sau limitările UID-urilor non-root). Dimpotrivă,rungvîși blochează propriul proces gazdă (PR_SET_NO_NEW_PRIVS+ o listă de permisiuni BPFseccompstrict) și ruleazădockerdîn interiorul nucleului în spațiul utilizatorului gVisor (Sentry), dezvoltat de Google, cu siguranță a memoriei. Toate capabilitățileUID 0din interiorul sistemului invitat, montărileoverlayfs(ext4lite) și regulileiptablesrămân strict izolate în interiorul procesului sandbox.
1# 1. Instalați rungv în spațiul utilizatorului (binar precompilat cu atestare SLSA sau go install)
2# Opțiunea A: Verificați și extrageți binarul precompilat cu atestare SLSA din 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# Opțiunea B: Sau instalați direct din sursă, cu Go
9CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest
10
11# 2. Porniți docker:dind în interiorul gvx, cu un volum ext4lite dintr-un singur fișier
12# Redirecționează ~/.gvx/docker.sock (modul 0600) către portul 2375 al sistemului invitat
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. Configurați Docker CLI standard și Docker Compose să utilizeze socket-ul din spațiul utilizatorului
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# Rezultat: overlayfs Alpine Linux ... (containerized)
26
27docker build -t app:test .
28docker run --rm app:test
29docker compose up -d
De ce instrumentele standard fără privilegii root eșuează în sandbox-uri restricționate
Atunci când un inginer sau un agent de programare autonom trebuie să ruleze docker build, docker run sau docker compose într-un container întărit sau într-un runner multi-tenant, soluțiile alternative standard eșuează la cinci bariere distincte ale nucleului.
1. Lipsa socket-ului daemonului Docker al gazdei (systemctl și service docker start eșuează, de asemenea)
Rularea docker ps sau docker build eșuează imediat atunci când socket-ul /var/run/docker.sock al gazdei nu este montat în mediu, iar încercarea de a porni docker.io prin systemctl sau service docker start eșuează deoarece PID 1 nu este systemd, iar /etc/init.d/docker atinge plafonul strict ulimit -n al containerului:
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 blochează sudo și utilitarele setuid
Containerele întărite (securityContext.allowPrivilegeEscalation: false în Kubernetes, --security-opt=no-new-privileges în Docker sau sandbox-urile exebox / bwrap) setează bitul Linux PR_SET_NO_NEW_PRIVS pe arborele de procese. Încercarea de escaladare prin sudo sau binarele setuid (newuidmap, newgidmap, fusermount3) eșuează:
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. Politicile seccomp și sysctl blochează unshare(CLONE_NEWUSER)
Instrumentele promovate drept „rootless” (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap) se bazează totuși pe spațiile de nume ale utilizatorilor ale Linux (CLONE_NEWUSER). Ele eșuează ori de câte ori:
- Profilul seccomp implicit al containerului exterior blochează
unshare(2)sauclone(CLONE_NEWUSER). - Ubuntu 24.04+ activează
kernel.apparmor_restrict_unprivileged_userns = 1. - Nodurile Debian sau RHEL setează
kernel.unprivileged_userns_clone = 0sauuser.max_user_namespaces = 0. newuidmapnu poate scrie în/proc/self/uid_mapdeoarecePR_SET_NO_NEW_PRIVSeste activ.
Erorile rezultate arată astfel:
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) și pivot_root(2) sunt blocate în timpul docker build fără CAP_SYS_ADMIN pe gazdă
Chiar dacă un mediu permite CLONE_NEWUSER sau dockerd este forțat să pornească cu --iptables=false --bridge=none --storage-driver=vfs, rularea docker build eșuează la prima instrucțiune RUN, deoarece montarea unui /proc nou sau apelul pivot_root(2) într-un spațiu de nume de montare imbricat este blocat:
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. Lipsa /dev/fuse afectează fuse-overlayfs
Motoarele de containere fără privilegii root care nu pot monta overlayfs de nucleu revin la fuse-overlayfs. În containerele minimale sau în pod-urile Kubernetes fără transmiterea dispozitivului /dev/fuse, fuse-overlayfs eșuează imediat:
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
Cum rulează gvx docker:dind complet în spațiul utilizatorului
gvx ocolește toate cele cinci bariere ale nucleului deoarece nu solicită nucleului Linux al gazdei să creeze spații de nume, să monteze sisteme de fișiere sau să configureze interfețe de rețea. În schimb, rungv rulează un nucleu Linux intern, în spațiul utilizatorului (gVisor Sentry), și o stivă de rețea în spațiul utilizatorului (netstack) în interiorul unui singur proces obișnuit, fără privilegii.

1. Spații de nume și tabelă de montare în memorie (VFS2)
Atunci când dockerd, containerd și runc se execută în interiorul rungv, systrap interceptează apelurile lor de sistem și le direcționează către nucleul Sentry din proces, nu către nucleul gazdei:
unshare(2),clone(2),pivot_root(2),chroot(2)șimount(2)operează asupra ierarhiei de sarcini în memorie aSentryși asupra tabelei de montareVFS2(procfs,sysfs,tmpfs,devpts,cgroupfsv2), în timp ce procesul gazdărungvrulează sub UID-ul dumneavoastră neprivilegiat de pe gazdă.
2. overlayfs nativ pe volume ext4lite dintr-un singur fișier
Atunci când transmiteți -v gvx-docker:/var/lib/docker (sau --mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker), gvx montează sistemul de fișiere ext4lite integrat la /var/lib/docker.
Deoarece ext4lite implementează un sistem de fișiere ext4 cu jurnal JBD2, stocat într-un singur fișier al gazdei (gvx-docker.ext4), cu suport pentru atributele extinse trusted.overlay.*, dispozitive whiteout de tip caracter (0:0), d_type și RENAME_EXCHANGE, dockerd utilizează driverul de stocare nativ overlayfs în interiorul sistemului invitat. Operațiile docker build în mai multe etape partajează straturi Copy-on-Write fără /dev/fuse și fără explozia spațiului pe disc specifică driverului de rezervă vfs al Docker.
3. Puntea docker0 în spațiul utilizatorului și iptables-legacy
Atunci când docker:dind pornește în gvx:
dockerddin sistemul invitat creează punteadocker0(172.17.0.1/16), atașează perechi virtualevethpentru containerele imbricate și programează lanțurileNAT(DOCKER,POSTROUTING) șiFILTERcu potriviriaddrtypeșiconntrackîn interiorulnetstackalgvx, care rulează în spațiul utilizatorului.- Transmiterea
-p unix:~/.gvx/docker.sock:2375leagă un socket Unix de domeniu al gazdei (modul0600) și redirecționează conexiunile direct către portul2375aldockerddin interiorul sandbox-ului.
Comparație cu instrumentele de containerizare existente
| Capacitate / Restricție | Docker standard (dockerd) | Docker Rootless / Podman | Kaniko / Buildah (neprivilegiat) | gvx (rungv + docker:dind) |
|---|---|---|---|---|
Necesită root sau sudo pe gazdă | Da | Nu | Nu | Nu |
Funcționează cu PR_SET_NO_NEW_PRIVS=1 | Nu | Nu (newuidmap eșuează) | Parțial | Da |
Funcționează când unshare(CLONE_NEWUSER) este blocat (EPERM) | N/A | Nu | Buildah: Nu / Kaniko: Da | Da |
Funcționează fără /dev/fuse | Da (utilizează overlay2 de nucleu) | Doar prin copiere lentă vfs | Da | Da (overlayfs pe ext4lite) |
Suportă docker build (BuildKit) | Da | Da (dacă userns este permis) | Doar construire de imagini (fără runtime) | Da |
Suportă docker run și docker compose up | Da | Da (dacă userns este permis) | Nu | Da |
| Rulează pe Windows și macOS fără o mașină virtuală Linux | Nu (necesită WSL2 / VM) | Nu (necesită VM Linux) | Nu | Da (whpx, hvf, systrap) |
Cerințele exacte ale nucleului gazdă Linux
gvx nu necesită nicio capabilitate ridicată (CapEff: 0000000000000000) și niciun spațiu de nume al utilizatorului. Pe o gazdă Linux, rungv se bazează doar pe trei primitive standard ale nucleului, fără privilegii:
ptrace(PTRACE_ATTACH)asupra propriului fir de execuție copil, o singură dată, în timpul inițializării:systrappornește un fir de execuție stub local, se atașează la acesta pentru a inițializa contextul de memorie partajată și apoi se detașează. Dacă gazda seteazăkernel.yama.ptrace_scope = 3(care dezactiveazăptracela nivelul întregului sistem) sau blocheazăptraceprintr-un filtru seccomp exterior, inițializareasystrapnu poate fi finalizată. (Cuyama.ptrace_scope = 1, valoarea implicită pe Ubuntu și Debian,gvxfuncționează, deoarece trasarea de la părinte la copil este permisă.)seccomp(SECCOMP_SET_MODE_FILTER): Utilizat pentru a intercepta apelurile de sistem ale sistemului invitat din firele de execuție stub ale containerului. Așa cum este descris în Partea 3 a acestei serii,gvxfuncționează atât atunci cândSECCOMP_FILTER_FLAG_NEW_LISTENEReste disponibil, cât și atunci când returneazăEBUSYîntr-un sandbox imbricat.mmap(..., PROT_EXEC, ...): Utilizat pentru a mapa segmentele ELF ale sistemului invitat și trampolina de memorie partajată asystrap.
Exemplu integral: construirea și testarea unei aplicații multi-container fără root
Următorul script rulează un docker build complet și un test de integrare cu două containere (redis și containerul aplicației) într-o sesiune de utilizator fără privilegii:
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# Construiți imaginea utilizând overlayfs pe ext4lite
20docker build -t demo-ping:latest .
21
22# Creați o rețea de tip bridge, porniți Redis și verificați conectivitatea dintre containere
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# Rezultat: PONG
27
28# Curățați containerele de lucru și opriți sandbox-ul
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID
Lecturi suplimentare din această serie
- gvx: Rularea containerelor Linux, Docker și Kubernetes într-un singur proces fără privilegii: Prezentarea arhitecturii
gvx, a motoarelor de execuție multiplatformă (whpx,hvf,systrap), aVFS2(erofs,ext4lite,hostfsv2) și anetstack. - Rularea Docker, docker build și docker compose fără root sau spații de nume ale utilizatorilor (această postare)
- Rularea Docker-in-Docker în gVisor și în sandbox-uri imbricate cu gvx: Modul în care
gvxrezolvămodprobe,iptables-nft,overlay2pegoferșiSECCOMP_FILTER_FLAG_NEW_LISTENERimbricat (EBUSY) îngVisor(runsc). - Medii Docker izolate și rapide pentru rulările RL ale agenților AI cu gvx: Scalarea mediilor paralele de tip
SWE-benchși RL, cu resetarea volumelor.ext4Copy-on-Write în 100 de milisecunde.