GoSuda

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

By Lemon Mint views ...

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 istimewa root, pengungkapan /var/run/docker.sock pada 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, atau Virtualization.framework dan 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): gvx tidak pernah melemahkan batas keamanan OS host, tidak pernah memasang driver kernel, dan tidak pernah memerlukan root, sudo, --privileged pada host, atau CLONE_NEWUSER. Sebelum mengeksekusi satu instruksi tamu pun, gvx mengunci proses host-nya sendiri menggunakan Windows Restricted Tokens dan Job Objects, macOS Seatbelt (deny default), atau Linux PR_SET_NO_NEW_PRIVS + BPF seccomp. Semua system call tamu, hak istimewa UID 0 virtual, mount (VFS2), dan tabel jaringan (netstack) dibatasi secara ketat di dalam kernel Go Sentry yang aman terhadap memori.

RuntimeHost OSCold StartMemory FootprintAdmin atau sudo DiperlukanBackground VM DaemonDirect OCI Image Run
gvx (whpx / hvf)Windows (amd64, arm64) dan macOS (arm64)~20 ms~30 MiBTidakTidakYa
gvx (systrap)Windows (amd64, arm64) dan Linux (amd64, arm64)~25 hingga 65 ms~31 MiBTidakTidakYa
WSL2 / Docker DesktopWindows dan macOS800 hingga 2500 ms> 512 MiBYa (Pengaturan Awal)YaMemerlukan Daemon
QEMU (Full System)Windows dan macOS1500 hingga 4000 ms> 256 MiBTidakTidakMemerlukan 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.

Siklus Hidup Kontainer gvx dan Penguncian Mandiri Host

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():

  1. Windows NT (lockdown_windows.go): Memanggil AdjustTokenPrivileges dengan SE_PRIVILEGE_REMOVED untuk menghapus hak istimewa secara langsung (in-place), mengikat token impersonasi terbatas DISABLE_MAX_PRIVILEGE pada integritas Low (S-1-16-4096), menetapkan proses ke Job Object anonim dengan JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1 (memblokir pembuatan proses anak), dan mengaktifkan DisallowWin32kSystemCalls = 1 melalui SetProcessMitigationPolicy.
  2. macOS XNU (lockdown_darwin.go): Menetapkan RLIMIT_NPROC = 0 dan menerapkan profil Seatbelt (sandbox_init_with_parameters) yang dimulai dari (version 1)(deny default) dan memblokir process-fork, process-exec*, serta semua pembukaan file host baru.
  3. Linux (lockdown_linux.go + pkg/gvx/syscallfilter_linux.go): Menetapkan PR_SET_NO_NEW_PRIVS dan memasang allowlist BPF seccomp milik Sentry di seluruh thread (SECCOMP_FILTER_FLAG_TSYNC) dengan aturan EPERM eksplisit untuk execve, execveat, clone, openat, dan socket.

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:

Mekanisme Intersepsi Syscall Platform gvx dan Paging 4KB

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: whpx mendaftarkan setiap arena 64 MiB dari pgalloc.MemoryFile ke partisi satu kali melalui WHvMapGpaRange. Pemanggilan MapFile dan Unmap berikutnya hanya memodifikasi tabel halaman tamu di dalam partisi, sehingga menerapkan izin R/W/X 4 KB yang tepat meskipun Windows menggunakan granularitas alokasi 64 KB.
  • Pertukaran Register Tanpa API melalui CommPage:
    • Pada amd64, SYSCALL Ring-3 tamu masuk ke stub MSR_LSTAR Ring-0 di dalam partisi (stubgen_x86.go), yang menyimpan GPR system call jalur cepat (RAX, RDI, RSI, RDX, R10, R8, R9, RIP, RSP) ke dalam CommPage 4 KB yang dibagikan, lalu mengeksekusi HLT untuk keluar dari WHvRunVirtualProcessor.
    • Pada arm64, SVC #0 EL0 tamu masuk ke vektor EL1 (stubgen_a64.go), menyimpan X0..X8, SP_EL0, ELR_EL1, PSTATE ke dalam CommPage, dan memuat dari GPA tanpa backing nol untuk memicu memory exit kembali ke Sentry.

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 dengan HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC. Di dalam vCPU, stub EL1 bluepill (bluepill_arm64.go) mengonfigurasi TCR_EL1.TG0 = 0b00 (granule 4 KB) di TTBR0_EL1 dan mengisi entri tabel halaman Stage-1 4 KB dengan izin R-X atau RW- (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 #0 EL0 tamu men-trap ke VBAR_EL1 EL1, menyimpan register ke CommPage 4 KB yang dibagikan, dan mengeksekusi HVC #0 untuk kembali dari hv_vcpu_run ke 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_EL1 Perangkat 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: Pada amd64, patcher (patcher_amd64_xplat.go) mengganti instruksi syscall (0f 05) 2-byte dengan lompatan jmp rel32 5-byte (atau jebakan breakpoint CC 90 ketika jendela 5-byte tidak tersedia) menuju fast-path island. Pada arm64, svc #0 di-patch dengan cabang B ke slot island. Island menyimpan register ke halaman winSysmsg yang dibagikan dan disinkronkan dengan Sentry melalui WaitOnAddress dan WakeByAddressSingle, 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:

Bagaimana gvx Memetakan Syscall Linux Tamu ke Subsistem dan OS Host

Domain SyscallSystem Call Linux Tamu RepresentatifSubsistem Internal gvx (pkg/sentry)Interaksi Batas OS Host (Setelah hostlockdown)
A. Proses, Namespace, Mount & Sinkronisasiclone, execve, unshare, setns, mount, umount2, pivot_root, setuid, capset, futex, epoll_wait, pipe2kernel.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_createmm.MemoryManager (pohon VMA 4 KB) dan pgalloc.MemoryFilePembaruan 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_watchRouter 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 & Netfiltersocket, 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 VFS2 gvx dan Netstack Dua Jalur

Mesin PenyimpananPeran Mount di gvxSumber Daya Host PendukungPanggilan Host openat / NtCreateFile saat RuntimePemindaian Per-File Windows Defender (WdFilter.sys)
fsimpl/erofsLapisan bawah image OCI hanya-bacaSatu file <sha256>.erofs per layer00
fsimpl/ext4liteVolume bernama (-v name:/path)Satu file <name>.ext4 per volume00
fsimpl/hostfs v2Workspace host (-workspace, -v /host:/guest)Direktori host *os.Root yang di-pinO(depth) per open yang tidak di-cache1 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 (fallocate pada Linux, fcntl(F_PREALLOCATE) pada macOS, FileAllocationInfo pada 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 extended trusted.overlay.* dan security.capability, whiteout perangkat karakter (0:0), pelaporan entri direktori d_type, dan RENAME_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 entitlement com.apple.security.hypervisor (xattr -d com.apple.quarantine rungv menghapus karantina browser). Jika Anda membangun secara lokal dengan go install atau go build pada macOS, tandatangani binari dengan codesign --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

  1. gvx: Menjalankan Kontainer Linux, Docker, dan Kubernetes dalam Satu Proses Tanpa Hak Istimewa (postingan ini)
  2. Menjalankan Docker, docker build, dan docker compose Tanpa Root atau User Namespace: Cara melakukan bootstrap docker:dind di dalam sandbox yang terkunci ketat, tempat sudo, unshare(CLONE_NEWUSER), mount(2), dan /dev/fuse diblokir.
  3. Menjalankan Docker-in-Docker di Dalam gVisor dan Sandbox Bersarang dengan gvx: Mengapa apt-get install docker.io dan runsc bersarang gagal di dalam gVisor melintasi lima penghalang kernel, dan bagaimana gvx menjalankan docker:dind bersarang menggunakan loop futex memori bersama.
  4. Lingkungan Docker Terisolasi yang Cepat untuk Rollout RL Agen AI dengan gvx: Menskalakan SWE-bench dan rollout reinforcement learning agen dengan ratusan instans docker:dind tanpa hak istimewa yang berjalan paralel, serta reset volume .ext4 copy-on-write dalam 100 milidetik.