gvx: Menjalankan Linux Containers, Docker, dan Kubernetes dalam Satu Unprivileged Process

Pendahuluan: Kontainer Berkeamanan Tinggi Tanpa Root atau Host Daemon
Menjalankan kontainer Linux, docker build, atau Kubernetes di dalam mesin pengembangan, runner CI, atau node cloud multi-tenant biasanya memaksa adanya kompromi keamanan (trade-off) yang tidak nyaman:
- Pada Linux, runtime OCI standar (
runc,crun,runsc) memerlukan salah satu dari hak istimewaroot, pengungkapan/var/run/docker.sockpada host,--privileged(CAP_SYS_ADMIN), atau user namespace (unshare(CLONE_NEWUSER)) yang memperluas permukaan serangan kernel host. - Pada Windows dan macOS, mesin kontainer (
Docker Desktop,Podman Machine,Lima,OrbStack) menjalankan kernel mesin virtual Linux lengkap di dalam WSL2, Hyper-V, atauVirtualization.frameworkdan menjalankan daemon latar belakang yang persisten di dalam VM tersebut.
gvx (CLI rungv dan SDK Go z.gfire.dev/gvx/pkg/gvx) menyediakan alternatif yang aman dan tanpa hak istimewa. Dibangun sebagai fork yang dapat dilacak dari upstream gVisor milik Google, yaitu kernel Linux ruang pengguna (Sentry) dan tumpukan jaringan ruang pengguna (netstack), gvx menjalankan binari ELF64 linux/amd64 dan linux/arm64 yang tidak dimodifikasi serta kontainer OCI secara langsung di dalam satu proses host tanpa hak istimewa yang mengunci dirinya sendiri pada Windows, macOS, dan Linux.
Jaminan Keamanan (Pertahanan Mendalam Tiga Lapis):
gvxtidak pernah melemahkan batas keamanan OS host, tidak pernah memasang driver kernel, dan tidak pernah memerlukanroot,sudo,--privilegedpada host, atauCLONE_NEWUSER. Sebelum mengeksekusi satu instruksi tamu pun,gvxmengunci proses host-nya sendiri menggunakan Windows Restricted Tokens dan Job Objects, macOS Seatbelt (deny default), atau LinuxPR_SET_NO_NEW_PRIVS+ BPFseccomp. Semua system call tamu, hak istimewaUID 0virtual, mount (VFS2), dan tabel jaringan (netstack) dibatasi secara ketat di dalam kernel GoSentryyang aman terhadap memori.
| Runtime | Host OS | Cold Start | Memory Footprint | Admin atau sudo Diperlukan | Background VM Daemon | Direct OCI Image Run |
|---|---|---|---|---|---|---|
gvx (whpx / hvf) | Windows (amd64, arm64) dan macOS (arm64) | ~20 ms | ~30 MiB | Tidak | Tidak | Ya |
gvx (systrap) | Windows (amd64, arm64) dan Linux (amd64, arm64) | ~25 hingga 65 ms | ~31 MiB | Tidak | Tidak | Ya |
| WSL2 / Docker Desktop | Windows dan macOS | 800 hingga 2500 ms | > 512 MiB | Ya (Pengaturan Awal) | Ya | Memerlukan Daemon |
| QEMU (Full System) | Windows dan macOS | 1500 hingga 4000 ms | > 256 MiB | Tidak | Tidak | Memerlukan Disk Image |
Tidak ada kernel VM Linux yang di-boot di Windows atau macOS. Tidak ada daemon latar belakang yang berjalan di host. Tidak ada hak istimewa root, sudo, user namespace (CLONE_NEWUSER), mount(2), driver TUN/TAP, atau CAP_NET_ADMIN yang diperlukan pada OS host. Di dalam proses tanpa hak istimewa tersebut, gvx menjalankan Docker-in-Docker (docker:dind), docker build, docker compose, dan Kubernetes High-Availability multi-node (k3s dengan etcd tertanam dan CNI Calico).
Siklus Hidup Kontainer dan Penguncian Host Tiga Lapis
Setiap sandbox gvx berjalan di dalam satu proses host tanpa hak istimewa dan menerapkan siklus hidup tiga fase: pra-membuka semua deskriptor file dan soket host yang diperlukan, mengunci proses OS host melalui pkg/hostlockdown, dan memasuki loop eksekusi Sentry di dalam proses.

Sebelum menyerahkan kendali ke instruksi tamu pertama, gvx pra-membuka deskriptor layer image .erofs yang di-cache, deskriptor file volume .ext4, handle *os.Root untuk /workspace, dan soket jaringan host, kemudian memanggil hostlockdown.Apply():
- Windows NT (
lockdown_windows.go): MemanggilAdjustTokenPrivilegesdenganSE_PRIVILEGE_REMOVEDuntuk menghapus hak istimewa secara langsung (in-place), mengikat token impersonasi terbatasDISABLE_MAX_PRIVILEGEpada integritas Low (S-1-16-4096), menetapkan proses ke Job Object anonim denganJOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1(memblokir pembuatan proses anak), dan mengaktifkanDisallowWin32kSystemCalls = 1melaluiSetProcessMitigationPolicy. - macOS XNU (
lockdown_darwin.go): MenetapkanRLIMIT_NPROC = 0dan menerapkan profil Seatbelt (sandbox_init_with_parameters) yang dimulai dari(version 1)(deny default)dan memblokirprocess-fork,process-exec*, serta semua pembukaan file host baru. - Linux (
lockdown_linux.go+pkg/gvx/syscallfilter_linux.go): MenetapkanPR_SET_NO_NEW_PRIVSdan memasang allowlist BPFseccompmilik Sentry di seluruh thread (SECCOMP_FILTER_FLAG_TSYNC) dengan aturanEPERMeksplisit untukexecve,execveat,clone,openat, dansocket.
Intersepsi System Call Lintas Platform dan Paging 4 KB (pkg/sentry/platform)
Binari ELF64 Linux mengasumsikan granularitas halaman memori 4 KB (0x1000) untuk pemetaan segmen mmap(MAP_FIXED). Namun, VirtualAlloc dan MapViewOfFile3 pada Windows menerapkan alignment 64 KB (0x10000), dan Apple Silicon macOS (darwin/arm64) menggunakan halaman host 16 KB (0x4000). Selain itu, setiap OS memerlukan mekanisme yang berbeda untuk mencegat instruksi Linux SYSCALL (amd64) dan SVC #0 (arm64) tanpa mem-boot VM Linux.
gvx memecahkan kedua masalah tersebut di empat backend eksekusi yang mengimplementasikan pkg/sentry/platform.Context:

1. Mesin Perangkat Keras Windows: platform/whpx (WinHvPlatform.dll)
Pada windows/amd64 dan windows/arm64, platform/whpx membuat partisi hypervisor ringan melalui WHvCreatePartition tanpa mem-boot kernel OS tamu:
- Decoupler Paging 64 KB-ke-4 KB:
whpxmendaftarkan setiap arena 64 MiB daripgalloc.MemoryFileke partisi satu kali melaluiWHvMapGpaRange. PemanggilanMapFiledanUnmapberikutnya hanya memodifikasi tabel halaman tamu di dalam partisi, sehingga menerapkan izinR/W/X4 KB yang tepat meskipun Windows menggunakan granularitas alokasi 64 KB. - Pertukaran Register Tanpa API melalui
CommPage:- Pada
amd64,SYSCALLRing-3 tamu masuk ke stubMSR_LSTARRing-0 di dalam partisi (stubgen_x86.go), yang menyimpan GPR system call jalur cepat (RAX, RDI, RSI, RDX, R10, R8, R9, RIP, RSP) ke dalamCommPage4 KB yang dibagikan, lalu mengeksekusiHLTuntuk keluar dariWHvRunVirtualProcessor. - Pada
arm64,SVC #0EL0 tamu masuk ke vektor EL1 (stubgen_a64.go), menyimpanX0..X8, SP_EL0, ELR_EL1, PSTATEke dalamCommPage, dan memuat dari GPA tanpa backing nol untuk memicu memory exit kembali ke Sentry.
- Pada
2. Mesin Perangkat Keras macOS: platform/hvf (Hypervisor.framework)
Pada Apple Silicon (darwin/arm64), platform/hvf menjalankan thread kontainer di dalam vCPU Hypervisor.framework (hv_vm_create, hv_vcpu_run):
- Translasi MMU Dua Tahap 4 KB di atas 16 KB: Stage-2 (
hv_vm_map) memetakan potongan memory-file 1 GiB ke ruang Intermediate Physical Address (IPA) satu kali pada granularitas halaman host 16 KB denganHV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC. Di dalam vCPU, stub EL1bluepill(bluepill_arm64.go) mengonfigurasiTCR_EL1.TG0 = 0b00(granule 4 KB) diTTBR0_EL1dan mengisi entri tabel halaman Stage-1 4 KB dengan izinR-XatauRW-(UXN=1) yang tepat. Karena MMU ARM64 menerapkan irisan izin Stage-1 dan Stage-2, perangkat keras menjebak fault izin tepat pada batas 4 KB. - Trap System Call:
SVC #0EL0 tamu men-trap keVBAR_EL1EL1, menyimpan register keCommPage4 KB yang dibagikan, dan mengeksekusiHVC #0untuk kembali darihv_vcpu_runke thread Go Sentry.
3. Fallback Mode Pengguna Windows: platform/systrap (winsystrap)
Ketika virtualisasi perangkat keras tidak tersedia (misalnya di dalam VM cloud bersarang), winsystrap menjalankan kode tamu dalam mode pengguna pada windows/amd64 dan windows/arm64:
- Isolasi
CR3/TTBR0_EL1Perangkat Keras: Thread tamu berjalan di dalam subproses stub terisolasi yang di-boot dari executable 1 KB yang dihasilkan (stubimage_xplat.go) dan dibatasi oleh token Low-Integrity serta Job Object. - Patching Binari di Tempat & IPC
winSysmsg: Padaamd64, patcher (patcher_amd64_xplat.go) mengganti instruksisyscall(0f 05) 2-byte dengan lompatanjmp rel325-byte (atau jebakan breakpointCC 90ketika jendela 5-byte tidak tersedia) menuju fast-path island. Padaarm64,svc #0di-patch dengan cabangBke slot island. Island menyimpan register ke halamanwinSysmsgyang dibagikan dan disinkronkan dengan Sentry melaluiWaitOnAddressdanWakeByAddressSingle, sementara trap yang tidak di-patch dan page fault berhenti pada Windows Debug Object (WaitForDebugEventEx).
4. Mesin Mode Pengguna Linux: platform/systrap
Pada linux/amd64 dan linux/arm64, systrap menjalankan thread tamu (sysmsg) yang men-trap system call tamu melalui SECCOMP_RET_TRAP (SIGSYS) dan melakukan binary-patch pada situs syscall ke dalam trampoline memori bersama (sysmsg) yang disinkronkan melalui SYS_FUTEX, sementara thread stub syscallThread (syscall_thread.go) mengeksekusi operasi address-space (mmap, munmap, clone) melalui SECCOMP_RET_USER_NOTIF (_RUN_SECCOMP_LOOP). Ketika gvx berjalan di dalam sandbox gVisor (runsc) luar tempat slot SECCOMP_FILTER_FLAG_NEW_LISTENER tunggal mengembalikan EBUSY, gvx secara otomatis beralih syscallThread ke loop futex memori bersama (_RUN_SYSCALL_LOOP), sebagaimana dijelaskan secara rinci dalam Bagian 3 dari seri ini.
Cara gvx Memetakan System Call Linux Tamu ke Subsistem Virtual dan Handle Host
Setelah mesin platform men-trap instruksi SYSCALL (amd64) atau SVC #0 (arm64) tamu dan menuliskan register tamu ke halaman bersama 4 KB (CommPage, sysmsg, atau winSysmsg), kontrol kembali ke goroutine task Go Sentry (pkg/sentry/kernel/task_syscall.go). Alih-alih meneruskan system call Linux ke kernel OS host yang mendasarinya, Task.doSyscall() menyimpan status register asli (SyscallSaveOrig()), mengevaluasi filter BPF seccomp dan henti ptrace di dalam tamu jika ada, lalu mengirimkan nomor system call melalui linux.AMD64 atau linux.ARM64 (pkg/sentry/syscalls/linux/linux64.go, mencakup sekitar 370 system call Linux) ke dalam empat domain internal:

| Domain Syscall | System Call Linux Tamu Representatif | Subsistem Internal gvx (pkg/sentry) | Interaksi Batas OS Host (Setelah hostlockdown) |
|---|---|---|---|
| A. Proses, Namespace, Mount & Sinkronisasi | clone, execve, unshare, setns, mount, umount2, pivot_root, setuid, capset, futex, epoll_wait, pipe2 | kernel.Task, kredensial virtual, tabel futex, dan VFS2 dalam memori (procfs, sysfs, cgroupfs v2, tmpfs, overlayfs) | Nol system call host (100% state Go dalam memori; sepenuhnya tidak terlihat oleh OS host) |
B. Memori Virtual (MM) | mmap, munmap, mremap, mprotect, brk, madvise, memfd_create | mm.MemoryManager (pohon VMA 4 KB) dan pgalloc.MemoryFile | Pembaruan tabel halaman 4 KB dua arah melalui WHvMapGpaRange, hv_vm_map + TTBR0_EL1, atau mmap/mprotect stub |
C. File & Penyimpanan (VFS2) | openat, pread64, pwrite64, getdents64, statx, renameat2, setxattr, mknod, inotify_add_watch | Router tabel mount vfs.VirtualFilesystem (fsimpl/erofs, fsimpl/ext4lite, fsimpl/hostfs v2) | pread/pwrite dua arah pada 1 FD .erofs yang di-pin atau 1 FD .ext4, atau penelusuran relatif terbatas pada *os.Root |
| D. Jaringan & Netfilter | socket, bind, listen, connect, sendmsg, recvmsg, netlink, setsockopt(IPT_SO_SET_REPLACE) | pkg/tcpip ruang pengguna (lo, eth0, docker0, veth, vxlan, wireguard, ipset, iptables-legacy) | Lalu lintas intra-sandbox menggunakan nol system call host; lalu lintas eksternal dipetakan ke net.Dial/net.Listen (-net=nat) atau 1 soket UDP (-net=wg) |
Ketika handler mengembalikan (rval, ctrl, err), Task.doSyscallInvoke() mengonversi error apa pun menjadi errno Linux negatif (-ExtractErrno(err, sysno)), menuliskan nilai kembali ke halaman register bersama (RAX pada amd64, X0 pada arm64), dan melanjutkan eksekusi tamu.
Penyimpanan (VFS2) dan Jaringan Ruang Pengguna Dua Jalur (netstack)
Semua operasi file dan jaringan tamu dilayani di dalam Go Sentry melalui tiga filesystem VFS2 dan dua jalur pkg/netbackend:

| Mesin Penyimpanan | Peran Mount di gvx | Sumber Daya Host Pendukung | Panggilan Host openat / NtCreateFile saat Runtime | Pemindaian Per-File Windows Defender (WdFilter.sys) |
|---|---|---|---|---|
fsimpl/erofs | Lapisan bawah image OCI hanya-baca | Satu file <sha256>.erofs per layer | 0 | 0 |
fsimpl/ext4lite | Volume bernama (-v name:/path) | Satu file <name>.ext4 per volume | 0 | 0 |
fsimpl/hostfs v2 | Workspace host (-workspace, -v /host:/guest) | Direktori host *os.Root yang di-pin | O(depth) per open yang tidak di-cache | 1 per penutupan file host |
Mengapa ext4lite Penting untuk Docker, Basis Data, dan Package Manager
Mengekstrak atau menulis puluhan ribu file kontainer langsung ke NTFS Windows atau APFS macOS memicu pemindaian minifilter antivirus (WdFilter.sys), tabrakan sensitivitas huruf besar-kecil, dan hilangnya atribut extended POSIX.
pkg/sentry/fsimpl/ext4lite mengimplementasikan filesystem ext4 murni Go di dalam satu file host yang telah dibuka sebelumnya (%LOCALAPPDATA%\gvx\cache\volumes\<name>.ext4 pada Windows, ~/Library/Caches/gvx/volumes/<name>.ext4 pada macOS, atau ~/.cache/gvx/volumes/<name>.ext4 pada Linux) dengan:
- Blok 4 KiB dan grup blok dinamis 16 MiB (
meta_bg): Tumbuh secara dinamis tanpa hambatan alokasi file sparse (fallocatepada Linux,fcntl(F_PREALLOCATE)pada macOS,FileAllocationInfopada Windows) dan memadatkan grup trailing yang tidak terpakai saat unmount bersih. - Jurnal mode terurut JBD2 (inode 8): Commit transaksi atomik dengan checksum CRC-32 dan blok revoke, serta pemulihan
fsckLite. - Dukungan upper-layer
overlayfs: Mengimplementasikan atribut extendedtrusted.overlay.*dansecurity.capability, whiteout perangkat karakter (0:0), pelaporan entri direktorid_type, danRENAME_EXCHANGE.
Beban Kerja Praktis: Docker Engine, Kubernetes (k3s + Calico), dan SDK Go
Instalasi rungv: Rilis Prebuilt Ter-attest SLSA atau go install
Anda dapat mengunduh arsip rungv prebuilt untuk linux/amd64, linux/arm64, darwin/arm64, windows/amd64, atau windows/arm64 dari GitHub Releases (masing-masing dibangun oleh pipeline rilis SLSA dengan bundle provenance Sigstore dan SHA256SUMS), atau menginstal langsung dari sumber dengan go install:
1# Opsi A: Unduh rilis prebuilt dan verifikasi provenance build SLSA
2gh attestation verify rungv-v0.2.0-linux-amd64.tar.gz --repo gfire-sigs/gvx \
3 --signer-workflow gfire-sigs/gvx/.github/workflows/release.yml \
4 --source-ref refs/tags/v0.2.0 --deny-self-hosted-runners
5shasum -a 256 -c SHA256SUMS --ignore-missing
6tar -xzf rungv-v0.2.0-linux-amd64.tar.gz
7
8# Opsi B: Instal langsung melalui Go (Linux / Windows)
9CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest
Pada macOS (
darwin/arm64), arsip rilis prebuilt sudah ditandatangani dengan entitlementcom.apple.security.hypervisor(xattr -d com.apple.quarantine rungvmenghapus karantina browser). Jika Anda membangun secara lokal dengango installataugo buildpada macOS, tandatangani binari dengancodesign --sign - --entitlements entitlements.plist --force $(which rungv).
1. Menjalankan Docker Engine Tanpa Hak Istimewa (docker:dind)
Mengikuti docs/docker-engine.md, Anda dapat menjalankan kontainer docker:dind yang tidak dimodifikasi di dalam rungv pada Windows, macOS, atau Linux dan mengendalikannya dengan CLI docker host Anda:
1# 1. Buat direktori host untuk soket Unix yang diteruskan
2mkdir -p ~/.gvx
3
4# 2. Jalankan docker:dind di dalam sandbox gvx dengan volume ext4lite
5rungv run \
6 -p unix:~/.gvx/docker.sock:2375 \
7 -p 127.0.0.1:9000-9019:9000-9019 \
8 -v gvx-docker:/var/lib/docker \
9 -e DOCKER_TLS_CERTDIR= \
10 docker:dind --tls=false
Di terminal kedua, arahkan CLI docker host Anda ke soket sandbox:
1docker context create gvx --docker host=unix://$HOME/.gvx/docker.sock
2docker context use gvx
3
4docker info --format '{{.Driver}} {{.OperatingSystem}}'
5# Keluaran: overlayfs Alpine Linux ... (containerized)
6
7docker build -t myapp ./app
8docker compose up -d
2. Menjalankan Kubernetes (k3s + Calico) di Sandbox Tanpa Hak Istimewa
Karena gvx mengimplementasikan /dev/kmsg, cgroup v2 (OOM killer memory.max dan throttling bandwidth cpu.max), /proc/diskstats, perangkat netlink wireguard dan vxlan, ipset, serta iptables-legacy (addrtype, conntrack, statistic, recent), image rancher/k3s yang tidak dimodifikasi mencapai Ready di dalam satu proses rungv:
1mkdir -p ~/.gvx/k3s
2
3rungv run \
4 -e IPTABLES_MODE=legacy \
5 -p 127.0.0.1:6443:6443 \
6 -p 127.0.0.1:30000-30019:30000-30019 \
7 -v k3s:/var/lib/rancher \
8 --mount type=bind,src=$HOME/.gvx/k3s,dst=/output \
9 rancher/k3s:v1.31.4-k3s1 server \
10 --disable=traefik,servicelb \
11 --write-kubeconfig=/output/kubeconfig.yaml \
12 --write-kubeconfig-mode=644
Anda juga dapat menggabungkan beberapa node gvx melalui WireGuard (flannel-backend=wireguard-native atau mesh wg0) dengan high availability etcd tertanam, atau menjalankan Calico (calico-vxlan.yaml dengan FELIX_IPTABLESBACKEND=Legacy) untuk menegakkan Kubernetes NetworkPolicy sepenuhnya di dalam ruang pengguna.
3. Menyematkan Kontainer Linux di Go melalui z.gfire.dev/gvx/pkg/gvx
Aplikasi Go dapat menyematkan kontainer Linux secara langsung tanpa daemon kontainer eksternal:
1package main
2
3import (
4 "context"
5 "fmt"
6 "os"
7
8 "z.gfire.dev/gvx/pkg/gvx"
9)
10
11func main() {
12 ctx := context.Background()
13 sb, err := gvx.New(ctx, gvx.Config{
14 Platform: gvx.PlatformAuto,
15 NetworkMode: gvx.NetModeNAT,
16 OCIImage: "alpine:3.20",
17 WorkspacePath: "./repo",
18 Argv: []string{"/usr/bin/git", "status"},
19 Stdout: os.Stdout,
20 Stderr: os.Stderr,
21 })
22 if err != nil {
23 fmt.Fprintf(os.Stderr, "init sandbox: %v\n", err)
24 os.Exit(1)
25 }
26 defer sb.Close()
27
28 exitCode, err := sb.Run(ctx)
29 if err != nil {
30 fmt.Fprintf(os.Stderr, "run sandbox: %v\n", err)
31 os.Exit(1)
32 }
33 os.Exit(exitCode)
34}
Bacaan Lanjutan dalam Seri Ini
- gvx: Menjalankan Kontainer Linux, Docker, dan Kubernetes dalam Satu Proses Tanpa Hak Istimewa (postingan ini)
- Menjalankan Docker, docker build, dan docker compose Tanpa Root atau User Namespace: Cara melakukan bootstrap
docker:dinddi dalam sandbox yang terkunci ketat, tempatsudo,unshare(CLONE_NEWUSER),mount(2), dan/dev/fusediblokir. - Menjalankan Docker-in-Docker di Dalam gVisor dan Sandbox Bersarang dengan gvx: Mengapa
apt-get install docker.iodanrunscbersarang gagal di dalamgVisormelintasi lima penghalang kernel, dan bagaimanagvxmenjalankandocker:dindbersarang menggunakan loop futex memori bersama. - Lingkungan Docker Terisolasi yang Cepat untuk Rollout RL Agen AI dengan gvx: Menskalakan
SWE-benchdan rollout reinforcement learning agen dengan ratusan instansdocker:dindtanpa hak istimewa yang berjalan paralel, serta reset volume.ext4copy-on-write dalam 100 milidetik.