gvx: Kjøring av Linux Containers, Docker og Kubernetes i én uprivilegert prosess

Introduksjon: Sikkerhetsherdede containere uten root eller vertsdaemoner
Å kjøre Linux-containere, docker build eller Kubernetes på en utviklingsmaskin, en CI-runner eller en flerleietakersky-node krever vanligvis en ubehagelig sikkerhetsavveining:
- På Linux krever standard OCI-runtimes (
runc,crun,runsc) entenroot-rettigheter, eksponering av vertens/var/run/docker.sock,--privileged(CAP_SYS_ADMIN) eller brukernavnerom (unshare(CLONE_NEWUSER)), som utvider angrepsflaten i vertens kjerne. - På Windows og macOS starter containermotorer (
Docker Desktop,Podman Machine,Lima,OrbStack) en fullstendig Linux-kjerne i en virtuell maskin innenfor WSL2, Hyper-V ellerVirtualization.framework, og kjører en vedvarende bakgrunnsdaemon inne i den virtuelle maskinen.
gvx (rungv-CLI-en og z.gfire.dev/gvx/pkg/gvx Go-SDK-en) tilbyr et sikkert alternativ uten privilegier. Det er bygget som en oppstrømssporbar gaffel av Googles gVisor brukerromsbaserte Linux-kjerne (Sentry) og brukerromsbasert nettverksstakk (netstack), og gvx kjører umodifiserte linux/amd64- og linux/arm64-ELF64-binærfiler og OCI-containere direkte inne i en enkelt, uprivilegert og selvlåst vertsprosess på Windows, macOS og Linux.
Sikkerhetsgaranti (tre-ringers forsvar i dybden):
gvxsvekker aldri sikkerhetsgrensene i vertsoperativsystemet, installerer aldri kjernedrivere og krever aldriroot,sudo,--privilegedpå verten ellerCLONE_NEWUSER. Før en eneste gjesteinstruksjon kjøres, låsergvxsin egen vertsprosess ved hjelp av Windows Restricted Tokens og Job Objects, macOS Seatbelt (deny default), eller LinuxPR_SET_NO_NEW_PRIVS+ BPFseccomp. Alle systemkall fra gjesten, virtuelleUID 0-rettigheter, monteringer (VFS2) og nettverkstabeller (netstack) er strengt begrenset innenfor den minnesikre Go-kjernenSentry.
| Runtime | Vertsoperativsystem | Kaldstart | Minneavtrykk | Krever administrator eller sudo | VM-daemon i bakgrunnen | Direkte kjøring av OCI-image |
|---|---|---|---|---|---|---|
gvx (whpx / hvf) | Windows (amd64, arm64) og macOS (arm64) | ~20 ms | ~30 MiB | Nei | Nei | Ja |
gvx (systrap) | Windows (amd64, arm64) og Linux (amd64, arm64) | ~25 til 65 ms | ~31 MiB | Nei | Nei | Ja |
| WSL2 / Docker Desktop | Windows og macOS | 800 til 2500 ms | > 512 MiB | Ja (førstegangsoppsett) | Ja | Krever daemon |
| QEMU (fullt system) | Windows og macOS | 1500 til 4000 ms | > 256 MiB | Nei | Nei | Krever diskbilde |
Ingen Linux-kjerne i en virtuell maskin starter på Windows eller macOS. Ingen bakgrunnsdaemon kjører på verten. Det kreves ingen root, sudo, brukernavnerom (CLONE_NEWUSER), mount(2), TUN/TAP-drivere eller CAP_NET_ADMIN-rettigheter på vertsoperativsystemet. Inne i denne uprivilegerte prosessen kjører gvx Docker-in-Docker (docker:dind), docker build, docker compose og høyt tilgjengelig Kubernetes med flere noder (k3s med innebygd etcd og Calico CNI).
Containerlivssyklus og tre-ringers vertslåsing
Hver gvx-sandkasse kjører innenfor én uprivilegert vertsprosess og følger en livssyklus i tre faser: forhåndsåpning av alle nødvendige vertens filbeskrivere og sockets, låsing av vertens operativsystemprosess via pkg/hostlockdown, og inngang til den interne Sentry-utførelsessløyfen.

Før kontrollen overføres til den første gjesteinstruksjonen, forhåndsåpner gvx sine cachede .erofs-bildelagsdeskriptorer, .ext4-volumfilbeskrivere, /workspace-håndtaket *os.Root og vertens nettverkssockets, og kaller deretter hostlockdown.Apply():
- Windows NT (
lockdown_windows.go): KallerAdjustTokenPrivilegesmedSE_PRIVILEGE_REMOVEDfor å fjerne privilegier på stedet, binder etDISABLE_MAX_PRIVILEGE-restriktivt impersonation-token med lav integritet (S-1-16-4096), tilordner prosessen til et anonymt Job Object medJOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1(blokkerer opprettelse av underordnede prosesser), og aktivererDisallowWin32kSystemCalls = 1viaSetProcessMitigationPolicy. - macOS XNU (
lockdown_darwin.go): SetterRLIMIT_NPROC = 0og bruker en Seatbelt-profil (sandbox_init_with_parameters) som starter fra(version 1)(deny default)og blokkererprocess-fork,process-exec*og alle nye filåpninger på verten. - Linux (
lockdown_linux.go+pkg/gvx/syscallfilter_linux.go): SetterPR_SET_NO_NEW_PRIVSog installerer Sentrys BPFseccomp-tillatelsesliste på tvers av alle tråder (SECCOMP_FILTER_FLAG_TSYNC), med eksplisitteEPERM-regler forexecve,execveat,clone,openatogsocket.
Krysplattform-avskjæring av systemkall og 4 KB-paging (pkg/sentry/platform)
Linux ELF64-binærfiler forutsetter en minnesidekornethet på 4 KB (0x1000) for mmap(MAP_FIXED)-segmentmappinger. Windows VirtualAlloc og MapViewOfFile3 håndhever imidlertid 64 KB-justering (0x10000), og Apple Silicon macOS (darwin/arm64) bruker 16 KB-sider (0x4000) på verten. Dessuten krever hvert operativsystem en ulik mekanisme for å avskjære Linux-instruksjonene SYSCALL (amd64) og SVC #0 (arm64) uten å starte en Linux-virtuell maskin.
gvx løser begge problemene på tvers av fire utførelsesbackends som implementerer pkg/sentry/platform.Context:

1. Windows maskinvaremotor: platform/whpx (WinHvPlatform.dll)
På windows/amd64 og windows/arm64 oppretter platform/whpx en lettvekts hypervisor-partisjon via WHvCreatePartition uten å starte en gjestekjerne for operativsystemet:
- Frikobler for 64 KB-til-4 KB-paging:
whpxregistrerer hver 64 MiB-arena avpgalloc.MemoryFilehos partisjonen én gang viaWHvMapGpaRange. Påfølgende kall tilMapFileogUnmapendrer kun gjestens sidetabeller i partisjonen, og håndhever nøyaktige 4 KB-tillatelserR/W/Xtil tross for Windows' 64 KB-allokeringskornethet. - Registerutveksling uten API via
CommPage:- På
amd64går gjestens Ring-3SYSCALLinn i en Ring-0MSR_LSTAR-stub i partisjonen (stubgen_x86.go), som skriver de raske systemkall-GPR-ene (RAX, RDI, RSI, RDX, R10, R8, R9, RIP, RSP) til en delt 4 KBCommPageog kjørerHLTfor å avslutteWHvRunVirtualProcessor. - På
arm64går gjestens EL0SVC #0inn i EL1-vektorer (stubgen_a64.go), skriverX0..X8, SP_EL0, ELR_EL1, PSTATEtilCommPage, og laster fra ubakket GPA-null for å utløse et minneavbrudd tilbake til Sentry.
- På
2. macOS maskinvaremotor: platform/hvf (Hypervisor.framework)
På Apple Silicon (darwin/arm64) kjører platform/hvf containertråder inne i Hypervisor.framework-vCPU-er (hv_vm_create, hv_vcpu_run):
- To-trinns 4 KB over 16 KB MMU-oversettelse: Stage-2 (
hv_vm_map) mapper 1 GiB store minnefilbiter inn i Intermediate Physical Address (IPA)-rommet én gang, med 16 KB vertssidekornethet ogHV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC. Inne i vCPU-en konfigurerer EL1-stubbenbluepill(bluepill_arm64.go)TCR_EL1.TG0 = 0b00(4 KB-granule) iTTBR0_EL1og fyller Stage-1-sidetabelloppføringer på 4 KB med nøyaktigeR-X- ellerRW--tillatelser (UXN=1). Fordi ARM64-MMU-en håndhever snittet av Stage-1- og Stage-2-tillatelser, fanger maskinvaren tillatelsesfeil på nøyaktige 4 KB-grenser. - Systemkallfelle: Gjestens EL0
SVC #0fanges opp til EL1VBAR_EL1, skriver registrene til den delte 4 KBCommPageog kjørerHVC #0for å returnere frahv_vcpu_runtil Go-tråden for Sentry.
3. Windows brukermodus-reserve: platform/systrap (winsystrap)
Når maskinvarevirtualisering ikke er tilgjengelig (for eksempel i nestede skytilfeller), kjører winsystrap gjestekode i brukermodus på windows/amd64 og windows/arm64:
- Maskinvarebasert isolasjon via
CR3/TTBR0_EL1: Gjestetråder kjører i isolerte stub-underprosesser startet fra en generert 1 KB kjørbar fil (stubimage_xplat.go), begrenset av et token med lav integritet og et Job Object. - Patching av binærfil på stedet og
winSysmsgIPC: Påamd64erstatter patcheren (patcher_amd64_xplat.go) 2-bytesyscall-instruksjoner (0f 05) med 5-bytejmp rel32-hopp (ellerCC 90-breakpoint-feller når et 5-byte-vindu ikke er tilgjengelig) inn i en øy for hurtig bane. Påarm64patchessvc #0med enB-grenhopp til en øyplass. Øya skriver registrene til den deltewinSysmsg-siden og synkroniseres med Sentry viaWaitOnAddressogWakeByAddressSingle, mens upatchede feller og sidefeil stopper på et Windows Debug Object (WaitForDebugEventEx).
4. Linux brukermodusmotor: platform/systrap
På linux/amd64 og linux/arm64 kjører systrap gjestetråder (sysmsg) som fanger gjestens systemkall via SECCOMP_RET_TRAP (SIGSYS) og binærpatcher syscall-steder til en trampoline i delt minne (sysmsg) synkronisert via SYS_FUTEX. Samtidig utfører en stub-syscallThread (syscall_thread.go) operasjoner på adresserom (mmap, munmap, clone) via SECCOMP_RET_USER_NOTIF (_RUN_SECCOMP_LOOP). Når gvx kjører inne i en ytre gVisor (runsc)-sandkasse der den ene SECCOMP_FILTER_FLAG_NEW_LISTENER-plassen returnerer EBUSY, bytter gvx automatisk syscallThread til en futex-løkke i delt minne (_RUN_SYSCALL_LOOP), som beskrevet i del 3 av denne serien.
Hvordan gvx kobler gjestens Linux-systemkall til virtuelle delsystemer og vertshåndtak
Når en plattformmotor har fanget en gjeste-SYSCALL (amd64) eller SVC #0-instruksjon (arm64) og skrevet gjestens registre til den delte 4 KB-siden (CommPage, sysmsg eller winSysmsg), returnerer kontrollen til Go-gorutinen for Sentry-oppgaven (pkg/sentry/kernel/task_syscall.go). I stedet for å videresende Linux-systemkall til den underliggende kjernen i vertens operativsystem, lagrer Task.doSyscall() den opprinnelige registertilstanden (SyscallSaveOrig()), evaluerer eventuelle BPF seccomp-filtre og ptrace-stopp i gjesten, og sender systemkallnummeret gjennom linux.AMD64 eller linux.ARM64 (pkg/sentry/syscalls/linux/linux64.go, som dekker ca. 370 Linux-systemkall) til fire interne domener:

| Systemkalldomene | Representative Linux-systemkall i gjesten | Intern gvx-delkomponent (pkg/sentry) | Interaksjon med vertens operativsystemgrense (etter hostlockdown) |
|---|---|---|---|
| A. Prosess, navnerom, montering og synkronisering | clone, execve, unshare, setns, mount, umount2, pivot_root, setuid, capset, futex, epoll_wait, pipe2 | kernel.Task, virtuelle legitimasjoner, futex-tabell og VFS2 i minnet (procfs, sysfs, cgroupfs v2, tmpfs, overlayfs) | Null systemkall på verten (100 % Go-tilstand i minnet; fullstendig usynlig for vertens operativsystem) |
B. Virtuelt minne (MM) | mmap, munmap, mremap, mprotect, brk, madvise, memfd_create | mm.MemoryManager (VMA-tre med 4 KB) og pgalloc.MemoryFile | Toveis oppdateringer av 4 KB-sidetabeller via WHvMapGpaRange, hv_vm_map + TTBR0_EL1, eller stub-mmap/mprotect |
C. Filer og lagring (VFS2) | openat, pread64, pwrite64, getdents64, statx, renameat2, setxattr, mknod, inotify_add_watch | vfs.VirtualFilesystem-monteringstabellens ruter (fsimpl/erofs, fsimpl/ext4lite, fsimpl/hostfs v2) | Toveis pread/pwrite på 1 festet .erofs-FD eller 1 .ext4-FD, eller avgrensede relative gjennomgåinger av *os.Root |
| D. Nettverk og Netfilter | socket, bind, listen, connect, sendmsg, recvmsg, netlink, setsockopt(IPT_SO_SET_REPLACE) | Brukerromsbasert pkg/tcpip (lo, eth0, docker0, veth, vxlan, wireguard, ipset, iptables-legacy) | Trafikk internt i sandkassen bruker null systemkall på verten; ekstern trafikk mappes til net.Dial/net.Listen (-net=nat) eller 1 UDP-socket (-net=wg) |
Når behandleren returnerer (rval, ctrl, err), konverterer Task.doSyscallInvoke() eventuelle feil til en negativ Linux-errno (-ExtractErrno(err, sysno)), skriver returverdien tilbake til den delte registersiden (RAX på amd64, X0 på arm64) og gjenopptar kjøringen i gjesten.
Lagring (VFS2) og tospors brukerromsnettverk (netstack)
All filhåndtering og nettverkstrafikk i gjesten betjenes inne i Go-Sentry gjennom tre VFS2-filsystemer og to spor i pkg/netbackend:

| Lagringsmotor | Monteringsrolle i gvx | Underliggende vertsressurs | Kall til vertens openat / NtCreateFile ved kjøring | Windows Defender (WdFilter.sys) skanninger per fil |
|---|---|---|---|---|
fsimpl/erofs | Skrivebeskyttede nedre lag i OCI-image | Én <sha256>.erofs-fil per lag | 0 | 0 |
fsimpl/ext4lite | Navngitte volumer (-v name:/path) | Én <name>.ext4-fil per volum | 0 | 0 |
fsimpl/hostfs v2 | Vertens arbeidsområde (-workspace, -v /host:/guest) | Festet *os.Root-vertskatalog | O(dybde) per ukachet åpning | 1 per lukking av vertsfil |
Hvorfor ext4lite er viktig for Docker, databaser og pakkehåndterere
Å pakke ut eller skrive titusenvis av containerfiler direkte til Windows NTFS eller macOS APFS utløser skanninger fra antivirusfilterdrivere (WdFilter.sys), kollisjoner i forskjell mellom store og små bokstaver, og manglende utvidede POSIX-attributter.
pkg/sentry/fsimpl/ext4lite implementerer et rent Go-ext4-filsystem inne i én forhåndsåpnet vertsfil (%LOCALAPPDATA%\gvx\cache\volumes\<name>.ext4 på Windows, ~/Library/Caches/gvx/volumes/<name>.ext4 på macOS, eller ~/.cache/gvx/volumes/<name>.ext4 på Linux) med:
- 4 KiB-blokker og 16 MiB dynamiske blokkgrupper (
meta_bg): Vokser dynamisk uten stopp ved allokering av sparsomme filer (fallocatepå Linux,fcntl(F_PREALLOCATE)på macOS,FileAllocationInfopå Windows) og komprimerer ubrukte grupper på slutten ved ren avmontering. - JBD2-journal i ordered-modus (inode 8): Atomiske transaksjonsforpliktelser med CRC-32-kontrollsummer og revoke-blokker, samt gjenoppretting med
fsckLite. - Støtte for
overlayfsøvre lag: Implementerer utvidede attributtertrusted.overlay.*ogsecurity.capability, whiteouts for tegnenheter (0:0), rapportering avd_typefor katalogoppføringer ogRENAME_EXCHANGE.
Praktiske arbeidslaster: Docker-motor, Kubernetes (k3s + Calico) og Go-SDK
Installasjon av rungv: SLSA-attesterte forhåndsbygde utgivelser eller go install
Du kan laste ned et forhåndsbygd rungv-arkiv for linux/amd64, linux/arm64, darwin/arm64, windows/amd64 eller windows/arm64 fra GitHub Releases (hvert bygget av SLSA-utgivelsespipelinen med en Sigstore-proveniensbunt og SHA256SUMS), eller installere direkte fra kildekoden med go install:
1# Alternativ A: Last ned en forhåndsbygd utgivelse og verifiser SLSA-byggproveniens
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# Alternativ B: Installer direkte via Go (Linux / Windows)
9CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest
På macOS (
darwin/arm64) er det forhåndsbygde utgivelsesarkivet allerede signert med rettighetencom.apple.security.hypervisor(xattr -d com.apple.quarantine rungvfjerner karantene fra nettleseren). Hvis du bygger lokalt medgo installellergo buildpå macOS, må du signere binærfilen medcodesign --sign - --entitlements entitlements.plist --force $(which rungv).
1. Kjøre en uprivilegert Docker-motor (docker:dind)
Etter docs/docker-engine.md kan du kjøre en umodifisert docker:dind-container inne i rungv på Windows, macOS eller Linux og styre den med din lokale docker-CLI:
1# 1. Opprett vertskatalogen for det videresendte Unix-socket
2mkdir -p ~/.gvx
3
4# 2. Start docker:dind inne i en gvx-sandkasse med et ext4lite-volum
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
I en andre terminal peker du din lokale docker-CLI mot sandkassens socket:
1docker context create gvx --docker host=unix://$HOME/.gvx/docker.sock
2docker context use gvx
3
4docker info --format '{{.Driver}} {{.OperatingSystem}}'
5# Utdata: overlayfs Alpine Linux ... (containerized)
6
7docker build -t myapp ./app
8docker compose up -d
2. Kjøre Kubernetes (k3s + Calico) i uprivilegerte sandkasser
Fordi gvx implementerer /dev/kmsg, cgroup v2 (memory.max OOM-drepen og cpu.max-båndbreddebegrensning), /proc/diskstats, netlink-enheter for wireguard og vxlan, ipset og iptables-legacy (addrtype, conntrack, statistic, recent), når et umodifisert rancher/k3s-image status Ready inne i én enkelt rungv-prosess:
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
Du kan også koble flere gvx-noder sammen over WireGuard (flannel-backend=wireguard-native eller et wg0-nett) med innebygd etcd og høy tilgjengelighet, eller kjøre Calico (calico-vxlan.yaml med FELIX_IPTABLESBACKEND=Legacy) for å håndheve Kubernetes NetworkPolicy helt i brukerrommet.
3. Innbygging av Linux-containere i Go via z.gfire.dev/gvx/pkg/gvx
Go-applikasjoner kan bygge inn Linux-containere direkte uten en ekstern containerdaemon:
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}
Videre lesning i denne serien
- gvx: Kjøre Linux-containere, Docker og Kubernetes i én uprivilegert prosess (dette innlegget)
- Kjøre Docker, docker build og docker compose uten root eller brukernavnerom: Hvordan starte
docker:dindi låste sandkasser dersudo,unshare(CLONE_NEWUSER),mount(2)og/dev/fuseer blokkert. - Kjøre Docker-in-Docker inne i gVisor og nestede sandkasser med gvx: Hvorfor
apt-get install docker.ioog nestetrunscfeiler inne igVisorpå tvers av fem kjernebarrierer, og hvordangvxkjører nestetdocker:dindved hjelp av en futex-løkke i delt minne. - Raske, isolerte Docker-miljøer for RL-utrullinger av AI-agenter med gvx: Skalering av
SWE-benchog forsterkningslæringsutrullinger for agenter med hundrevis av parallelle uprivilegertedocker:dind-instanser og 100 millisekunders copy-on-write-tilbakestillinger av.ext4-volumer.