GoSuda

Spúšťanie Docker, docker build a docker compose bez root oprávnení alebo používateľských menných priestorov

By Lemon Mint views ...

Rýchle riešenie: Bezpečný neprivilegovaný Docker v troch krokoch

Ak pracujete vo vnútri uzamknutého Linux kontajnera, CI runnera, Kubernetes Podu alebo pieskoviska AI kódovacieho agenta (napríklad výpočtového prostredia cloudového agenta), kde sú blokované root, sudo, používateľské menné priestory (unshare(CLONE_NEWUSER)), mount(2) a /dev/fuse, je gvx (rungv) bezpečným, nenáročným a úsporným na tokeny riešením na spustenie plného enginu docker:dind v používateľskom priestore.

Riešenie s nízkou námahou a úsporou tokenov pre AI kódovacích agentov: Keď autonómny kódovací agent narazí na chybu Cannot connect to the Docker daemon, pokus o inštaláciu a záplatovanie docker.io, podman alebo rootlesskit vo vnútri obmedzeného kontajnera typicky spotrebuje 15 až 30 volaní nástrojov a desiatky tisíc kontextových tokenov pri cyklení cez chyby systemctl, ulimit, iptables, unshare a runc, pričom výsledkom je zlyhanie alebo návrat k manuálnemu prepisovaniu súborov Dockerfile. S gvx (rungv) agent vykoná jeden deterministický trojkrokový bootstrap v jedinom kroku a okamžite spustí štandardné príkazy docker build, docker run a docker compose bez zmeny.

Prečo je gvx bezpečný už z návrhu (nie obchádzka eskalácie oprávnení): Na rozdiel od pripojenia hostiteľského /var/run/docker.sock alebo spustenia docker run --privileged (ktoré poskytujú kontrolu ekvivalentnú roote nad jadrom hostiteľa) gvx neobchádza ani neoslabuje žiadnu bezpečnostnú politiku hostiteľa (PR_SET_NO_NEW_PRIVS, seccomp, AppArmor ani obmedzenia neroot UID). Namiesto toho rungv uzamkne svoj vlastný hostiteľský proces (PR_SET_NO_NEW_PRIVS + prísny zoznam povolených BPF seccomp) a spustí dockerd vo vnútri používateľského jadra gVisor (Sentry) od Googlu, ktoré je bezpečné z hľadiska pamäte. Všetky schopnosti UID 0 hosťa, pripojenia overlayfs (ext4lite) a pravidlá iptables zostávajú prísne izolované vo vnútri procesu pieskoviska.

 1# 1. Inštalácia rungv v používateľskom priestore (predkompilované vydanie overené SLSA alebo go install)
 2# Možnosť A: Overenie a extrakcia predkompilovaného binárneho súboru overeného SLSA z 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# Možnosť B: Alebo inštalácia priamo zo zdrojového kódu pomocou Go
 9CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest
10
11# 2. Spustenie docker:dind vo vnútri gvx so zväzkom ext4lite v jedinom súbore
12#    Presmerováva ~/.gvx/docker.sock (režim 0600) na port 2375 hosťa
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. Nasmerovanie štandardného Docker CLI a Docker Compose na socket v používateľskom priestore
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# Výstup: overlayfs Alpine Linux ... (kontajnerizované)
26
27docker build -t app:test .
28docker run --rm app:test
29docker compose up -d

Prečo štandardné nástroje bez roota zlyhávajú v obmedzených pieskoviskách

Keď inžinier alebo autonómny kódovací agent potrebuje spustiť docker build, docker run alebo docker compose vo vnútri vytvrdeného kontajnera alebo viacnájomcového runnera, štandardné obchádzky zlyhajú na piatich odlišných bariérach jadra.

1. Chýbajúci socket démona Docker hostiteľa (zlyhávajú aj systemctl a service docker start)

Spustenie docker ps alebo docker build zlyhá okamžite, keď hostiteľský /var/run/docker.sock nie je pripojený do prostredia, a pokus o spustenie docker.io cez systemctl alebo service docker start zlyhá, pretože PID 1 nie je systemd a /etc/init.d/docker narazí na pevný limit ulimit -n kontajnera:

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 blokuje sudo a pomocné programy setuid

Vytvrdené kontajnery (securityContext.allowPrivilegeEscalation: false v Kubernetes, --security-opt=no-new-privileges v Dockeri alebo pieskoviská exebox / bwrap) nastavujú bit Linuxu PR_SET_NO_NEW_PRIVS na strome procesov. Pokus o eskaláciu cez sudo alebo binárne súbory setuid (newuidmap, newgidmap, fusermount3) zlyhá:

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. Politiky seccomp a sysctl blokujú unshare(CLONE_NEWUSER)

Nástroje propagované ako „rootless“ (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap) sa stále spoliehajú na používateľské menné priestory Linuxu (CLONE_NEWUSER). Zlyhávajú vždy, keď:

  • Predvolený profil seccomp vonkajšieho kontajnera blokuje unshare(2) alebo clone(CLONE_NEWUSER).
  • Ubuntu 24.04+ povolí kernel.apparmor_restrict_unprivileged_userns = 1.
  • Uzly Debian alebo RHEL nastavia kernel.unprivileged_userns_clone = 0 alebo user.max_user_namespaces = 0.
  • newuidmap nemôže zapisovať do /proc/self/uid_map, pretože je aktívny PR_SET_NO_NEW_PRIVS.

Výsledné chyby vyzerajú takto:

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) a pivot_root(2) sú blokované počas docker build bez CAP_SYS_ADMIN hostiteľa

Aj keď prostredie povoľuje CLONE_NEWUSER alebo sa dockerd vynúti spustiť s --iptables=false --bridge=none --storage-driver=vfs, spustenie docker build zlyhá pri prvej inštrukcii RUN, pretože pripojenie nového /proc alebo volanie pivot_root(2) vo vnorenom mount namespace je blokované:

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. Chýbajúci /dev/fuse narúša fuse-overlayfs

Kontajnerové enginy bez roota, ktoré nedokážu pripojiť jadrový overlayfs, sa prepnú na fuse-overlayfs. V minimalistických kontajneroch alebo Kubernetes podoch bez preposlania zariadenia /dev/fuse fuse-overlayfs okamžite zlyhá:

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

Ako gvx spúšťa docker:dind úplne v používateľskom priestore

gvx obchádza všetkých päť bariér jadra, pretože nežiada hostiteľské jadro Linuxu o vytváranie menných priestorov, pripájanie súborových systémov ani konfiguráciu sieťových rozhraní. Namiesto toho rungv spúšťa interné jadro Linuxu v používateľskom priestore (gVisor Sentry) a sieťový zásobník v používateľskom priestore (netstack) v rámci jedného bežného neprivilegovaného procesu.

Spustenie neprivilegovaného Docker-in-Docker s gvx

1. Menné priestory a tabuľka pripojení v pamäti (VFS2)

Keď dockerd, containerd a runc bežia vo vnútri rungv, systrap zachytáva ich systémové volania a smeruje ich do procesového jadra Sentry namiesto jadra hostiteľa:

  • unshare(2), clone(2), pivot_root(2), chroot(2) a mount(2) pracujú nad hierarchiou úloh Sentry v pamäti a tabuľkou pripojení VFS2 (procfs, sysfs, tmpfs, devpts, cgroupfs v2), zatiaľ čo hostiteľský proces rungv beží pod vaším neprivilegovaným UID hostiteľa.

2. Natívny overlayfs na zväzkoch ext4lite v jedinom súbore

Keď odovzdáte -v gvx-docker:/var/lib/docker (alebo --mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker), gvx pripojí svoj vstavaný súborový systém ext4lite na /var/lib/docker.

Keďže ext4lite implementuje súborový systém ext4 s žurnálom JBD2 vo vnútri jedného hostiteľského súboru (gvx-docker.ext4) s podporou rozšírených atribútov trusted.overlay.*, znakových whiteout zariadení (0:0), d_type a RENAME_EXCHANGE, dockerd používa svoj natívny ovládač úložiska overlayfs vo vnútri hosťa. Viacstupňové operácie docker build zdieľajú vrstvy Copy-on-Write bez /dev/fuse a bez nárastu využitia diskového priestoru, ktorý spôsobuje záložný ovládač vfs v Dockeri.

3. Most docker0 v používateľskom priestore a iptables-legacy

Keď sa docker:dind spustí vo vnútri gvx:

  • dockerd hosťa vytvorí most docker0 (172.17.0.1/16), pripojí virtuálne páry veth pre vnorené kontajnery a naprogramuje reťazce NAT (DOCKER, POSTROUTING) a FILTER so zhodami addrtype a conntrack vo vnútri používateľského netstack systému gvx.
  • Odovzdanie -p unix:~/.gvx/docker.sock:2375 viaže hostiteľský socket Unix domény (režim 0600) a presmerováva pripojenia priamo na port 2375 dockerd vo vnútri pieskoviska.

Porovnanie s existujúcimi nástrojmi pre kontajnery

Schopnosť / obmedzenieŠtandardný Docker (dockerd)Rootless Docker / PodmanKaniko / Buildah (neprivilegovaný)gvx (rungv + docker:dind)
Vyžaduje sa root alebo sudo hostiteľaÁnoNieNieNie
Funguje s PR_SET_NO_NEW_PRIVS=1NieNie (newuidmap zlyhá)ČiastočneÁno
Funguje, keď je unshare(CLONE_NEWUSER) blokované (EPERM)N/ANieBuildah: Nie / Kaniko: ÁnoÁno
Funguje bez /dev/fuseÁno (používa jadrový overlay2)Iba cez pomalú kópiu vfsÁnoÁno (overlayfs na ext4lite)
Podporuje docker build (BuildKit)ÁnoÁno (ak je povolený userns)Iba zostavenie obrazu (bez runtime)Áno
Podporuje docker run a docker compose upÁnoÁno (ak je povolený userns)NieÁno
Beží na Windows a macOS bez VM LinuxuNie (vyžaduje WSL2 / VM)Nie (vyžaduje VM Linuxu)NieÁno (whpx, hvf, systrap)

Presné požiadavky na jadro hostiteľa Linux

gvx nevyžaduje žiadne zvýšené schopnosti (CapEff: 0000000000000000) a žiadne používateľské menné priestory. Na hostiteľovi Linuxu sa rungv spolieha iba na tri štandardné neprivilegované primitíva jadra:

  1. ptrace(PTRACE_ATTACH) na jeho vlastnom podriadenom vlákne jednorazovo počas inicializácie: systrap pri štarte vytvorí pomocné vlákno stub, pripojí sa k nemu na inicializáciu zdieľaného kontextu pamäte a odpojí sa. Ak hostiteľ nastaví kernel.yama.ptrace_scope = 3 (čím sa zakážu všetky ptrace v celom systéme) alebo blokuje ptrace vo vonkajšom filtri seccomp, inicializácia systrap sa nemôže dokončiť. (Pri yama.ptrace_scope = 1, predvolenom nastavení na Ubuntu a Debiane, gvx uspeje, pretože sledovanie z rodiča na potomka je povolené.)
  2. seccomp(SECCOMP_SET_MODE_FILTER): Slúži na zachytávanie systémových volaní hosťa z pomocných vlákien stub kontajnera. Ako je uvedené v 3. časti tohto seriálu, gvx funguje tak, keď je SECCOMP_FILTER_FLAG_NEW_LISTENER dostupný, ako aj vtedy, keď vo vnorenom pieskovisku vráti EBUSY.
  3. mmap(..., PROT_EXEC, ...): Slúži na mapovanie segmentov ELF hosťa a trampolíny systrap so zdieľanou pamäťou.

Komplexný príklad: zostavenie a testovanie viackontajnerovej aplikácie bez roota

Nasledujúci skript spustí kompletné docker build a integračný test s dvoma kontajnermi (redis + kontajner aplikácie) v neprivilegovanej používateľskej relácii:

 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# Zostavenie obrazu pomocou overlayfs na ext4lite
20docker build -t demo-ping:latest .
21
22# Vytvorenie mostovej siete, spustenie Redis a overenie konektivity medzi kontajnermi
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# Výstup: PONG
27
28# Vyčistenie pracovných kontajnerov a zastavenie pieskoviska
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID

Ďalšie čítanie v tomto seriáli

  1. gvx: Spúšťanie kontajnerov Linux, Docker a Kubernetes v jednom neprivilegovanom procese: Architektonický prehľad gvx, jeho multiplatformných výpočtových enginov (whpx, hvf, systrap), VFS2 (erofs, ext4lite, hostfs v2) a netstack.
  2. Spúšťanie Docker, docker build a docker compose bez roota a používateľských menných priestorov (tento príspevok)
  3. Spúšťanie Docker-in-Docker vo vnútri gVisor a vnorených pieskovísk pomocou gvx: Ako gvx rieši modprobe, iptables-nft, overlay2 na gofer a vnorený SECCOMP_FILTER_FLAG_NEW_LISTENER (EBUSY) vo vnútri gVisor (runsc).
  4. Rýchle izolované Docker prostredia pre RL rollouty AI agentov pomocou gvx: Škálovanie paralelných prostredí SWE-bench a RL rolloutov pomocou resetov zväzkov .ext4 typu copy-on-write v trvaní 100 milisekúnd.