Docker-in-Docker suorittaminen gVisorin sisällä ja sisäkkäisissä sandboxeissa gvx:n avulla

Pikaratkaisu: docker:dind-ympäristön turvallinen suorittaminen rajoitetussa gVisor (runsc) -sandboxissa
Pilvipohjaiset tekoälyagenttien laskentaympäristöt (kuten Google Agent Compute Engine -sandboxit), Kubernetes-agent-sandbox-käyttöönotot (RuntimeClass: gvisor) ja vahvistusoppimisen arviointiympäristöt eristävät luotettamattomat työkuormat gVisor (runsc) -konteissa erityisesti isäntäkernelin suojaamiseksi. Kun työkuorma oletusarvoisesti rajoitetussa runsc-kontissa yrittää asentaa docker.io- tai docker-ce-paketin apt-get-komennolla, käynnistää service docker start- tai dockerd-komennon sekä suorittaa docker build-komennon, se epäonnistuu, vaikka prosessilla on root (UID 0) kontin sisällä.
gvx (rungv) -työkalulla voit turvallisesti suorittaa docker:dind-, docker build- ja docker compose-komennot suoraan rajoitetun gVisor (runsc) -kontin sisällä (tai sisäkkäin gvx gvx:n sisällä) heikentämättä ulomman RuntimeClass-määrityksen turvallisuutta, paljastamatta isännän socketeja tai myöntämättä --privileged-oikeuksia.
Matalan vaivan ja tokenitehokas ratkaisu tekoälyagenteille: Kun toimitaan gVisor-pohjaisessa pilvi-agenttiympäristössä,
docker.io-paketin asentaminen ja manuaalinen kiertäminen/etc/init.d/docker-skriptinulimit-virheiden,iptables-nft-virheiden,iptables-legacy-raakasocket-estojen,overlay2-liitoshäiriöiden jadocker build-komennonrunc- /pivot_root-kaatumisten kanssa kuluttaa tyypillisesti yli 20 työkalukutsukierrosta ja kymmeniätuhansia tokeneita ennen täydellistä epäonnistumista.gvx(rungv) on matalan vaivan ja tokenitehokas suoraan käyttöön otettava ratkaisu: agentti suorittaa 3 komentoa yhdellä kierroksella käynnistääkseen toimivan käyttäjätilan Docker-daemonin ja suorittaa välittömästidocker build- jadocker compose-komennot muokkaamatta projektitiedostoja.
Ulomman sandboxin eristyksen säilyttäminen ja vahvistaminen:
rungv-komennon suorittaminenrunsc-kontin sisällä ei ohita yhtäkään ulomman gVisor-sandboxin asettamaa rajoitusta. Sen sijaangvxluo toisen, sisäkkäisen käyttäjätilan gVisor-Sentry-kernelin, jolla on omahostlockdown(PR_SET_NO_NEW_PRIVS+seccomp), oma muistinvarainennetstackja yksitiedostoinenext4lite-volyymi. Sisemmätdockerd- jarunc-kontit ovat kaksinkertaisesti sandboxattuja: niiden järjestelmäkutsut, liitokset ja raakapaketit eivät koskaan poistu sisemmästägvx-käyttäjätilan kernelistä.
1# Olemassa olevan gVisor (runsc) -kontin tai Kubernetes-Podin sisällä:
2# Asenna rungv SLSA-todennetusta GitHub-julkaisuarkistosta tai go install -komennolla:
3CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest
4
5mkdir -p ~/.gvx
6rungv run \
7 -p unix:~/.gvx/docker.sock:2375 \
8 -v gvx-docker:/var/lib/docker \
9 -e DOCKER_TLS_CERTDIR= \
10 docker:dind --tls=false &
11
12# Suorita tavalliset Docker-buildit ja -kontit sisemmän gvx-socketin kautta
13export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
14until docker info >/dev/null 2>&1; do sleep 0.5; done
15docker info --format '{{.Driver}} {{.OperatingSystem}}'
16# Tuloste: overlayfs Alpine Linux ... (konteroitu)
17
18docker build -t nested-app:test .
19docker run --rm nested-app:test
Viisi syytä, miksi suora Docker-asennus epäonnistuu gVisor-ympäristössä (runsc)
Kun käyttäjä tai tekoälykoodausagentti suorittaa apt-get install -y docker.io-komennon (tai docker-ce-komennon) tavallisessa runsc-kontissa ja yrittää käynnistää dockerd-daemonin, se kohtaa viisi peräkkäistä teknistä estettä.
1. Alustusskriptin (systemctl / service docker start ulimit) ja kernel-moduulin (modprobe 4.4.0) virheet
Kun apt-get install -y docker.io (tai docker-ce) on suoritettu, daemonin käynnistäminen komennolla systemctl start docker tai service docker start epäonnistuu välittömästi, koska PID 1 ei ole systemd ja /etc/init.d/docker yrittää nostaa ulimit -n-arvoa sandboxin kovan RLIMIT_NOFILE-katon yli. Kun dockerd käynnistetään sitten suoraan, alustusskriptit kutsuvat modprobe-komentoa ladatakseen moduulit overlay, bridge, br_netfilter, ip_tables ja nf_nat gVisorin virtuaalikernelin versiota (4.4.0) vastaan. Hakemistoa /lib/modules/4.4.0 ei ole olemassa:
1System has not been booted with systemd as init system (PID 1). Can't operate.
2Failed to connect to bus: Host is down
3/etc/init.d/docker: 62: ulimit: error setting limit (Operation not permitted)
4level=warning msg="Running modprobe bridge br_netfilter failed with message: modprobe: WARNING: Module bridge not found in directory /lib/modules/4.4.0\nmodprobe: WARNING: Module br_netfilter not found in directory /lib/modules/4.4.0\n, error: exit status 1"
5modprobe: FATAL: Module overlay not found in directory /lib/modules/4.4.0
6modprobe: FATAL: Module br_netfilter not found in directory /lib/modules/4.4.0
7modprobe: FATAL: Module ip_tables not found in directory /lib/modules/4.4.0
8modprobe: FATAL: Module nf_nat not found in directory /lib/modules/4.4.0
2. iptables-nft (nf_tables Netlink), iptables-legacy -raakasocket-estot ja docker0-sillan virheet
Ubuntu 22.04:ssä, Ubuntu 24.04:ssä ja Debian 11/12:ssa apt-get install docker.io valitsee oletusarvoisesti /usr/sbin/iptables-vaihtoehdoksi iptables-nft-työkalun (joka perustuu nf_tables-netlink-alijärjestelmään). Lisäksi vaikka käyttäjä tai agentti vaihtaisi iptables-legacy-työkaluun (update-alternatives --set iptables /usr/sbin/iptables-legacy), oletusarvoiset rajoitetut gVisor-sandboxit (--net-raw=false) hylkäävät AF_INET, SOCK_RAW-socketit virheellä EPERM. Tämän seurauksena iptables-legacy epäonnistuu virheellä Permission denied (you must be root) myös silloin, kun suoritus tapahtuu UID 0 -käyttäjänä:
1failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to register "bridge" driver: failed to create NAT chain DOCKER: iptables failed: iptables --wait -t nat -N DOCKER: iptables v1.8.10 (nf_tables): Could not fetch rule set generation id: Invalid argument
2iptables v1.8.10 (legacy): can't initialize iptables table `nat': Permission denied (you must be root)
3Perhaps iptables or your kernel needs to be upgraded.
4iptables v1.8.10 (legacy): can't initialize iptables table 'nat': Table does not exist (do you need to insmod?)
5iptables: No chain/target/match by that name.
6Unable to enable SKIP DNAT rule: (iptables failed: iptables --wait -t nat -I DOCKER -i docker0 -j RETURN: iptables: No chain/target/match by that name.)
7Error initializing network controller: error obtaining controller instance: failed to create bridge "docker0": operation not permitted
3. overlay2-virhe gofer- / 9p-juurijärjestelmässä ja vfs-levytilan ehtyminen
Ellei ulomman runsc-kontin ylläpitäjä ole nimenomaisesti määrittänyt erillistä tmpfs- tai filestore-liitosta kohteeseen /var/lib/docker, kontin juurijärjestelmä perustuu gVisorin gofer-palvelimeen (9p/lisafs) tai sisäkkäiseen overlay-liitokseen. overlay2-liitos kohteeseen /var/lib/docker epäonnistuu:
1level=error msg="[graphdriver] prior storage driver overlay2 failed: driver not supported"
2failed to mount overlay: invalid argument
3Error starting daemon: error initializing graphdriver: driver not supported: overlay2
Kun käyttäjät tai agentit yrittävät kiertää tämän virheen käynnistämällä dockerd --storage-driver=vfs-komennon, Docker poistaa kokonaan Copy-on-Write-kerrosten jakamisen käytöstä. Jokainen Dockerfile-tiedoston RUN-ohje suorittaa koko juurijärjestelmän täyden rekursiivisen kopioinnin (dirCopy). Tämä kertoo 500 MB:n imagen kymmenen rakennusvaiheen aikana 5 GB:n kirjoituksiksi ja päättää sandboxin virheeseen no space left on device tai I/O-aikakatkaisuun.
4. docker build (BuildKit) ja sisäkkäisen runc:n virheet (pivot_root, /proc, cgroupit ja DNS)
Vaikka dockerd pakotettaisiin käynnistymään komennolla dockerd --iptables=false --ip6tables=false --bridge=none --storage-driver=vfs, komennon docker build -t app . tai docker run suoritus epäonnistuu välittömästi, kun BuildKit tai runc käynnistää rakennuskontin:
1ERROR: failed to solve: process "/bin/sh -c ..." did not complete successfully:
2docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process:
3error during container init: error jailing process inside rootfs: pivot_root .: invalid argument
4error during container init: error mounting "cgroup" to rootfs at "/sys/fs/cgroup": mount cgroup:/sys/fs/cgroup/cpuset (via /proc/self/fd/6), flags: 0xf, data: cpuset: operation not permitted
5cgroups: cgroup mountpoint does not exist: unknown
6runc create failed: systemd not running on this host: cannot use systemd cgroup manager
7error during container init: error mounting "proc" to rootfs at "/proc": mount proc:/proc (via /proc/self/fd/6), flags: 0xe: operation not permitted
8error during container init: error loading seccomp filter into kernel: operation not permitted
9Temporary failure resolving 'deb.debian.org'
10Temporary failure resolving 'archive.ubuntu.com'
5. Sisäkkäinen runsc epäonnistuu virheellä SECCOMP_FILTER_FLAG_NEW_LISTENER (EBUSY)
Jos käyttäjä yrittää suorittaa toista runsc-instanssia runsc:n sisällä systrap-alustalla, alustus keskeytyy välittömästi:
1creating subprocess: sysmsg: failed to initialize thread: failed to install seccomp notify rules: device or resource busy
Sekä Linux-kernel että gVisor Sentry sallivat enintään yhden SECCOMP_FILTER_FLAG_NEW_LISTENER- (SECCOMP_RET_USER_NOTIF) -kuuntelijan tehtäväpuuta kohden. Koska ulompi sandbox pitää jo tuon kuuntelijapaikan hallussaan, sisemmän seccomp(2)-kutsun paluuarvo on EBUSY (device or resource busy).
Miten gvx ratkaisee sisäkkäisen suorituksen gVisor-ympäristössä
gvx käsittelee kaikki viisi estettä omassa käyttäjätilan kernelissään ja alustakerroksessaan. Tämä mahdollistaa rungv-komennon suorittamisen ulomman gVisor (runsc) -kontin sisällä tai jopa rekursiivisesti toisen gvx-kontin sisällä (gvx gvx:ssä).

1. Automaattinen systrap-varajärjestelmä EBUSY-tilanteesta _RUN_SYSCALL_LOOP futex-silmukkaan
Linuxin systrap-alustassa jokainen stub-aliprosessi suorittaa kahdenlaisia säikeitä:
- Vierassuorituksen säikeet (
sysmsg): Vangitsevat vieraan järjestelmäkutsutSECCOMP_RET_TRAP-mekanismilla (SIGSYS) ja binääripatchatuillajmp rel32-trampoliineilla (syshandler). Ne vaihtavat rekisteritilan Sentryn kanssa jaetun muistin jaSYS_FUTEX-kutsun avulla. - Stub-
syscallThread(syscall_thread.go): Suorittaa osoiteavaruuden järjestelmäkutsut (mmap,munmap,clone) stub-aliprosessin sisällä Sentryn puolesta.
Upstream-gVisorissa syscallThread tukee kahta heräätystilaa tiedostoissa stub_amd64.s ja stub_arm64.s:
- Seccomp User-Notify -silmukka (
_RUN_SECCOMP_LOOP = 6, kunt.seccompNotify != nil):syscallThread.init()-vaiheessat.installSeccompNotify()asentaaSECCOMP_RET_USER_NOTIF-kuuntelijan (seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, stubSyscallRules)), jotta Sentry voi lähettää pyyntöjäioctl(SECCOMP_IOCTL_NOTIF_SEND)- jaioctl(SECCOMP_IOCTL_NOTIF_RECV)-kutsujen kautta. - Futex-järjestelmäkutsusilmukka (
_RUN_SYSCALL_LOOP = 5, kunt.seccompNotify == nil):syscallThreadsuorittaasyscall_loop-silmukan (stub_amd64.s/stub_arm64.s). Se nukkuuSYS_FUTEX-kutsun (FUTEX_WAIT) varassa jaetun 2-sivuisensyscallSentryMessage-alueen (PROT_READstubissa) jasyscallStubMessage-alueen (PROT_READ | PROT_WRITEstubissa) yllä.
Upstream-gVisor kutsuu ehdoitta t.installSeccompNotify()-funktiota aina, kun seccompNotifyIsSupported on tosi, ja keskeyttää stubin luomisen, jos seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, ...) palauttaa EBUSY.
gvx:ssä (pkg/sentry/platform/systrap/seccomp_notify_xplat.go ja syscall_thread.go) syscallThread.init() kutsuu t.installSeccompNotifyIfFree()-funktiota:
1// pkg/sentry/platform/systrap/seccomp_notify_xplat.go
2var seccompNotifyBusy atomic.Bool
3
4func (t *syscallThread) installSeccompNotifyIfFree() (*os.File, error) {
5 if seccompNotifyBusy.Load() {
6 return nil, nil
7 }
8 f, err := t.installSeccompNotify()
9 if errors.Is(err, unix.EBUSY) {
10 if seccompNotifyBusy.CompareAndSwap(false, true) {
11 log.Warningf("Another seccomp listener exists in the filter chain of the host process. " +
12 "The stub threads use the slower futex loop instead of seccomp-unotify.")
13 }
14 return nil, nil
15 }
16 return f, err
17}
Kun installSeccompNotify() palauttaa unix.EBUSY-virheen, koska ulompi gVisor (runsc) tai ulompi gvx-sandbox pitää jo prosessin suodatinketjun ainoaa kuuntelijapaikkaa hallussaan:
installSeccompNotifyIfFree()asettaaseccompNotifyBusy-muuttujan (atomic.Bool), kirjaa yhden varoituksen prosessille ja jättäät.seccompNotify == nil-arvon kyseiselle stubille sekä kaikille seuraaville stubeille.syscallThread.detach()-funktiossa (syscall_thread_amd64.gojasyscall_thread_arm64.go) ehdont.seccompNotify == nilollessa tosi asetetaanregs.Rbx = _RUN_SYSCALL_LOOP(5) arkkitehtuurillaamd64(tairegs.Regs[9] = _RUN_SYSCALL_LOOParkkitehtuurillaarm64)_RUN_SECCOMP_LOOP-arvon (6) sijaan.syscallThread.syscall()-funktiossa (syscall_thread.go) sisempi Sentry lähettää stub-järjestelmäkutsut yksinomaan jaetun muistin jaSYS_FUTEX-kutsun kautta:
1// pkg/sentry/platform/systrap/syscall_thread.go
2// Ilmoita syscall-säikeelle uudesta syscall-pyynnöstä.
3atomic.AddUint32(&sentryMsg.state, 1)
4futexWakeUint32(&sentryMsg.state)
5
6// Odota vastausta.
7futexWaitWake(&sentryMsg.state, atomic.LoadUint32(&sentryMsg.state))
Koska SECCOMP_RET_TRAP-suodattimet pinoavat vieraiden säikeiden kohdalla mielivaltaisen syvälle ja syscallThread siirtyy varajärjestelmänä silmukkaan _RUN_SYSCALL_LOOP pyytämättä toista käyttäjäilmoituskuuntelijaa, gvx toimii siististi runsc:n sisällä ja tukee rekursiivista sisäkkäisyyttä (gvx gvx:ssä).
2. Natiivi overlayfs yksitiedostoisilla ext4lite-volyymeilla
Jotta overlayfs-liitosta ei tarvitse asentaa ulomman gVisor-kontin gofer-juurijärjestelmän (9p/lisafs) päälle, -v gvx-docker:/var/lib/docker tallentaa kohteen /var/lib/docker sisäisen ext4lite-tiedostojärjestelmän gvx:stä yhteen tavalliseen tiedostoon (gvx-docker.ext4).
Ulomman runsc:n näkökulmasta gvx-docker.ext4 on vain yksi tavallinen tiedosto, jota käsitellään kutsuilla pread64(2) ja pwrite64(2). gvx:n sisällä ext4lite tarjoaa JBD2-journaloidun ext4-tiedostojärjestelmän, jossa on laajennetut attribuutit trusted.overlay.* ja security.capability, merkkilaitteiden whiteout-solmut (0:0), d_type-hakemistomerkinnät sekä RENAME_EXCHANGE-tuki. Tämän ansiosta sisempi dockerd voi käyttää omaa natiivia overlayfs-tallennusajuriaan.
3. Itsenäinen netstack docker0-sillalla ja iptables-legacy-työkalulla
gvx:n sisällä dockerd ei ole vuorovaikutuksessa ulomman runsc-kontin verkkoliittymien tai netfilter-taulujen kanssa. Sen sijaan:
dockerdluodocker0-siltansa javeth-parinsagvx:n omassa käyttäjätilannetstack-kerroksessa.- Kaikki
iptables-legacy-säännöt (-t nat -N DOCKER,MASQUERADE,conntrack,addrtype) suoritetaangvx:n sisäistä netfilter-toteutusta vastaan. - Sisäkkäiset
runc-kontit liittävät/proc-,/sys- ja/sys/fs/cgroup-hierarkiansagvx:n muistinvaraiseenVFS2-liitostauluun eivätkä koskaan kutsu ulommanrunsc:nmount(2)-järjestelmäkutsua.
Sisäkkäisen docker:dind-ympäristön todentaminen gVisor-ympäristössä (tai gvx gvx:ssä)
Voit todentaa molemmat skenaariot (rungv runsc:n sisällä tai rungv rungv:n sisällä) seuraavilla komennoilla:
1# Ulomman gVisor (runsc) -kontin tai ulomman rungv-kontin sisällä:
2mkdir -p ~/.gvx
3rungv run \
4 -p unix:~/.gvx/docker.sock:2375 \
5 -v gvx-docker:/var/lib/docker \
6 -e DOCKER_TLS_CERTDIR= \
7 docker:dind --tls=false &
8DIND_PID=$!
9
10export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
11until docker info >/dev/null 2>&1; do sleep 0.5; done
12
13# Varmista, että sisempi Docker-daemon käyttää natiivia overlayfs-tallennusajuria (ei vfs)
14docker info --format 'Storage Driver: {{.Driver}}'
15# Tuloste: Storage Driver: overlayfs
16
17# Suorita monikerroksinen build ja käynnistä sisäkkäinen kontti
18mkdir -p /tmp/nested-test && cd /tmp/nested-test
19cat <<'EOF' > Dockerfile
20FROM alpine:3.20
21RUN adduser -D -u 1001 appuser && mkdir -p /app && chown appuser:appuser /app
22USER appuser
23WORKDIR /app
24RUN echo "nested-build-ok" > status.txt
25CMD ["cat", "/app/status.txt"]
26EOF
27
28docker build -t nested-test:latest .
29docker run --rm nested-test:latest
30# Tuloste: nested-build-ok
31
32kill -INT $DIND_PID
Lisälukemista tästä sarjasta
- gvx: Running Linux Containers, Docker, and Kubernetes in One Unprivileged Process: Yleiskatsaus
gvx:stä Windowsissa (whpx,systrap), macOS:ssä (hvf) ja Linuxissa (systrap). - Running Docker, docker build, and docker compose Without Root or User Namespaces: Miten
gvxsuorittaadocker:dind-ympäristön silloin, kunroot,sudo,unshare(CLONE_NEWUSER),mount(2)ja/dev/fuseon estetty. - Running Docker-in-Docker Inside gVisor and Nested Sandboxes with gvx (tämä kirjoitus)
- Fast Isolated Docker Environments for AI Agent RL Rollouts with gvx: Miten
gvx:n ja yksitiedostoistenext4lite-copy-on-write-tilannekuvien avulla voi ajaa satoja rinnakkaisiaSWE-bench- ja RL-rollout-ympäristöjä 100 millisekunnin palautuksilla.