---
id: 40e9d44f33fa847eb498f09bac5bf144
author: Lemon Mint
title: Rularea Docker, docker build și docker compose fără root sau user namespaces
description: Cum să rulați în mod securizat un Docker Engine complet (dockerd, BuildKit, overlayfs și docker compose) într-un sandbox gVisor izolat, în spațiul utilizatorului, ca utilizator neprivilegiat, atunci când root, sudo, unshare(CLONE_NEWUSER), mount(2) și /dev/fuse sunt blocate.
language: ro
date: 2026-11-06T12:02:56.050897958Z
path: /blog/posts/run-docker-without-root-or-userns-gvx-z48916d02
go_package: z.gfire.dev/gvx
go_repourl: https://github.com/gfire-sigs/gvx.git
category: ops
---

## Soluție rapidă: Rulați Docker fără privilegii în trei pași

Dacă lucrați într-un container Linux restricționat, un runner CI, un Kubernetes Pod sau un sandbox pentru agenți AI în care `root`, `sudo`, user namespaces (`unshare(CLONE_NEWUSER)`), `mount(2)` și `/dev/fuse` sunt blocate, **[gvx](https://github.com/gfire-sigs/gvx)** (`rungv`) este **soluția sigură, bazată pe apărare în profunzime**, pentru a rula un motor `docker:dind` complet în spațiul utilizatorului.

> **De ce `gvx` este sigur prin design (nu este o soluție de ocolire a escaladării privilegiilor)**: Spre deosebire de montarea socket-ului host `/var/run/docker.sock` sau de rularea `docker run --privileged` (care acordă un control echivalent cu cel de root asupra kernelului host), `gvx` nu ocolește și nu slăbește nicio politică de securitate a host-ului (`PR_SET_NO_NEW_PRIVS`, `seccomp`, AppArmor sau limitările UID-urilor non-root). În schimb, `rungv` își blochează propriul proces host (`PR_SET_NO_NEW_PRIVS` + allowlist `seccomp` BPF strict) și rulează `dockerd` în interiorul kernelului user-space **gVisor (`Sentry`)** al Google, un kernel cu siguranță a memoriei. Toate capabilitățile `UID 0` ale oaspetelui, montările `overlayfs` (`ext4lite`) și regulile `iptables` rămân strict izolate în interiorul procesului sandbox.

```sh
# 1. Instalați rungv în spațiul utilizatorului (versiune prebuilt cu atestare SLSA sau go install)
# Opțiunea A: Verificați și extrageți binarul prebuilt cu atestare SLSA din GitHub Releases
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
tar -xzf rungv-v0.2.0-linux-amd64.tar.gz && install -m 0755 rungv-v0.2.0-linux-amd64/rungv ~/.local/bin/rungv

# Opțiunea B: Sau instalați direct din sursă cu Go
CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest

# 2. Porniți docker:dind în gvx cu un volum ext4lite dintr-un singur fișier
#    Redirecționează ~/.gvx/docker.sock (modul 0600) către portul 2375 al oaspetelui
mkdir -p ~/.gvx
~/.local/bin/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 &

# 3. Indicați CLI-ul Docker standard și Docker Compose către socket-ul din spațiul utilizatorului
export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
until docker info >/dev/null 2>&1; do sleep 0.5; done
docker info --format '{{.Driver}} {{.OperatingSystem}}'
# Ieșire: overlayfs Alpine Linux ... (containerized)

docker build -t app:test .
docker run --rm app:test
docker compose up -d
```

---

## De ce instrumentele standard rootless eșuează în sandbox-uri restricționate

Atunci când un inginer sau un agent de codare autonom are nevoie să ruleze `docker build`, `docker run` sau `docker compose` într-un container întărit sau într-un runner multi-tenant, soluțiile standard eșuează în cinci bariere distincte ale kernelului.

### 1. Lipsa socket-ului daemonului Docker de pe host

Rularea `docker ps` sau `docker build` eșuează imediat atunci când `/var/run/docker.sock` de pe host nu este montat în mediu:

```text
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
```

### 2. `PR_SET_NO_NEW_PRIVS` blochează `sudo` și helperii setuid

Containerele întărite (`securityContext.allowPrivilegeEscalation: false` în Kubernetes, `--security-opt=no-new-privileges` în Docker sau sandbox-urile `exebox` / `bwrap`) setează bitul Linux `PR_SET_NO_NEW_PRIVS` pe arborele de procese. Încercarea de escaladare prin `sudo` sau binare setuid (`newuidmap`, `newgidmap`, `fusermount3`) eșuează:

```text
sudo: The "no new privileges" flag is set, which prevents sudo from running as root.
sudo: effective uid is not 0, is /usr/bin/sudo on a file system with the 'nosuid' option set or an NFS file system without root privileges?
```

### 3. Politicile seccomp și sysctl blochează `unshare(CLONE_NEWUSER)`

Instrumentele promovate ca „rootless” (`Rootless Docker`, `Podman`, `Buildah`, `nerdctl`, `bubblewrap`) se bazează totuși pe user namespaces Linux (`CLONE_NEWUSER`). Ele eșuează ori de câte ori:

- Profilul seccomp implicit al containerului exterior blochează `unshare(2)` sau `clone(CLONE_NEWUSER)`.
- Ubuntu 24.04+ activează `kernel.apparmor_restrict_unprivileged_userns = 1`.
- Nodurile Debian sau RHEL setează `kernel.unprivileged_userns_clone = 0` sau `user.max_user_namespaces = 0`.
- `newuidmap` nu poate scrie în `/proc/self/uid_map` deoarece `PR_SET_NO_NEW_PRIVS` este activ.

Erorile rezultate arată astfel:

```text
unshare: unshare failed: Operation not permitted
cannot clone: Operation not permitted
Error: cannot re-exec process to join the existing user namespace
newuidmap: write to uid_map failed: Operation not permitted
RootlessKit: parent: failed to start the child: fork/exec /proc/self/exe: operation not permitted
```

### 4. `mount(2)` este blocat fără `CAP_SYS_ADMIN` pe host

Chiar dacă un mediu permite `CLONE_NEWUSER`, montarea unui `/proc` nou într-un mount namespace imbricat eșuează dacă containerul exterior a mascat subarborii `/proc` (`/proc/acpi`, `/proc/kcore`, `/proc/keys`) sau a eliminat `mount(2)` din seccomp:

```text
mount("/proc", "/proc", "proc", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL): Operation not permitted
OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "proc" to rootfs at "/proc": mount proc:/proc (via /proc/self/fd/6), flags: 0xe: operation not permitted
bwrap: Creating new namespace failed: Operation not permitted
```

### 5. Lipsa `/dev/fuse` împiedică funcționarea `fuse-overlayfs`

Motoarele de containere rootless care nu pot monta `overlayfs` de kernel trec la `fuse-overlayfs`. În containerele minimale sau în pod-urile Kubernetes fără transmiterea dispozitivului `/dev/fuse`, `fuse-overlayfs` eșuează imediat:

```text
fuse: device not found, try 'modprobe fuse' first
fusermount3: failed to open /dev/fuse: No such file or directory
error initializing graphdriver: driver not supported: fuse-overlayfs
```

---

## Cum rulează `gvx` `docker:dind` complet în spațiul utilizatorului

`gvx` ocolește toate cele cinci bariere ale kernelului deoarece nu cere kernelului Linux de pe host să creeze namespaces, să monteze sisteme de fișiere sau să configureze interfețe de rețea. În schimb, `rungv` rulează un kernel Linux intern, în spațiul utilizatorului (gVisor `Sentry`), și o stivă de rețea în spațiul utilizatorului (`netstack`), în interiorul unui singur proces obișnuit, fără privilegii.

![Running Unprivileged Docker-in-Docker with gvx](/assets/images/gvx/gvx-unprivileged-dind.png)

### 1. Namespaces în memorie și tabela de montare (`VFS2`)

Atunci când `dockerd`, `containerd` și `runc` se execută în `rungv`, `systrap` interceptează apelurile lor de sistem și le direcționează către kernelul `Sentry` din proces, nu către kernelul host:

- `unshare(2)`, `clone(2)`, `pivot_root(2)`, `chroot(2)` și `mount(2)` operează asupra ierarhiei de task-uri în memorie a `Sentry` și asupra tabelei de montare `VFS2` (`procfs`, `sysfs`, `tmpfs`, `devpts`, `cgroupfs` v2), în timp ce procesul host `rungv` rulează sub UID-ul dvs. host fără privilegii.

### 2. `overlayfs` nativ pe volume `ext4lite` dintr-un singur fișier

Atunci când transmiteți `-v gvx-docker:/var/lib/docker` (sau `--mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker`), `gvx` montează sistemul de fișiere integrat **`ext4lite`** la `/var/lib/docker`.

Deoarece `ext4lite` implementează un sistem de fișiere `ext4` cu jurnal JBD2 într-un singur fișier de pe host (`gvx-docker.ext4`), cu suport pentru atributele extinse `trusted.overlay.*`, dispozitivele whiteout de tip caracter (`0:0`), `d_type` și `RENAME_EXCHANGE`, `dockerd` utilizează driverul de stocare nativ **`overlayfs`** în interiorul oaspetelui. Operațiile `docker build` în mai multe etape partajează straturile Copy-on-Write fără `/dev/fuse` și fără explozia spațiului pe disc specifică driverului de rezervă `vfs` al Docker.

### 3. Bridge-ul `docker0` în spațiul utilizatorului și `iptables-legacy`

Atunci când `docker:dind` pornește în `gvx`:

- `dockerd` din interiorul oaspetelui creează bridge-ul `docker0` (`172.17.0.1/16`), atașează perechi virtuale `veth` pentru containerele imbricate și programează lanțurile `NAT` (`DOCKER`, `POSTROUTING`) și `FILTER` cu potriviri `addrtype` și `conntrack` în `netstack`-ul `gvx` din spațiul utilizatorului.
- Transmiterea `-p unix:~/.gvx/docker.sock:2375` leagă un socket Unix de domeniu de pe host (modul `0600`) și redirecționează conexiunile direct către portul `2375` al `dockerd` din sandbox.

---

## Comparație cu instrumentele de containerizare existente

| Capabilitate / Constrângere                                  | Docker standard (`dockerd`)  | Rootless Docker / Podman  | Kaniko / Buildah (fără privilegii) | `gvx` (`rungv` + `docker:dind`)     |
| :----------------------------------------------------------- | :--------------------------- | :------------------------ | :--------------------------------- | :---------------------------------- |
| **Necesită `root` sau `sudo` pe host**                       | Da                           | Nu                        | Nu                                 | **Nu**                              |
| **Funcționează cu `PR_SET_NO_NEW_PRIVS=1`**                  | Nu                           | Nu (`newuidmap` eșuează)  | Parțial                            | **Da**                              |
| **Funcționează când `unshare(CLONE_NEWUSER)` este blocat (`EPERM`)** | N/A                  | Nu                        | Buildah: Nu / Kaniko: Da           | **Da**                              |
| **Funcționează fără `/dev/fuse`**                            | Da (folosește `overlay2` al kernelului) | Doar prin copierea lentă `vfs` | Da                    | **Da (`overlayfs` pe `ext4lite`)**  |
| **Suportă `docker build` (BuildKit)**                        | Da                           | Da (dacă `userns` este permis) | Doar construire de imagini (fără runtime) | **Da**              |
| **Suportă `docker run` și `docker compose up`**              | Da                           | Da (dacă `userns` este permis) | **Nu**                        | **Da**                              |
| **Rulează pe Windows și macOS fără VM Linux**                | Nu (necesită WSL2 / VM)      | Nu (necesită VM Linux)    | Nu                                 | **Da (`whpx`, `hvf`, `systrap`)**   |

---

## Cerințe exacte ale kernelului host pe Linux

`gvx` nu necesită capabilități ridicate (`CapEff: 0000000000000000`) și nici user namespaces. Pe un host Linux, `rungv` se bazează doar pe trei primitive standard ale kernelului, fără privilegii:

1. **`ptrace(PTRACE_ATTACH)` asupra propriului său thread copil, o singură dată, la inițializare**: `systrap` pornește un thread helper local la pornire, se atașează la acesta pentru a inițializa contextul de memorie partajată și apoi se detașează. Dacă host-ul setează `kernel.yama.ptrace_scope = 3` (ceea ce dezactivează `ptrace` la nivel de sistem) sau blochează `ptrace` într-un filtru seccomp exterior, inițializarea `systrap` nu poate fi finalizată. (Cu `yama.ptrace_scope = 1`, valoarea implicită pe Ubuntu și Debian, `gvx` reușește, deoarece trasarea de la părinte la copil este permisă.)
2. **`seccomp(SECCOMP_SET_MODE_FILTER)`**: Folosit pentru a intercepta apelurile de sistem ale oaspetelui din thread-urile stub ale containerului. După cum este prezentat în [Partea 3 a acestei serii](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03), `gvx` funcționează atât atunci când `SECCOMP_FILTER_FLAG_NEW_LISTENER` este disponibil, cât și atunci când returnează `EBUSY` într-un sandbox imbricat.
3. **`mmap(..., PROT_EXEC, ...)`**: Folosit pentru a mapa segmentele ELF ale oaspetelui și trampolina de memorie partajată a `systrap`.

---

## Exemplu cap-coadă: Construirea și testarea unei aplicații multi-container fără root

Următorul script rulează un `docker build` complet și un test de integrare cu două containere (`redis` + container de aplicație) într-o sesiune de utilizator fără privilegii:

```sh
mkdir -p ~/.gvx
rungv run \
  -p unix:~/.gvx/docker.sock:2375 \
  -v gvx-docker:/var/lib/docker \
  -e DOCKER_TLS_CERTDIR= \
  docker:dind --tls=false &
DIND_PID=$!

export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
until docker info >/dev/null 2>&1; do sleep 0.5; done

mkdir -p /tmp/demo-app && cd /tmp/demo-app
cat <<'EOF' > Dockerfile
FROM alpine:3.20
RUN apk add --no-cache redis
CMD ["redis-cli", "-h", "cache", "ping"]
EOF

# Construiți imaginea folosind overlayfs pe ext4lite
docker build -t demo-ping:latest .

# Creați o rețea bridge, porniți Redis și verificați conectivitatea între containere
docker network create demo-net
docker run -d --name cache --network demo-net redis:7-alpine
docker run --rm --network demo-net demo-ping:latest
# Ieșire: PONG

# Curățați containerele de lucru și opriți sandbox-ul
docker rm -f cache
docker network rm demo-net
kill -INT $DIND_PID
```

---

## Alte lecturi din această serie

1. **[gvx: Running Linux Containers, Docker, and Kubernetes in One Unprivileged Process](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)**: Prezentare arhitecturală a `gvx`, a motoarelor sale de execuție multi-platformă (`whpx`, `hvf`, `systrap`), a `VFS2` (`erofs`, `ext4lite`, `hostfs` v2) și a `netstack`.
2. **[Running Docker, docker build, and docker compose Without Root or User Namespaces](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)** (această postare)
3. **[Running Docker-in-Docker Inside gVisor and Nested Sandboxes with gvx](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)**: Cum rezolvă `gvx` `modprobe`, `iptables-nft`, `overlay2` pe `gofer` și `SECCOMP_FILTER_FLAG_NEW_LISTENER` imbricat (`EBUSY`) în interiorul `gVisor` (`runsc`).
4. **[Fast Isolated Docker Environments for AI Agent RL Rollouts with gvx](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)**: Scalarea mediilor paralele `SWE-bench` și RL rollout cu resetări de volume `.ext4` Copy-on-Write în 100 de milisecunde.
