GoSuda

Spúšťanie Dockeru, 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 Linuxového kontajnera, CI runnera, Kubernetes Pod alebo sandboxu AI 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 riešením s viacvrstvovou obranou na spustenie úplného enginu docker:dind v používateľskom priestore.

Prečo je gvx bezpečný už z návrhu (nejde o obídenie 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 alebo obmedzenia neroot UID). Namiesto toho rungv uzamkne svoj vlastný hostiteľský proces (PR_SET_NO_NEW_PRIVS + prísny BPF seccomp allowlist) a spúšťa dockerd vo vnútri používateľského jadra gVisor (Sentry) od Google, ktoré je odolné voči chybám pamäte. Všetky schopnosti UID 0 v hosťovi, pripojenia overlayfs (ext4lite) a pravidlá iptables zostávajú prísne izolované vo vnútri procesu sandboxu.

 1# 1. Nainštalujte rungv do používateľského priestoru (predkompilované vydanie overené cez SLSA alebo go install)
 2# Možnosť A: Overte a rozbaľte predkompilovaný binárny súbor overený cez 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 nainštalujte 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. Spustite docker:dind vo vnútri gvx s jedným súborovým zväzkom ext4lite
12#    Preposiela ~/.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. Nasmerujte štandardné 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 ... (containerized)
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 sandboxoch

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

1. Žiadny socket démona Docker na hostiteľovi

Spustenie docker ps alebo docker build okamžite zlyhá, ak nie je hostiteľský /var/run/docker.sock pripojený do prostredia:

1Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

2. PR_SET_NO_NEW_PRIVS blokuje sudo a pomocné programy setuid

Vyztužené kontajnery (securityContext.allowPrivilegeEscalation: false v Kubernetes, --security-opt=no-new-privileges v Dockeri alebo sandboxy exebox / bwrap) nastavujú bit Linuxu PR_SET_NO_NEW_PRIVS v 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 (user namespaces, CLONE_NEWUSER). Zlyhajú vždy, keď:

  • Predvolený profil seccomp vonkajšieho kontajnera blokuje unshare(2) alebo clone(CLONE_NEWUSER).
  • Ubuntu 24.04+ aktivuje kernel.apparmor_restrict_unprivileged_userns = 1.
  • Uzly Debianu alebo RHEL nastavujú 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) je blokovaný bez CAP_SYS_ADMIN na hostiteľovi

Aj keď prostredie povoľuje CLONE_NEWUSER, pripojenie nového /proc vo vnútri vnoreného menného priestoru pripojení zlyhá, ak vonkajší kontajner maskuje podstromy /proc (/proc/acpi, /proc/kcore, /proc/keys) alebo zakázal mount(2) v 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. Chýbajúci /dev/fuse narušuje fuse-overlayfs

Rootless enginy kontajnerov, ktoré nedokážu pripojiť jadrový overlayfs, sa vracajú k fuse-overlayfs. V minimalistických kontajneroch alebo v Kubernetes podoch bez priechodu 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 od hostiteľského jadra Linuxu nežiada 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é používateľské jadro Linuxu (gVisor Sentry) a sieťový zásobník v používateľskom priestore (netstack) vo vnútri jedného bežného neprivilegovaného procesu.

Spustenie neprivilegovaného Docker-in-Docker pomocou 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 jadra Sentry v rámci procesu namiesto hostiteľského jadra:

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

2. Natívny overlayfs na jednosúborových zväzkoch ext4lite

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 do /var/lib/docker.

Keďže ext4lite implementuje súborový systém ext4 s žurnálom JBD2 vo vnútri jedného súboru hostiteľa (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 vo vnútri hosťa svoj natívny ovládač úložiska overlayfs. Viacstupňové operácie docker build zdieľajú vrstvy Copy-on-Write bez /dev/fuse a bez nárastu miesta na disku, ktorý spôsobuje záložný ovládač vfs Dockeru.

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 sieťového zásobníka netstack v používateľskom priestore gvx.
  • Odovzdanie -p unix:~/.gvx/docker.sock:2375 viaže hostiteľský socket Unix domény (režim 0600) a smeruje spojenia priamo na port 2375 služby dockerd vo vnútri sandboxu.

Porovnanie s existujúcimi nástrojmi pre kontajnery

Schopnosť / obmedzenieŠtandardný Docker (dockerd)Rootless Docker / PodmanKaniko / Buildah (neprivilegovaný)gvx (rungv + docker:dind)
Vyžaduje root alebo sudo na hostiteľoviÁ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)Len cez pomalú kópiu vfsÁnoÁno (overlayfs na ext4lite)
Podporuje docker build (BuildKit)ÁnoÁno (ak je userns povolené)Iba zostavenie obrazu (bez runtime)Áno
Podporuje docker run a docker compose upÁnoÁno (ak je userns povolené)NieÁno
Beží na Windows a macOS bez virtuálneho stroja LinuxNie (vyžaduje WSL2 / VM)Nie (vyžaduje virtuálny stroj Linux)NieÁno (whpx, hvf, systrap)

Presné požiadavky na jadro hostiteľa Linux

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

  1. ptrace(PTRACE_ATTACH) na vlastnom podriadenom vlákne jedenkrát počas inicializácie: systrap pri spustení vytvorí lokálne pomocné stub vlákno, pripojí sa k nemu, aby inicializoval zdieľaný kontext pamäte, a potom sa odpojí. Ak hostiteľ nastaví kernel.yama.ptrace_scope = 3 (čo zakazuje 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, čo je predvolené nastavenie na Ubuntu a Debian, gvx uspeje, pretože sledovanie z rodiča na potomka je povolené.)
  2. seccomp(SECCOMP_SET_MODE_FILTER): Používa sa na zachytávanie systémových volaní hosťa z pomocných stub vlákien kontajnera. Ako je popísané v 3. časti tejto série, gvx funguje tak, keď je k dispozícii SECCOMP_FILTER_FLAG_NEW_LISTENER, ako aj vtedy, keď vracia EBUSY vo vnorenom sandboxe.
  3. mmap(..., PROT_EXEC, ...): Používa sa na mapovanie segmentov ELF hosťa a trampolíny systrap v zdieľanej pamäti.

Príklad od začiatku do konca: zostavenie a testovanie viackontajnerovej aplikácie bez roota

Nasledujúci skript spúšťa 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 sandboxu
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID

Ďalšie čítanie v tejto sérii

  1. gvx: Spúšťanie Linuxových kontajnerov, Dockeru a Kubernetes v jednom neprivilegovanom procese: Architektonický prehľad gvx, jeho multiplatformných vykonávacích enginov (whpx, hvf, systrap), VFS2 (erofs, ext4lite, hostfs v2) a netstack.
  2. Spúšťanie Dockeru, docker build a docker compose bez roota alebo používateľských menných priestorov (tento príspevok)
  3. Spúšťanie Docker-in-Docker vo vnútri gVisor a vnorených sandboxov 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é prostredia Dockeru pre RL rollouty AI agentov pomocou gvx: Škálovanie paralelných prostredí SWE-bench a RL rolloutov pomocou resetov zväzkov .ext4 s copy-on-write v trvaní 100 milisekúnd.