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

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
gvxbezpečný už z návrhu (nejde o obídenie eskalácie oprávnení): Na rozdiel od pripojenia hostiteľského/var/run/docker.sockalebo spusteniadocker run --privileged(ktoré poskytujú kontrolu ekvivalentnú roote nad jadrom hostiteľa),gvxneobchádza ani neoslabuje žiadnu bezpečnostnú politiku hostiteľa (PR_SET_NO_NEW_PRIVS,seccomp, AppArmor alebo obmedzenia neroot UID). Namiesto tohorungvuzamkne svoj vlastný hostiteľský proces (PR_SET_NO_NEW_PRIVS+ prísny BPFseccompallowlist) a spúšťadockerdvo vnútri používateľského jadra gVisor (Sentry) od Google, ktoré je odolné voči chybám pamäte. Všetky schopnostiUID 0v hosťovi, pripojeniaoverlayfs(ext4lite) a pravidláiptableszostá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)aleboclone(CLONE_NEWUSER). - Ubuntu 24.04+ aktivuje
kernel.apparmor_restrict_unprivileged_userns = 1. - Uzly Debianu alebo RHEL nastavujú
kernel.unprivileged_userns_clone = 0alebouser.max_user_namespaces = 0. newuidmapnemôže zapisovať do/proc/self/uid_map, pretože je aktívnyPR_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.

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)amount(2)pracujú nad pamäťovou hierarchiou úloh a tabuľkou pripojeníVFS2jadra Sentry (procfs,sysfs,tmpfs,devpts,cgroupfsv2), zatiaľ čo hostiteľský procesrungvbeží 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:
dockerdhosťa vytvorí mostdocker0(172.17.0.1/16), pripojí virtuálne páryvethpre vnorené kontajnery a naprogramuje reťazceNAT(DOCKER,POSTROUTING) aFILTERso zhodamiaddrtypeaconntrackvo vnútri sieťového zásobníkanetstackv používateľskom priestoregvx.- Odovzdanie
-p unix:~/.gvx/docker.sock:2375viaže hostiteľský socket Unix domény (režim0600) a smeruje spojenia priamo na port2375službydockerdvo vnútri sandboxu.
Porovnanie s existujúcimi nástrojmi pre kontajnery
| Schopnosť / obmedzenie | Štandardný Docker (dockerd) | Rootless Docker / Podman | Kaniko / Buildah (neprivilegovaný) | gvx (rungv + docker:dind) |
|---|---|---|---|---|
Vyžaduje root alebo sudo na hostiteľovi | Áno | Nie | Nie | Nie |
Funguje s PR_SET_NO_NEW_PRIVS=1 | Nie | Nie (newuidmap zlyhá) | Čiastočne | Áno |
Funguje, keď je unshare(CLONE_NEWUSER) blokované (EPERM) | N/A | Nie | Buildah: 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 Linux | Nie (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:
ptrace(PTRACE_ATTACH)na vlastnom podriadenom vlákne jedenkrát počas inicializácie:systrappri 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šetkyptracev celom systéme) alebo blokujeptracevo vonkajšom filtri seccomp, inicializáciasystrapsa nemôže dokončiť. (Priyama.ptrace_scope = 1, čo je predvolené nastavenie na Ubuntu a Debian,gvxuspeje, pretože sledovanie z rodiča na potomka je povolené.)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,gvxfunguje tak, keď je k dispozíciiSECCOMP_FILTER_FLAG_NEW_LISTENER, ako aj vtedy, keď vraciaEBUSYvo vnorenom sandboxe.mmap(..., PROT_EXEC, ...): Používa sa na mapovanie segmentov ELF hosťa a trampolínysystrapv 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
- 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,hostfsv2) anetstack. - Spúšťanie Dockeru, docker build a docker compose bez roota alebo používateľských menných priestorov (tento príspevok)
- Spúšťanie Docker-in-Docker vo vnútri gVisor a vnorených sandboxov pomocou gvx: Ako
gvxriešimodprobe,iptables-nft,overlay2nagofera vnorenýSECCOMP_FILTER_FLAG_NEW_LISTENER(EBUSY) vo vnútrigVisor(runsc). - Rýchle izolované prostredia Dockeru pre RL rollouty AI agentov pomocou gvx: Škálovanie paralelných prostredí
SWE-bencha RL rolloutov pomocou resetov zväzkov.ext4s copy-on-write v trvaní 100 milisekúnd.