GoSuda

gvx: Spúšťanie Linux Containers, Docker a Kubernetes v jednom neprivilegovanom procese

By Lemon Mint views ...

Úvod: Bezpečné kontajnery s posilnenou ochranou bez roota a hostiteľských démonov

Spúšťanie Linuxových kontajnerov, príkazu docker build alebo Kubernetes na vývojovom počítači, CI runneri alebo viacnájomnom cloudovom uzle si zvyčajne vynúti nepríjemný bezpečnostný kompromis:

  • Na Linuxe štandardné OCI runtime (runc, crun, runsc) vyžadujú buď oprávnenia root, sprístupnenie hostiteľského /var/run/docker.sock, --privileged (CAP_SYS_ADMIN), alebo používateľské menné priestory (unshare(CLONE_NEWUSER)), ktoré rozširujú útočnú plochu hostiteľského jadra.
  • Na Windows a macOS kontajnerové enginy (Docker Desktop, Podman Machine, Lima, OrbStack) spúšťajú úplné virtuálne stroje s jadrom Linuxu vo WSL2, Hyper-V alebo Virtualization.framework a v rámci tohto VM prevádzkujú perzistentný démon na pozadí.

gvx (CLI rungv a Go SDK z.gfire.dev/gvx/pkg/gvx) poskytuje bezpečnú alternatívu s nulovými oprávneniami. Je vytvorený ako fork projektu Google gVisor, ktorého upstream sa dá sledovať, a obsahuje jeho používateľskopriestorové jadro Linuxu (Sentry) a používateľskopriestorový sieťový zásobník (netstack). gvx spúšťa nemodifikované binárne súbory linux/amd64 a linux/arm64 vo formáte ELF64 a kontajnery OCI priamo vnútri jediného neprivilegovaného, sebazamknutého hostiteľského procesu na systémoch Windows, macOS a Linux.

Bezpečnostná záruka (trojvrstvová obrana do hĺbky): gvx nikdy neoslabuje hranice zabezpečenia hostiteľského OS, nikdy neinštaluje ovládače jadra a nikdy nevyžaduje root, sudo, --privileged na hostiteľovi ani CLONE_NEWUSER. Pred vykonaním jedinej inštrukcie hosťa gvx zamkne svoj vlastný hostiteľský proces pomocou Windows Restricted Tokens a Job Objects, macOS Seatbelt (deny default) alebo Linux PR_SET_NO_NEW_PRIVS + BPF seccomp. Všetky systémové volania hosťa, virtuálne privilégiá UID 0, pripojenia (VFS2) a sieťové tabuľky (netstack) sú prísne ohraničené v rámci bezpečného z hľadiska pamäte jadra Go Sentry.

RuntimeHostiteľský OSStudený štartPamäťová stopaPotrebný správca alebo sudoDémon VM na pozadíPriame spustenie OCI obrazu
gvx (whpx / hvf)Windows (amd64, arm64) a macOS (arm64)~20 ms~30 MiBNieNieÁno
gvx (systrap)Windows (amd64, arm64) a Linux (amd64, arm64)~25 až 65 ms~31 MiBNieNieÁno
WSL2 / Docker DesktopWindows a macOS800 až 2500 ms> 512 MiBÁno (počiatočné nastavenie)ÁnoVyžaduje démon
QEMU (Full System)Windows a macOS1500 až 4000 ms> 256 MiBNieNieVyžaduje diskový obraz

Na systémoch Windows ani macOS sa nespúšťa žiadne jadro Linux VM. Na hostiteľovi nebeží žiadny démon na pozadí. Na hostiteľskom OS nie sú potrebné root, sudo, používateľské menné priestory (CLONE_NEWUSER), mount(2), ovládače TUN/TAP ani oprávnenia CAP_NET_ADMIN. Vo vnútri tohto neprivilegovaného procesu gvx spúšťa Docker-in-Docker (docker:dind), docker build, docker compose a viacuzlový vysoko dostupný Kubernetes (k3s s vloženým etcd a sieťovým pluginom Calico CNI).


Životný cyklus kontajnera a trojvrstvové zamknutie hostiteľa

Každá sandboxová oblasť gvx beží v rámci jedného neprivilegovaného hostiteľského procesu a presadzuje trojfázový životný cyklus: vopred otvorenie všetkých potrebných deskriptorov súborov a soketov hostiteľa, zamknutie hostiteľského procesu OS prostredníctvom pkg/hostlockdown a vstup do vnútorného výkonného cyklu Sentry.

Životný cyklus kontajnera gvx a sebazamknutie hostiteľa

Pred odovzdaním riadenia prvej inštrukcii hosťa gvx vopred otvorí svoje cachované deskriptory vrstiev obrazu .erofs, deskriptory súborov zväzkov .ext4, handle *os.Root pre /workspace a sieťové sokety hostiteľa, potom zavolá hostlockdown.Apply():

  1. Windows NT (lockdown_windows.go): Volá AdjustTokenPrivileges s SE_PRIVILEGE_REMOVED, aby odobrala oprávnenia na mieste, naviaže obmedzený impersonačný token DISABLE_MAX_PRIVILEGE s nízkou integritou (S-1-16-4096), priradí proces k anonymnému Job Object s JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1 (čím blokuje vytváranie akýchkoľvek podriadených procesov) a aktivuje DisallowWin32kSystemCalls = 1 cez SetProcessMitigationPolicy.
  2. macOS XNU (lockdown_darwin.go): Nastaví RLIMIT_NPROC = 0 a aplikuje profil Seatbelt (sandbox_init_with_parameters) začínajúci na (version 1)(deny default), ktorý blokuje process-fork, process-exec* a všetky nové otvorenia súborov hostiteľa.
  3. Linux (lockdown_linux.go + pkg/gvx/syscallfilter_linux.go): Nastaví PR_SET_NO_NEW_PRIVS a nainštaluje allowlist BPF seccomp jadra Sentry naprieč všetkými vláknami (SECCOMP_FILTER_FLAG_TSYNC) s explicitnými pravidlami EPERM pre execve, execveat, clone, openat a socket.

Multiplatformové zachytávanie systémových volaní a stránkovanie 4 KB (pkg/sentry/platform)

Binárne súbory ELF64 systému Linux predpokladajú granularitu pamäťových stránok 4 KB (0x1000) pre mapovanie segmentov mmap(MAP_FIXED). Funkcie Windows VirtualAlloc a MapViewOfFile3 však vynucujú zarovnanie na 64 KB (0x10000) a macOS na čipoch Apple Silicon (darwin/arm64) používa hostiteľské stránky s veľkosťou 16 KB (0x4000). Každý OS navyše vyžaduje iný mechanizmus na zachytenie inštrukcií Linuxu SYSCALL (amd64) a SVC #0 (arm64) bez spustenia virtuálneho stroja s Linuxom.

gvx rieši oba problémy v rámci štyroch vykonávacích backendov, ktoré implementujú pkg/sentry/platform.Context:

Mechanika zachytávania systémových volaní a stránkovania 4 KB v platforme gvx

1. Hardvérový engine pre Windows: platform/whpx (WinHvPlatform.dll)

Na windows/amd64 a windows/arm64 platform/whpx vytvára ľahkú hypervízorovú partíciu cez WHvCreatePartition bez spustenia jadra operačného systému hosťa:

  • Dekuplér stránkovania 64 KB na 4 KB: whpx raz zaregistruje každú 64 MiB arénu pgalloc.MemoryFile v partícii cez WHvMapGpaRange. Následné volania MapFile a Unmap upravujú iba tabuľky stránok hosťa v rámci partície a vynucujú presné oprávnenia R/W/X na úrovni 4 KB, napriek alokačnej granularite 64 KB systému Windows.
  • Výmena registrov bez API cez CommPage:
    • Na amd64 sa SYSCALL hosťa z Ring-3 presúva do stubu MSR_LSTAR bežiaceho v Ring-0 v rámci partície (stubgen_x86.go), ktorý uloží registre GPR rýchlej cesty systémových volaní (RAX, RDI, RSI, RDX, R10, R8, R9, RIP, RSP) do zdieľanej stránky CommPage s veľkosťou 4 KB a vykoná HLT, aby ukončil WHvRunVirtualProcessor.
    • Na arm64 sa SVC #0 hosťa z EL0 presúva do vektorov EL1 (stubgen_a64.go), uloží X0..X8, SP_EL0, ELR_EL1, PSTATE do CommPage a načíta z nepodloženej GPA nula, čím vyvolá pamäťový výstup späť do Sentry.

2. Hardvérový engine pre macOS: platform/hvf (Hypervisor.framework)

Na čipoch Apple Silicon (darwin/arm64) platform/hvf spúšťa vlákna kontajnerov vo vCPU frameworku Hypervisor.framework (hv_vm_create, hv_vcpu_run):

  • Dvojstupňová translácia MMU 4 KB cez 16 KB: Stage-2 (hv_vm_map) mapuje časti pamäťových súborov po 1 GiB do priestoru Intermediate Physical Address (IPA) raz, s granularitou hostiteľskej stránky 16 KB, s oprávneniami HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC. Vo vnútri vCPU stub EL1 bluepill (bluepill_arm64.go) nastaví TCR_EL1.TG0 = 0b00 (granulu 4 KB) v TTBR0_EL1 a naplní záznamy tabuliek stránok Stage-1 s veľkosťou 4 KB s presnými oprávneniami R-X alebo RW- (UXN=1). Keďže ARM64 MMU vynucuje prienik oprávnení Stage-1 a Stage-2, hardvér zachytáva chyby oprávnení na presných hraniciach 4 KB.
  • Zachytenie systémového volania: SVC #0 hosťa z EL0 sa presúva do VBAR_EL1 na EL1, uloží registre do zdieľanej stránky CommPage s veľkosťou 4 KB a vykoná HVC #0, aby sa vrátil z hv_vcpu_run do vlákna Go Sentry.

3. Používateľskorežimová záložná možnosť pre Windows: platform/systrap (winsystrap)

Keď hardvérová virtualizácia nie je dostupná (napríklad v rámci vnorených cloudových VM), winsystrap spúšťa kód hosťa v používateľskom režime na windows/amd64 a windows/arm64:

  • Hardvérová izolácia CR3 / TTBR0_EL1: Vlákna hosťa bežia v izolovaných podprocesoch stubov, ktoré sa spúšťajú z vygenerovaného spustiteľného súboru s veľkosťou 1 KB (stubimage_xplat.go) ohraničeného tokenom s nízkou integritou a Job Object.
  • Patchovanie binárneho kódu na mieste a IPC winSysmsg: Na amd64 patcher (patcher_amd64_xplat.go) nahrádza 2-bajtové inštrukcie syscall (0f 05) 5-bajtovými skokmi jmp rel32 (alebo breakpointovými pascami CC 90, keď nie je dostupné 5-bajtové okno) do ostrova rýchlej cesty. Na arm64 sa svc #0 patchuje vetvou B do slotu ostrova. Ostrov uloží registre do zdieľanej stránky winSysmsg a synchronizuje sa so Sentry cez WaitOnAddress a WakeByAddressSingle, zatiaľ čo nepatchované pasce a chyby stránok sa zastavujú na Windows Debug Object (WaitForDebugEventEx).

4. Používateľskorežimový engine pre Linux: platform/systrap

Na linux/amd64 a linux/arm64 systrap spúšťa vlákna hosťa (sysmsg), ktoré zachytávajú systémové volania hosťa cez SECCOMP_RET_TRAP (SIGSYS) a binárne patchujú miesta syscall do zdieľaného trampolína v pamäti (sysmsg) synchronizovaného cez SYS_FUTEX. Zároveň stub syscallThread (syscall_thread.go) vykonáva operácie nad adresným priestorom (mmap, munmap, clone) cez SECCOMP_RET_USER_NOTIF (_RUN_SECCOMP_LOOP). Keď gvx beží vo vonkajšej sandboxe gVisor (runsc), kde jediný slot SECCOMP_FILTER_FLAG_NEW_LISTENER vracia EBUSY, gvx automaticky prepne syscallThread na slučku futexu v zdieľanej pamäti (_RUN_SYSCALL_LOOP), ako je podrobne opísané v 3. časti tejto série.


Ako gvx mapuje systémové volania Linuxu hosťa na virtuálne podsystémy a hostiteľské handly

Keď platformový engine zachytí SYSCALL (amd64) alebo SVC #0 (arm64) inštrukciu hosťa a zapíše registre hosťa do stránky s veľkosťou 4 KB (CommPage, sysmsg alebo winSysmsg), riadenie sa vráti do goroutine úlohy Sentry v jazyku Go (pkg/sentry/kernel/task_syscall.go). Namiesto preposielania systémových volaní Linuxu podkladovému jadru hostiteľského OS Task.doSyscall() uloží pôvodný stav registrov (SyscallSaveOrig()), vyhodnotí všetky filtre BPF seccomp a zastavenia ptrace v rámci hosťa a odošle číslo systémového volania cez linux.AMD64 alebo linux.ARM64 (pkg/sentry/syscalls/linux/linux64.go, pokrývajúce približne 370 systémových volaní Linuxu) do štyroch interných domén:

Ako gvx mapuje systémové volania Linuxu hosťa na podsystémy a hostiteľský OS

Doména systémových volaníReprezentatívne systémové volania Linuxu hosťaInterný podsystém gvx (pkg/sentry)Interakcia s hranicou hostiteľského OS (po hostlockdown)
A. Proces, menný priestor, pripojenia a synchronizáciaclone, execve, unshare, setns, mount, umount2, pivot_root, setuid, capset, futex, epoll_wait, pipe2kernel.Task, virtuálne poverenia, tabuľka futexov a VFS2 v pamäti (procfs, sysfs, cgroupfs v2, tmpfs, overlayfs)Nulové systémové volania hostiteľa (100 % stav v pamäti v Go; úplne neviditeľné pre hostiteľský OS)
B. Virtuálna pamäť (MM)mmap, munmap, mremap, mprotect, brk, madvise, memfd_createmm.MemoryManager (strom VMA s 4 KB) a pgalloc.MemoryFileObojsmerné aktualizácie tabuliek stránok po 4 KB cez WHvMapGpaRange, hv_vm_map + TTBR0_EL1 alebo stub mmap/mprotect
C. Súbory a úložisko (VFS2)openat, pread64, pwrite64, getdents64, statx, renameat2, setxattr, mknod, inotify_add_watchSmerovač tabuľky pripojení vfs.VirtualFilesystem (fsimpl/erofs, fsimpl/ext4lite, fsimpl/hostfs v2)Obojsmerné pread/pwrite nad 1 pripnutým deskriptorom .erofs alebo 1 deskriptorom .ext4, alebo rozsahovo ohraničené relatívne prechody nad *os.Root
D. Sieťovanie a Netfiltersocket, bind, listen, connect, sendmsg, recvmsg, netlink, setsockopt(IPT_SO_SET_REPLACE)Používateľskopriestorový pkg/tcpip (lo, eth0, docker0, veth, vxlan, wireguard, ipset, iptables-legacy)Komunikácia v rámci sandboxu nepoužíva žiadne systémové volania hostiteľa; externá komunikácia sa mapuje na net.Dial/net.Listen (-net=nat) alebo na 1 UDP soket (-net=wg)

Keď handler vráti (rval, ctrl, err), Task.doSyscallInvoke() prevedie každú chybu na záporný errno Linuxu (-ExtractErrno(err, sysno)), zapíše návratovú hodnotu späť do zdieľanej stránky registrov (RAX na amd64, X0 na arm64) a obnoví vykonávanie hosťa.


Úložisko (VFS2) a dvojitá používateľskopriestorová sieť (netstack)

Všetky súborové a sieťové operácie hosťa sa obsluhujú vnútri jadra Go Sentry prostredníctvom troch súborových systémov VFS2 a dvoch dráh pkg/netbackend:

Úložné enginy VFS2 a dvojdráhový netstack v gvx

Úložný engineÚloha pripojenia v gvxPodkladový hostiteľský zdrojVolania openat / NtCreateFile na hostiteľovi počas behuKontroly jednotlivých súborov programom Windows Defender (WdFilter.sys)
fsimpl/erofsNižšie vrstvy OCI obrazov určené len na čítanieJeden súbor <sha256>.erofs na vrstvu00
fsimpl/ext4litePomenované zväzky (-v name:/path)Jeden súbor <name>.ext4 na zväzok00
fsimpl/hostfs v2Pracovný priečinok hostiteľa (-workspace, -v /host:/guest)Pripnutý hostiteľský adresár *os.RootO(depth) pri každom neskrytom otvorení1 pri každom zatvorení hostiteľského súboru

Prečo ext4lite záleží pre Docker, databázy a správcov balíkov

Extrahovanie alebo zapisovanie desiatok tisíc súborov kontajnera priamo na Windows NTFS alebo macOS APFS spúšťa skenovanie antivírusovými minifiltrami (WdFilter.sys), kolízie citlivosti na veľkosť písmen a chýbajúce rozšírené atribúty POSIX.

pkg/sentry/fsimpl/ext4lite implementuje čistý súborový systém ext4 v jazyku Go vnútri jedného vopred otvoreného hostiteľského súboru (%LOCALAPPDATA%\gvx\cache\volumes\<name>.ext4 na Windows, ~/Library/Caches/gvx/volumes/<name>.ext4 na macOS alebo ~/.cache/gvx/volumes/<name>.ext4 na Linuxe), s týmito vlastnosťami:

  • Bloky 4 KiB a dynamické skupiny blokov 16 MiB (meta_bg): Rastie dynamicky bez zastavení pri alokácii riedkych súborov (fallocate na Linuxe, fcntl(F_PREALLOCATE) na macOS, FileAllocationInfo na Windows) a pri čistom odpojení zhutňuje nepoužité koncové skupiny.
  • Žurnál JBD2 v ordered režime (inode 8): Atomické potvrdzovanie transakcií s kontrolnými súčtmi CRC-32 a blokmi revoke, spolu s obnovou fsckLite.
  • Podpora hornej vrstvy overlayfs: Implementuje rozšírené atribúty trusted.overlay.* a security.capability, whiteouty znakových zariadení (0:0), hlásenie záznamov adresárov d_type a RENAME_EXCHANGE.

Praktické úlohy: Docker Engine, Kubernetes (k3s + Calico) a Go SDK

Inštalácia rungv: prekompilované vydania s overeným SLSA alebo go install

Môžete stiahnuť prekompilovaný archív rungv pre linux/amd64, linux/arm64, darwin/arm64, windows/amd64 alebo windows/arm64 zo stránky GitHub Releases (každý je zostavený pipeline pre vydania SLSA s balíkom dôkazov pôvodu Sigstore a súborom SHA256SUMS), alebo nainštalovať priamo zo zdrojového kódu pomocou go install:

1# Možnosť A: Stiahnite prekompilované vydanie a overte pôvod zostavenia 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# Možnosť B: Inštalácia priamo cez Go (Linux / Windows)
9CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest

Na macOS (darwin/arm64) je prekompilovaný archív vydania už podpísaný oprávnením com.apple.security.hypervisor (xattr -d com.apple.quarantine rungv odstráni karanténu prehliadača). Ak zostavujete lokálne pomocou go install alebo go build na macOS, podpíšte binárny súbor príkazom codesign --sign - --entitlements entitlements.plist --force $(which rungv).

1. Spustenie neprivilegovaného Docker Engine (docker:dind)

Podľa dokumentu docs/docker-engine.md môžete spustiť nemodifikovaný kontajner docker:dind v rámci rungv na systémoch Windows, macOS alebo Linux a ovládať ho pomocou vášho hostiteľského rozhrania docker CLI:

 1# 1. Vytvorte hostiteľský adresár pre preposlaný Unix soket
 2mkdir -p ~/.gvx
 3
 4# 2. Spustite docker:dind v sandboxe gvx so zväzkom 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

V druhom termináli nasmerujte vaše hostiteľské rozhranie docker CLI na soket sandboxu:

1docker context create gvx --docker host=unix://$HOME/.gvx/docker.sock
2docker context use gvx
3
4docker info --format '{{.Driver}} {{.OperatingSystem}}'
5# Výstup: overlayfs Alpine Linux ... (containerized)
6
7docker build -t myapp ./app
8docker compose up -d

2. Spustenie Kubernetes (k3s + Calico) v neprivilegovaných sandboxoch

Keďže gvx implementuje /dev/kmsg, cgroup v2 (vrah OOM memory.max a obmedzenie šírky pásma cpu.max), /proc/diskstats, zariadenia netlink wireguard a vxlan, ipset a iptables-legacy (addrtype, conntrack, statistic, recent), nemodifikovaný obraz rancher/k3s sa dostane do stavu Ready v rámci jediného procesu 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

Môžete tiež pripojiť viacero uzlov gvx cez WireGuard (flannel-backend=wireguard-native alebo sieť wg0 typu mesh) so vysokou dostupnosťou vloženého etcd, alebo spustiť Calico (calico-vxlan.yaml s FELIX_IPTABLESBACKEND=Legacy), aby sa Kubernetes NetworkPolicy vynucovala úplne v používateľskom priestore.

3. Vkladanie Linuxových kontajnerov do Go cez z.gfire.dev/gvx/pkg/gvx

Aplikácie v jazyku Go môžu vkladať Linuxové kontajnery priamo bez externého démona kontajnerov:

 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}

Ďalšie čítanie v tejto sérii

  1. gvx: Spúšťanie Linuxových kontajnerov, Dockera a Kubernetes v jednom neprivilegovanom procese (tento príspevok)
  2. Spúšťanie Docker, docker build a docker compose bez roota alebo používateľských menných priestorov: Ako zaviesť docker:dind v zamknutých sandboxoch, kde sú blokované sudo, unshare(CLONE_NEWUSER), mount(2) a /dev/fuse.
  3. Spúšťanie Docker-in-Docker vnútri gVisor a vnorených sandboxov s gvx: Prečo apt-get install docker.io a vnorený runsc zlyhávajú vnútri gVisor cez päť bariér jadra a ako gvx spúšťa vnorený docker:dind pomocou slučky futexu v zdieľanej pamäti.
  4. Rýchle izolované Docker prostredia pre RL rollouty agentov AI s gvx: Škálovanie SWE-bench a rollout učenia agentov pomocou posilňovania s stovkami paralelných neprivilegovaných inštancií docker:dind a 100-milisekundovými resetmi zväzkov .ext4 typu copy-on-write.