---
id: e62d78ef5850c982dbddc9d3ff9134ae
author: Lemon Mint
title: 'gvx: Exécuter Linux Containers, Docker et Kubernetes dans un seul processus non privilégié'
description: Vue d'ensemble technique de gvx, un runtime de conteneurs multiplateforme et SDK Go renforcé en matière de sécurité, construit sur le noyau gVisor en espace utilisateur, qui exécute en toute sécurité des conteneurs Linux, du Docker-in-Docker et des clusters Kubernetes haute disponibilité multi-nœuds au sein d'un unique processus non privilégié, sur Windows, macOS et Linux.
language: fr
date: 2026-11-06T12:02:56.008516536Z
path: /blog/posts/gvx-cross-platform-linux-containers-z48916d01
go_package: z.gfire.dev/gvx
go_repourl: https://github.com/gfire-sigs/gvx.git
category: lowlevel
---

## Introduction : conteneurs durcis en sécurité sans root ni daemons hôte

Exécuter des conteneurs Linux, `docker build` ou Kubernetes au sein d'une machine de développement, d'un runner CI ou d'un nœud cloud multi-locataire impose généralement un compromis de sécurité peu satisfaisant :

- Sur **Linux**, les runtimes OCI standard (`runc`, `crun`, `runsc`) exigent soit des privilèges `root`, soit l'exposition de `/var/run/docker.sock` de l'hôte, soit `--privileged` (`CAP_SYS_ADMIN`), soit des espaces de noms utilisateur (`unshare(CLONE_NEWUSER)`) qui élargissent la surface d'attaque du noyau hôte.
- Sur **Windows** et **macOS**, les moteurs de conteneurs (`Docker Desktop`, `Podman Machine`, `Lima`, `OrbStack`) démarrent un noyau Linux complet dans une machine virtuelle via WSL2, Hyper-V ou `Virtualization.framework`, et exécutent un daemon persistant en arrière-plan à l'intérieur de cette VM.

**[gvx](https://github.com/gfire-sigs/gvx)** (CLI `rungv` et SDK Go `z.gfire.dev/gvx/pkg/gvx`) offre une **alternative sécurisée et sans privilège**. Conçu comme une branche (fork) suivable en amont du noyau Linux en espace utilisateur de Google **gVisor** (`Sentry`) et de sa pile réseau en espace utilisateur (`netstack`), `gvx` exécute directement des binaires ELF64 `linux/amd64` et `linux/arm64` non modifiés ainsi que des conteneurs OCI **au sein d'un unique processus hôte non privilégié et auto-verrouillé** sur Windows, macOS et Linux.

> **Garantie de sécurité (défense en profondeur à trois anneaux)** : `gvx` n'affaiblit jamais les frontières de sécurité du système d'exploitation hôte, n'installe jamais de pilotes noyau et n'exige jamais `root`, `sudo`, `--privileged` sur l'hôte, ni `CLONE_NEWUSER`. Avant d'exécuter la moindre instruction invitée, `gvx` verrouille son propre processus hôte à l'aide de jetons restreints (Restricted Tokens) et de Job Objects Windows, de Seatbelt macOS (`deny default`), ou de `PR_SET_NO_NEW_PRIVS` et du `seccomp` BPF sous Linux. Tous les appels système invités, les privilèges virtuels `UID 0`, les montages (`VFS2`) et les tables réseau (`netstack`) sont strictement confinés au sein du noyau `Sentry` en Go, sûr vis-à-vis de la mémoire.

| Runtime                    | OS hôte                                               |        Démarrage à froid | Empreinte mémoire | Administrateur ou `sudo` requis | Daemon VM en arrière-plan | Exécution directe d'image OCI |
| :------------------------- | :---------------------------------------------------- | ----------------: | ---------------: | :--------------------- | :------------------- | :------------------- |
| **`gvx` (`whpx` / `hvf`)** | Windows (`amd64`, `arm64`) & macOS (`arm64`)          |          `~20 ms` |        `~30 MiB` | Non                    | Non                  | Oui                  |
| **`gvx` (`systrap`)**      | Windows (`amd64`, `arm64`) & Linux (`amd64`, `arm64`) |    `~25 à 65 ms` |        `~31 MiB` | Non                    | Non                  | Oui                  |
| **WSL2 / Docker Desktop**  | Windows & macOS                                       |  `800 à 2500 ms` |      `> 512 MiB` | Oui (configuration initiale) | Oui                  | Nécessite un daemon  |
| **QEMU (système complet)** | Windows & macOS                                       | `1500 à 4000 ms` |      `> 256 MiB` | Non                    | Non                  | Nécessite une image disque |

Aucun noyau de machine virtuelle Linux ne démarre sous Windows ou macOS. Aucun daemon en arrière-plan ne s'exécute sur l'hôte. Aucun privilège `root`, `sudo`, d'espaces de noms utilisateur (`CLONE_NEWUSER`), de `mount(2)`, de pilotes `TUN`/`TAP` ni de `CAP_NET_ADMIN` n'est requis sur le système d'exploitation hôte. Au sein de ce processus non privilégié, `gvx` exécute **Docker-in-Docker (`docker:dind`)**, `docker build`, `docker compose` et **Kubernetes haute disponibilité multi-nœuds (`k3s` avec `etcd` embarqué et CNI `Calico`)**.

---

## Cycle de vie du conteneur et verrouillage hôte à trois anneaux

Chaque bac à sable `gvx` s'exécute au sein d'un unique processus hôte non privilégié et applique un cycle de vie en trois phases : pré-ouverture de tous les descripteurs de fichiers et sockets hôte requis, verrouillage du processus du système d'exploitation hôte via `pkg/hostlockdown`, puis entrée dans la boucle d'exécution `Sentry` intégrée au processus.

![Cycle de vie du conteneur gvx et auto-verrouillage de l'hôte](/assets/images/gvx/gvx-lifecycle-lockdown.png)

Avant de transférer le contrôle à la première instruction invitée, `gvx` pré-ouvre ses descripteurs de couches d'image `.erofs` mises en cache, ses descripteurs de volumes `.ext4`, son handle `*os.Root` sur `/workspace` et ses sockets réseau hôte, puis invoque `hostlockdown.Apply()` :

1. **Windows NT (`lockdown_windows.go`)** : Appelle `AdjustTokenPrivileges` avec `SE_PRIVILEGE_REMOVED` pour retirer les privilèges sur place, lie un jeton d'usurpation restreint `DISABLE_MAX_PRIVILEGE` à l'intégrité Low (`S-1-16-4096`), affecte le processus à un Job Object anonyme avec `JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1` (bloquant la création de tout processus enfant), et active `DisallowWin32kSystemCalls = 1` via `SetProcessMitigationPolicy`.
2. **macOS XNU (`lockdown_darwin.go`)** : Définit `RLIMIT_NPROC = 0` et applique un profil Seatbelt (`sandbox_init_with_parameters`) débutant par `(version 1)(deny default)` qui bloque `process-fork`, `process-exec*` et toute nouvelle ouverture de fichier hôte.
3. **Linux (`lockdown_linux.go` + `pkg/gvx/syscallfilter_linux.go`)** : Définit `PR_SET_NO_NEW_PRIVS` et installe la liste blanche `seccomp` BPF de Sentry sur l'ensemble des threads (`SECCOMP_FILTER_FLAG_TSYNC`), avec des règles `EPERM` explicites pour `execve`, `execveat`, `clone`, `openat` et `socket`.

---

## Interception des appels système multiplateforme et pagination 4 Ko (`pkg/sentry/platform`)

Les binaires ELF64 Linux supposent une granularité de pages mémoire de 4 Ko (`0x1000`) pour les mappages de segments `mmap(MAP_FIXED)`. Cependant, `VirtualAlloc` et `MapViewOfFile3` de Windows imposent un alignement de 64 Ko (`0x10000`), et macOS sur Apple Silicon (`darwin/arm64`) utilise des pages hôte de 16 Ko (`0x4000`). De plus, chaque système d'exploitation requiert un mécanisme différent pour intercepter les instructions Linux `SYSCALL` (`amd64`) et `SVC #0` (`arm64`) sans démarrer de VM Linux.

`gvx` résout ces deux problèmes à travers quatre backends d'exécution qui implémentent `pkg/sentry/platform.Context` :

![Mécanique d'interception des appels système et de pagination 4 Ko de la plateforme gvx](/assets/images/gvx/gvx-platform-syscalls.png)

### 1. Moteur matériel Windows : `platform/whpx` (`WinHvPlatform.dll`)

Sur `windows/amd64` et `windows/arm64`, `platform/whpx` crée une partition hyperviseur légère via `WHvCreatePartition` sans démarrer de noyau de système d'exploitation invité :

- **Découplage de pagination 64 Ko vers 4 Ko** : `whpx` enregistre chaque arène de 64 MiB de `pgalloc.MemoryFile` auprès de la partition une seule fois via `WHvMapGpaRange`. Les appels `MapFile` et `Unmap` ultérieurs ne modifient que les tables de pages invitées au sein de la partition, imposant des permissions `R/W/X` exactes à 4 Ko malgré la granularité d'allocation de 64 Ko de Windows.
- **Échange de registres sans API via `CommPage`** :
  - Sur `amd64`, un `SYSCALL` invité en Ring-3 entre dans un stub `MSR_LSTAR` en Ring-0 au sein de la partition (`stubgen_x86.go`), qui écrit les GPR de l'appel système rapide (`RAX, RDI, RSI, RDX, R10, R8, R9, RIP, RSP`) dans une `CommPage` partagée de 4 Ko et exécute `HLT` pour quitter `WHvRunVirtualProcessor`.
  - Sur `arm64`, un `SVC #0` invité en EL0 entre dans les vecteurs EL1 (`stubgen_a64.go`), écrit `X0..X8, SP_EL0, ELR_EL1, PSTATE` dans `CommPage`, puis charge depuis le GPA zéro non alloué afin de déclencher une sortie mémoire vers Sentry.

### 2. Moteur matériel macOS : `platform/hvf` (`Hypervisor.framework`)

Sur Apple Silicon (`darwin/arm64`), `platform/hvf` exécute les threads de conteneurs au sein de vCPU `Hypervisor.framework` (`hv_vm_create`, `hv_vcpu_run`) :

- **Traduction MMU à deux étages : 4 Ko sur 16 Ko** : L'étage 2 (`hv_vm_map`) mappe des fragments de 1 GiB du fichier mémoire dans l'espace d'adresses physiques intermédiaires (IPA) une seule fois, à la granularité de page hôte de 16 Ko, avec `HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC`. Au sein du vCPU, le stub EL1 `bluepill` (`bluepill_arm64.go`) configure `TCR_EL1.TG0 = 0b00` (granule de 4 Ko) dans `TTBR0_EL1` et remplit des entrées de tables de pages d'étage 1 de 4 Ko avec des permissions exactes `R-X` ou `RW-` (`UXN=1`). Comme la MMU ARM64 applique l'intersection des permissions des étages 1 et 2, le matériel piège les fautes de permission à des frontières exactes de 4 Ko.
- **Piégeage d'appel système** : Le `SVC #0` invité en EL0 piège vers `VBAR_EL1` en EL1, écrit les registres dans la `CommPage` partagée de 4 Ko et exécute `HVC #0` pour revenir de `hv_vcpu_run` vers le thread Go de Sentry.

### 3. Repli en mode utilisateur Windows : `platform/systrap` (`winsystrap`)

Lorsque la virtualisation matérielle est indisponible (par exemple au sein de VM cloud imbriquées), `winsystrap` exécute le code invité en mode utilisateur sur `windows/amd64` et `windows/arm64` :

- **Isolation matérielle `CR3` / `TTBR0_EL1`** : Les threads invités s'exécutent au sein de sous-processus stub isolés, démarrés à partir d'un exécutable généré de 1 Ko (`stubimage_xplat.go`) confiné par un jeton d'intégrité Low et un Job Object.
- **Patch binaire sur place et IPC `winSysmsg`** : Sur `amd64`, le patcher (`patcher_amd64_xplat.go`) remplace les instructions `syscall` (`0f 05`) de 2 octets par des sauts `jmp rel32` de 5 octets (ou des pièges à points d'arrêt `CC 90` lorsqu'une fenêtre de 5 octets est indisponible) vers un îlot à chemin rapide. Sur `arm64`, `svc #0` est patché par une branche `B` vers un emplacement de l'îlot. L'îlot écrit les registres dans la page partagée `winSysmsg` et se synchronise avec Sentry via `WaitOnAddress` et `WakeByAddressSingle`, tandis que les pièges non patchés et les fautes de page s'arrêtent sur un Windows Debug Object (`WaitForDebugEventEx`).

### 4. Moteur en mode utilisateur Linux : `platform/systrap`

Sur `linux/amd64` et `linux/arm64`, `systrap` exécute des threads invités (`sysmsg`) qui interceptent les appels système invités via `SECCOMP_RET_TRAP` (`SIGSYS`) et patchent en binaire les sites `syscall` vers un trampoline en mémoire partagée (`sysmsg`) synchronisé via `SYS_FUTEX`, tandis qu'un stub `syscallThread` (`syscall_thread.go`) exécute les opérations d'espace d'adressage (`mmap`, `munmap`, `clone`) via `SECCOMP_RET_USER_NOTIF` (`_RUN_SECCOMP_LOOP`). Lorsque `gvx` s'exécute au sein d'un bac à sable `gVisor` externe (`runsc`) où l'unique emplacement `SECCOMP_FILTER_FLAG_NEW_LISTENER` renvoie `EBUSY`, `gvx` bascule automatiquement `syscallThread` vers une boucle futex en mémoire partagée (`_RUN_SYSCALL_LOOP`), comme détaillé dans la [Partie 3 de cette série](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03).

---

## Comment `gvx` associe les appels système Linux invités aux sous-systèmes virtuels et aux handles hôte

Une fois qu'un moteur de plateforme intercepte un `SYSCALL` (`amd64`) ou un `SVC #0` (`arm64`) invité et écrit les registres invités dans la page partagée de 4 Ko (`CommPage`, `sysmsg` ou `winSysmsg`), le contrôle revient à la goroutine de tâche Go de `Sentry` (`pkg/sentry/kernel/task_syscall.go`). Plutôt que de transmettre les appels système Linux au noyau du système d'exploitation hôte, `Task.doSyscall()` sauvegarde l'état original des registres (`SyscallSaveOrig()`), évalue les filtres BPF `seccomp` et les arrêts `ptrace` éventuels dans l'invité, puis répartit le numéro d'appel système via `linux.AMD64` ou `linux.ARM64` (`pkg/sentry/syscalls/linux/linux64.go`, couvrant environ 370 appels système Linux) vers quatre domaines internes :

![Comment gvx associe les appels système Linux invités aux sous-systèmes et à l'OS hôte](/assets/images/gvx/gvx-syscall-mapping.png)

| Domaine d'appel système | Appels système Linux invités représentatifs | Sous-système interne `gvx` (`pkg/sentry`) | Interaction avec la frontière du système d'exploitation hôte (après `hostlockdown`) |
| :--- | :--- | :--- | :--- |
| **A. Processus, espace de noms, montage et synchronisation** | `clone`, `execve`, `unshare`, `setns`, `mount`, `umount2`, `pivot_root`, `setuid`, `capset`, `futex`, `epoll_wait`, `pipe2` | `kernel.Task`, identifiants virtuels, table des futex et `VFS2` en mémoire (`procfs`, `sysfs`, `cgroupfs` v2, `tmpfs`, `overlayfs`) | **Zéro appel système hôte** (état Go à 100 % en mémoire ; totalement invisible pour le système d'exploitation hôte) |
| **B. Mémoire virtuelle (`MM`)** | `mmap`, `munmap`, `mremap`, `mprotect`, `brk`, `madvise`, `memfd_create` | `mm.MemoryManager` (arbre de VMA à 4 Ko) et `pgalloc.MemoryFile` | Mises à jour bidirectionnelles des tables de pages de 4 Ko via `WHvMapGpaRange`, `hv_vm_map` + `TTBR0_EL1`, ou `mmap`/`mprotect` du stub |
| **C. Fichiers et stockage (`VFS2`)** | `openat`, `pread64`, `pwrite64`, `getdents64`, `statx`, `renameat2`, `setxattr`, `mknod`, `inotify_add_watch` | Routeur de table de montage `vfs.VirtualFilesystem` (`fsimpl/erofs`, `fsimpl/ext4lite`, `fsimpl/hostfs` v2) | `pread`/`pwrite` bidirectionnels sur 1 FD `.erofs` épinglé ou 1 FD `.ext4`, ou parcours relatifs délimités sur `*os.Root` |
| **D. Réseau et Netfilter** | `socket`, `bind`, `listen`, `connect`, `sendmsg`, `recvmsg`, `netlink`, `setsockopt(IPT_SO_SET_REPLACE)` | `pkg/tcpip` en espace utilisateur (`lo`, `eth0`, `docker0`, `veth`, `vxlan`, `wireguard`, `ipset`, `iptables-legacy`) | Le trafic intra-bac à sable utilise **zéro appel système hôte** ; le trafic externe correspond à `net.Dial`/`net.Listen` (`-net=nat`) ou à 1 socket UDP (`-net=wg`) |

Lorsque le gestionnaire renvoie `(rval, ctrl, err)`, `Task.doSyscallInvoke()` convertit toute erreur en `errno` Linux négatif (`-ExtractErrno(err, sysno)`), réécrit la valeur de retour dans la page de registres partagée (`RAX` sur `amd64`, `X0` sur `arm64`) et reprend l'exécution invitée.

---

## Stockage (`VFS2`) et réseau en espace utilisateur à double piste (`netstack`)

Toutes les opérations de fichiers et réseau invitées sont traitées au sein du `Sentry` en Go par l'intermédiaire de trois systèmes de fichiers `VFS2` et de deux pistes `pkg/netbackend` :

![Moteurs de stockage VFS2 de gvx et netstack à double piste](/assets/images/gvx/gvx-storage-netstack.png)

| Moteur de stockage     | Rôle de montage dans `gvx`                       | Ressource hôte sous-jacente            | Appels hôte `openat` / `NtCreateFile` à l'exécution | Analyses par fichier de Windows Defender (`WdFilter.sys`) |
| :--------------------- | :----------------------------------------------- | :------------------------------------- | -----------------------------------------------: | -----------------------------------------------: |
| **`fsimpl/erofs`**     | Couches inférieures d'image OCI en lecture seule | Un seul fichier `<sha256>.erofs` par couche |                                              `0` |                                              `0` |
| **`fsimpl/ext4lite`**  | Volumes nommés (`-v name:/path`)                 | Un seul fichier `<name>.ext4` par volume   |                                              `0` |                                              `0` |
| **`fsimpl/hostfs` v2** | Espace de travail hôte (`-workspace`, `-v /host:/guest`) | Répertoire hôte `*os.Root` épinglé |                     `O(depth)` par ouverture non mise en cache |                          `1` par fermeture de fichier hôte |

### Pourquoi `ext4lite` est important pour Docker, les bases de données et les gestionnaires de paquets

L'extraction ou l'écriture de dizaines de milliers de fichiers de conteneur directement sur NTFS (Windows) ou APFS (macOS) déclenche des analyses par les minifiltres antivirus (`WdFilter.sys`), des collisions de sensibilité à la casse et l'absence des attributs étendus POSIX.

`pkg/sentry/fsimpl/ext4lite` implémente un système de fichiers `ext4` en Go pur au sein d'un unique fichier hôte pré-ouvert (`%LOCALAPPDATA%\gvx\cache\volumes\<name>.ext4` sous Windows, `~/Library/Caches/gvx/volumes/<name>.ext4` sous macOS, ou `~/.cache/gvx/volumes/<name>.ext4` sous Linux) avec :

- **Blocs de 4 KiB et groupes de blocs dynamiques de 16 MiB (`meta_bg`)** : Croît dynamiquement sans blocages d'allocation de fichiers creux (`fallocate` sous Linux, `fcntl(F_PREALLOCATE)` sous macOS, `FileAllocationInfo` sous Windows) et compacte les groupes finaux inutilisés lors d'un démontage propre.
- **Journal JBD2 en mode ordonné (inode 8)** : Validations de transactions atomiques avec sommes de contrôle CRC-32 et blocs de révocation, ainsi qu'une récupération `fsckLite`.
- **Prise en charge de la couche supérieure `overlayfs`** : Implémente les attributs étendus `trusted.overlay.*` et `security.capability`, les whiteouts de périphériques caractères (`0:0`), le rapport `d_type` des entrées de répertoire et `RENAME_EXCHANGE`.

---

## Charges de travail pratiques : moteur Docker, Kubernetes (`k3s` + `Calico`) et SDK Go

### Installer `rungv` : versions préconstruites attestées SLSA ou `go install`

Vous pouvez télécharger une archive `rungv` préconstruite pour `linux/amd64`, `linux/arm64`, `darwin/arm64`, `windows/amd64` ou `windows/arm64` depuis [GitHub Releases](https://github.com/gfire-sigs/gvx/releases) (chacune construite par le pipeline de publication SLSA avec un bundle de provenance Sigstore et un `SHA256SUMS`), ou installer directement depuis les sources avec `go install` :

```sh
# Option A : Télécharger une version préconstruite et vérifier la provenance de build SLSA
gh attestation verify rungv-v0.2.0-linux-amd64.tar.gz --repo gfire-sigs/gvx \
  --signer-workflow gfire-sigs/gvx/.github/workflows/release.yml \
  --source-ref refs/tags/v0.2.0 --deny-self-hosted-runners
shasum -a 256 -c SHA256SUMS --ignore-missing
tar -xzf rungv-v0.2.0-linux-amd64.tar.gz

# Option B : Installer directement via Go (Linux / Windows)
CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest
```

> Sur macOS (`darwin/arm64`), l'archive de version préconstruite est déjà signée avec le droit `com.apple.security.hypervisor` (`xattr -d com.apple.quarantine rungv` supprime la mise en quarantaine du navigateur). Si vous compilez localement avec `go install` ou `go build` sur macOS, signez le binaire avec `codesign --sign - --entitlements entitlements.plist --force $(which rungv)`.

### 1. Exécuter un moteur Docker non privilégié (`docker:dind`)

Conformément à [`docs/docker-engine.md`](https://github.com/gfire-sigs/gvx/blob/main/docs/docker-engine.md), vous pouvez exécuter un conteneur `docker:dind` non modifié au sein de `rungv` sous Windows, macOS ou Linux et le piloter avec votre CLI `docker` hôte :

```sh
# 1. Créer le répertoire hôte pour le socket Unix redirigé
mkdir -p ~/.gvx

# 2. Démarrer docker:dind dans un bac à sable gvx avec un volume ext4lite
rungv run \
  -p unix:~/.gvx/docker.sock:2375 \
  -p 127.0.0.1:9000-9019:9000-9019 \
  -v gvx-docker:/var/lib/docker \
  -e DOCKER_TLS_CERTDIR= \
  docker:dind --tls=false
```

Dans un second terminal, pointez votre CLI `docker` hôte vers le socket du bac à sable :

```sh
docker context create gvx --docker host=unix://$HOME/.gvx/docker.sock
docker context use gvx

docker info --format '{{.Driver}} {{.OperatingSystem}}'
# Sortie : overlayfs Alpine Linux ... (conteneurisé)

docker build -t myapp ./app
docker compose up -d
```

### 2. Exécuter Kubernetes (`k3s` + `Calico`) dans des bacs à sable non privilégiés

Comme `gvx` implémente `/dev/kmsg`, `cgroup` v2 (tueur OOM `memory.max` et limitation de bande passante `cpu.max`), `/proc/diskstats`, les périphériques netlink `wireguard` et `vxlan`, `ipset` et `iptables-legacy` (`addrtype`, `conntrack`, `statistic`, `recent`), une image `rancher/k3s` non modifiée atteint `Ready` au sein d'un unique processus `rungv` :

```sh
mkdir -p ~/.gvx/k3s

rungv run \
  -e IPTABLES_MODE=legacy \
  -p 127.0.0.1:6443:6443 \
  -p 127.0.0.1:30000-30019:30000-30019 \
  -v k3s:/var/lib/rancher \
  --mount type=bind,src=$HOME/.gvx/k3s,dst=/output \
  rancher/k3s:v1.31.4-k3s1 server \
  --disable=traefik,servicelb \
  --write-kubeconfig=/output/kubeconfig.yaml \
  --write-kubeconfig-mode=644
```

Vous pouvez également joindre plusieurs nœuds `gvx` via WireGuard (`flannel-backend=wireguard-native` ou un maillage `wg0`) avec une haute disponibilité `etcd` embarquée, ou exécuter **Calico** (`calico-vxlan.yaml` avec `FELIX_IPTABLESBACKEND=Legacy`) pour appliquer les `NetworkPolicy` Kubernetes entièrement en espace utilisateur.

### 3. Intégrer des conteneurs Linux en Go via `z.gfire.dev/gvx/pkg/gvx`

Les applications Go peuvent intégrer directement des conteneurs Linux sans démon de conteneurs externe :

```go
package main

import (
	"context"
	"fmt"
	"os"

	"z.gfire.dev/gvx/pkg/gvx"
)

func main() {
	ctx := context.Background()
	sb, err := gvx.New(ctx, gvx.Config{
		Platform:      gvx.PlatformAuto,
		NetworkMode:   gvx.NetModeNAT,
		OCIImage:      "alpine:3.20",
		WorkspacePath: "./repo",
		Argv:          []string{"/usr/bin/git", "status"},
		Stdout:        os.Stdout,
		Stderr:        os.Stderr,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "init sandbox: %v\n", err)
		os.Exit(1)
	}
	defer sb.Close()

	exitCode, err := sb.Run(ctx)
	if err != nil {
		fmt.Fprintf(os.Stderr, "run sandbox: %v\n", err)
		os.Exit(1)
	}
	os.Exit(exitCode)
}
```

---

## Autres lectures dans cette série

1. **[gvx : exécuter des conteneurs Linux, Docker et Kubernetes dans un seul processus non privilégié](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)** (cet article)
2. **[Exécuter Docker, docker build et docker compose sans root ni espaces de noms utilisateur](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)** : Comment amorcer `docker:dind` dans des bacs à sable verrouillés où `sudo`, `unshare(CLONE_NEWUSER)`, `mount(2)` et `/dev/fuse` sont bloqués.
3. **[Exécuter Docker-in-Docker au sein de gVisor et de bacs à sable imbriqués avec gvx](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)** : Pourquoi `apt-get install docker.io` et `runsc` imbriqué échouent au sein de `gVisor` à travers cinq barrières noyau, et comment `gvx` exécute `docker:dind` imbriqué à l'aide d'une boucle futex en mémoire partagée.
4. **[Environnements Docker isolés et rapides pour les rollouts RL d'agents IA avec gvx](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)** : Mise à l'échelle de `SWE-bench` et des rollouts d'apprentissage par renforcement d'agents avec des centaines d'instances `docker:dind` non privilégiées en parallèle et des réinitialisations de volumes `.ext4` en copie sur écriture en 100 millisecondes.
