gvx ile gVisor ve İç İçe Sandbox'larda Docker-in-Docker Çalıştırma

Hızlı Çözüm: Yetkisiz Bir gVisor (runsc) Sandbox İçinde docker:dind Dosyasını Güvenli Biçimde Çalıştırma
Bulut tabanlı YZ ajanı hesaplama ortamları (Google Agent Compute Engine sandbox'ları gibi), Kubernetes agent-sandbox dağıtımları (RuntimeClass: gvisor) ve pekiştirmeli öğrenme değerlendirme altyapıları, güvenilmeyen iş yüklerini özellikle ana makine çekirdeğini korumak amacıyla gVisor (runsc) konteynerleri içinde izole eder. Varsayılan yetkisiz bir runsc konteyneri içindeki bir iş yükü apt-get ile docker.io veya docker-ce kurmaya, service docker start veya dockerd komutunu başlatmaya ve docker build çalıştırmaya çalıştığında, konteyner içinde root (UID 0) yetkisine sahip olsa bile başarısız olur.
gvx (rungv) ile docker:dind, docker build ve docker compose komutlarını yetkisiz bir gVisor (runsc) konteyneri içinde doğrudan güvenli biçimde çalıştırabilirsiniz (veya gvx'i gvx içine yerleştirebilirsiniz). Bunu yaparken dış RuntimeClass yapılandırmasını zayıflatmaz, ana makine soketlerini açığa çıkarmaz ve --privileged yetkisi vermezsiniz.
Düşük Çaba Gerektiren, Token Verimli Çözüm: YZ Ajanları İçin: Gerçek bir gVisor destekli bulut ajanı ortamında
docker.iokurmaya ve/etc/init.d/dockerulimithatalarını,iptables-nfthatalarını,iptables-legacyham soket reddlerini,overlay2bağlama hatalarını vedocker buildrunc/pivot_rootçökmelerini manuel olarak aşmaya çalışmak, genellikle tamamen başarısız olmadan önce 20'den fazla araç çağrısı turunu ve on binlerce token'ı tüketir.gvx(rungv) ise düşük çaba gerektiren, token verimli ve doğrudan kullanılabilir bir çözümdür: ajan tek bir turda 3 komut çalıştırarak çalışan bir kullanıcı alanı Docker daemon'ı başlatır ve proje dosyalarını değiştirmeden hemendocker buildvedocker composekomutlarını yürütür.
Dış Sandbox İzolasyonunun Korunması ve Güçlendirilmesi:
runsciçinderungvçalıştırmak, dışgVisorsandbox'ı tarafından uygulanan hiçbir kısıtlamayı atlamaz. Bunun yerinegvx, kendihostlockdown(PR_SET_NO_NEW_PRIVS+seccomp) mekanizmasına, kendi bellek içinetstackyapısına ve tek dosyalık birext4litebirimine sahip ikinci, iç içe bir kullanıcı alanı gVisorSentryçekirdeği oluşturur. İçdockerdverunckonteynerleri çift katmanlı olarak sandbox'lanır: sistem çağrıları, bağlamalar ve ham paketler hiçbir zaman içgvxkullanıcı alanı çekirdeğinden çıkmaz.
1# Mevcut bir gVisor (runsc) konteyneri veya Kubernetes Pod'u içinde:
2# rungv'i SLSA ile doğrulanmış bir GitHub sürüm arşivinden kurun veya go install ile yükleyin:
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# Standart Docker build ve konteynerlerini iç gvx soketi üzerinden çalıştırın
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# Çıktı: overlayfs Alpine Linux ... (konteynerleştirilmiş)
17
18docker build -t nested-app:test .
19docker run --rm nested-app:test
gVisor (runsc) İçinde Doğrudan Docker Kurulumunun Başarısız Olmasının Beş Nedeni
Bir kullanıcı veya YZ kodlama ajanı standart bir runsc konteyneri içinde apt-get install -y docker.io (veya docker-ce) komutunu çalıştırıp dockerd başlatmaya çalıştığında, art arda gelen beş teknik engelle karşılaşır.
1. Init Betiği (systemctl / service docker start ulimit) ve Çekirdek Modülü (modprobe 4.4.0) Hataları
apt-get install -y docker.io (veya docker-ce) komutundan sonra daemon'ı systemctl start docker veya service docker start ile başlatma girişimi hemen başarısız olur, çünkü PID 1 systemd değildir ve /etc/init.d/docker betiği ulimit -n değerini sandbox'ın sert RLIMIT_NOFILE üst sınırının üzerine çıkarmaya çalışır. dockerd doğrudan çağrıldığında ise başlatma betikleri, gVisor'ın sanal çekirdek sürümü (4.4.0) için overlay, bridge, br_netfilter, ip_tables ve nf_nat modüllerini yüklemek üzere modprobe çağırır; oysa /lib/modules/4.4.0 dizini mevcut değildir:
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 Ham Soket Reddleri ve docker0 Köprü Hataları
Ubuntu 22.04, Ubuntu 24.04 ve Debian 11/12 üzerinde apt-get install docker.io komutu, varsayılan /usr/sbin/iptables alternatifi olarak iptables-nft seçer (bu, nf_tables netlink alt sistemini kullanır). Ayrıca kullanıcı veya ajan iptables-legacy aracına geçse bile (update-alternatives --set iptables /usr/sbin/iptables-legacy), varsayılan yetkisiz gVisor sandbox'ları (--net-raw=false) AF_INET, SOCK_RAW soketlerini EPERM ile reddeder. Bu durum, UID 0 olarak çalışılsa bile iptables-legacy aracının Permission denied (you must be root) hatasıyla başarısız olmasına yol açar:
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. gofer / 9p Rootfs Üzerinde overlay2 Hatası ve vfs Disk Tükenmesi
Dış runsc yöneticisi /var/lib/docker konumunda özel bir tmpfs veya filestore bağlamasını açıkça önceden yapılandırmadıysa, konteyner rootfs'i gVisor'ın gofer (9p/lisafs) yapısı veya iç içe bir overlay bağlaması üzerinde bulunur. /var/lib/docker üzerine overlay2 bağlanması başarısız olur:
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
Kullanıcılar veya ajanlar bu hatayı dockerd --storage-driver=vfs ile başlatarak aşmaya çalıştığında, Docker Copy-on-Write katman paylaşımını tamamen devre dışı bırakır. Bir Dockerfile içindeki her RUN talimatı, kök dosya sisteminin tamamının tam özyinelemeli kopyasını (dirCopy) oluşturur. Bu da 10 build adımına yayılan 500 MB'lık bir imajı 5 GB yazma işlemine dönüştürür ve sandbox'ın no space left on device hatasıyla ya da G/Ç zaman aşımıyla sonlanmasına neden olur.
4. docker build (BuildKit) ve İç İçe runc Hataları (pivot_root, /proc, Cgroup'lar ve DNS)
dockerd dockerd --iptables=false --ip6tables=false --bridge=none --storage-driver=vfs ile zorla başlatılsa bile, BuildKit veya runc build konteynerini başlattığında docker build -t app . veya docker run komutu hemen başarısız olur:
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. İç İçe runsc, SECCOMP_FILTER_FLAG_NEW_LISTENER (EBUSY) Hatasıyla Başarısız Olur
Bir kullanıcı systrap platformunu kullanarak runsc içinde başka bir runsc örneği çalıştırmaya çalışırsa, başlatma hemen iptal edilir:
1creating subprocess: sysmsg: failed to initialize thread: failed to install seccomp notify rules: device or resource busy
Hem Linux çekirdeği hem de gVisor Sentry, bir görev hiyerarşisi başına en fazla bir SECCOMP_FILTER_FLAG_NEW_LISTENER (SECCOMP_RET_USER_NOTIF) dinleyicisine izin verir. Dış sandbox bu dinleyici yuvasını zaten tuttuğu için, iç seccomp(2) çağrısı EBUSY (device or resource busy) döndürür.
gvx'in gVisor İçinde İç İçe Çalıştırmayı Nasıl Çözdüğü
gvx, beş engelin tamamını kendi kullanıcı alanı çekirdeği ve platform katmanı içinde ele alır ve rungv'in dış bir gVisor (runsc) konteyneri içinde, hatta başka bir gvx konteyneri içinde özyinelemeli olarak çalışmasını sağlar (gvx içinde gvx).

1. EBUSY Hatasından _RUN_SYSCALL_LOOP Futex Döngüsüne Otomatik systrap Geri Dönüşü
Linux systrap içinde her stub alt süreci iki tür iş parçacığı çalıştırır:
- Konuk çalıştırma iş parçacıkları (
sysmsg): Konuk sistem çağrılarınıSECCOMP_RET_TRAP(SIGSYS) ve ikili olarak yamalanmışjmp rel32trampolinleri (syshandler) aracılığıyla yakalar; kayıt durumunu paylaşılan bellek veSYS_FUTEXüzerinden Sentry ile değiş tokuş eder. - Bir stub
syscallThread(syscall_thread.go): Sentry adına, adres alanı sistem çağrılarını (mmap,munmap,clone) stub alt süreci içinde çalıştırır.
Upstream gVisor'da syscallThread, stub_amd64.s ve stub_arm64.s dosyalarında iki uyandırma modunu destekler:
- Seccomp User-Notify Döngüsü (
_RUN_SECCOMP_LOOP = 6,t.seccompNotify != nilolduğunda):syscallThread.init()sırasındat.installSeccompNotify(), Sentry'ninioctl(SECCOMP_IOCTL_NOTIF_SEND)veioctl(SECCOMP_IOCTL_NOTIF_RECV)aracılığıyla istekleri gönderebilmesi için birSECCOMP_RET_USER_NOTIFdinleyicisi kurar (seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, stubSyscallRules)). - Futex Sistem Çağrısı Döngüsü (
_RUN_SYSCALL_LOOP = 5,t.seccompNotify == nilolduğunda):syscallThread,syscall_loop(stub_amd64.s/stub_arm64.s) fonksiyonunu çalıştırır ve paylaşılan 2 sayfalıksyscallSentryMessage(stub içindePROT_READ) ilesyscallStubMessage(stub içindePROT_READ | PROT_WRITE) bölgesi üzerindeSYS_FUTEX(FUTEX_WAIT) ile uyku moduna geçer.
Upstream gVisor, seccompNotifyIsSupported doğru olduğunda koşulsuz olarak t.installSeccompNotify() çağırır ve seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, ...) EBUSY döndürürse stub oluşturmayı iptal eder.
gvx içinde (pkg/sentry/platform/systrap/seccomp_notify_xplat.go ve syscall_thread.go dosyalarında), syscallThread.init() t.installSeccompNotifyIfFree() fonksiyonunu çağırır:
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}
installSeccompNotify() fonksiyonu, dış bir gVisor (runsc) veya dış gvx sandbox'ı süreç filtre zincirinin tek dinleyici yuvasını zaten tuttuğu için unix.EBUSY döndürdüğünde:
installSeccompNotifyIfFree()seccompNotifyBusy(atomic.Bool) değerini ayarlar, süreç için tek bir uyarı günlüğe yazar ve o stub ile sonraki tüm stub'lar içint.seccompNotify == nildurumunu korur.syscallThread.detach()içinde (syscall_thread_amd64.govesyscall_thread_arm64.go),t.seccompNotify == nilolduğunda_RUN_SECCOMP_LOOP(6) yerineamd64üzerinderegs.Rbx = _RUN_SYSCALL_LOOP(5),arm64üzerinde iseregs.Regs[9] = _RUN_SYSCALL_LOOPatanır.syscallThread.syscall()(syscall_thread.go) içinde iç Sentry, stub sistem çağrılarını yalnızca paylaşılan bellek veSYS_FUTEXüzerinden gönderir:
1// pkg/sentry/platform/systrap/syscall_thread.go
2// Notify the syscall thread about a new syscall request.
3atomic.AddUint32(&sentryMsg.state, 1)
4futexWakeUint32(&sentryMsg.state)
5
6// Wait for reply.
7futexWaitWake(&sentryMsg.state, atomic.LoadUint32(&sentryMsg.state))
SECCOMP_RET_TRAP filtreleri konuk iş parçacıklarında keyfi derinlikte yığılabildiği ve syscallThread, ikinci bir kullanıcı bildirim dinleyicisi talep etmeden _RUN_SYSCALL_LOOP moduna geri döndüğü için, gvx runsc içinde sorunsuz çalışır ve özyinelemeli iç içe geçmeyi (gvx içinde gvx) destekler.
2. Tek Dosyalı ext4lite Birimleri Üzerinde Yerel overlayfs
Dış gVisor konteynerinin gofer (9p/lisafs) kök dosya sistemi üzerine overlayfs bağlamaktan kaçınmak için -v gvx-docker:/var/lib/docker parametresi, /var/lib/docker dizinini gvx'in dahili ext4lite dosya sistemiyle destekler; bu dosya sistemi tek bir normal dosyada (gvx-docker.ext4) saklanır.
Dış runsc için gvx-docker.ext4 yalnızca pread64(2) ve pwrite64(2) ile erişilen tek bir normal dosyadır. gvx içinde ise ext4lite, trusted.overlay.* ve security.capability genişletilmiş öznitelikleri, karakter aygıtı silme (whiteout) düğümleri (0:0), d_type dizin girişleri ve RENAME_EXCHANGE desteğiyle JBD2 günlüklemeli bir ext4 dosya sistemi sağlar. Bu sayede iç dockerd, kendi yerel overlayfs depolama sürücüsünü kullanabilir.
3. docker0 ve iptables-legacy ile Bağımsız netstack
gvx içinde dockerd, dış runsc konteynerinin ağ arayüzleri veya netfilter tablolarıyla etkileşime girmez. Bunun yerine:
dockerd,docker0köprüsünü vevethçiftlerinigvx'in kendi kullanıcı alanınetstackyapısı içinde oluşturur.- Tüm
iptables-legacykuralları (-t nat -N DOCKER,MASQUERADE,conntrack,addrtype),gvx'in dahili netfilter uygulamasına karşı yürütülür. - İç içe
runckonteynerleri,/proc,/sysve/sys/fs/cgrouphiyerarşilerinigvx'in bellek içiVFS2bağlama tablosunda bağlar; dışrunscmount(2)sistem çağrısını hiçbir zaman çağırmaz.
gVisor (veya gvx İçinde gvx) İçinde İç İçe docker:dind'i Doğrulama
Her iki senaryoyu (runsc içinde rungv veya rungv içinde rungv) aşağıdaki komutlarla doğrulayabilirsiniz:
1# Dış bir gVisor (runsc) konteyneri veya dış bir rungv konteyneri içinde:
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# İç Docker daemon'ının yerel overlayfs kullandığını (vfs değil) doğrulayın
14docker info --format 'Storage Driver: {{.Driver}}'
15# Çıktı: Storage Driver: overlayfs
16
17# Çok katmanlı bir build çalıştırın ve iç içe bir konteyner yürütün
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# Çıktı: nested-build-ok
31
32kill -INT $DIND_PID
Bu Serideki Diğer Yazılar
- gvx: Tek Bir Yetkisiz Süreçte Linux Konteynerleri, Docker ve Kubernetes Çalıştırma:
gvx'in Windows (whpx,systrap), macOS (hvf) ve Linux (systrap) üzerindeki genel bakışı. - Root veya Kullanıcı Ad Alanları Olmadan Docker, docker build ve docker compose Çalıştırma:
root,sudo,unshare(CLONE_NEWUSER),mount(2)ve/dev/fuseengellendiğindegvx'indocker:dind'i nasıl çalıştırdığı. - gvx ile gVisor ve İç İçe Sandbox'larda Docker-in-Docker Çalıştırma (bu yazı)
- gvx ile YZ Ajanı RL Rollout'ları İçin Hızlı İzole Docker Ortamları:
gvxve tek dosyalıext4litekopyala-yaz anlık görüntülerinin, 100 milisaniyelik sıfırlamalarla yüzlerce paralelSWE-benchve RL rollout ortamını nasıl çalıştırdığı.