---
id: 40e9d44f33fa847eb498f09bac5bf144
author: Lemon Mint
title: Kørsel af Docker, docker build og docker compose uden root eller brugernavneområder
description: Sådan kører du sikkert en komplet Docker Engine (dockerd, BuildKit, overlayfs og docker compose) inde i en isoleret gVisor user-space sandbox som en unprivileged user, når root, sudo, unshare(CLONE_NEWUSER), mount(2) og /dev/fuse er blokeret.
language: da
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
---

## Hurtig løsning: Sikker unprivilegeret Docker i tre trin

Hvis du arbejder i en låst Linux-container, CI-runner, Kubernetes Pod eller AI-agent-sandbox, hvor `root`, `sudo`, brugernamespaces (`unshare(CLONE_NEWUSER)`), `mount(2)` og `/dev/fuse` er blokeret, er **[gvx](https://github.com/gfire-sigs/gvx)** (`rungv`) den **sikre løsning med dyb forsvarsopbygning** til at køre en fuld `docker:dind`-motor i brugerrum.

> **Hvorfor `gvx` er sikker by design (ikke en omgåelse af privilegieeskalering)**: I modsætning til at montere den værts-`/var/run/docker.sock` eller køre `docker run --privileged` (som giver root-tilsvarende kontrol over værtskernen), omgår eller svækker `gvx` ikke nogen sikkerhedspolitik på værten (`PR_SET_NO_NEW_PRIVS`, `seccomp`, AppArmor eller begrænsninger for ikke-root UID). I stedet låser `rungv` sin egen værtsproces (`PR_SET_NO_NEW_PRIVS` + streng BPF `seccomp`-tilladelsesliste) og kører `dockerd` inde i Googles hukommelsessikre **gVisor (`Sentry`)** brugerrumskerne. Alle gæst-`UID 0`-kapaciteter, `overlayfs`-monteringer (`ext4lite`) og `iptables`-regler forbliver strengt isoleret inde i sandbox-processen.

```sh
# 1. Installer rungv i brugerrum (forudbygget SLSA-attesteret udgivelse eller go install)
# Mulighed A: Verificer og udpak den SLSA-attesterede forudbyggede binær fra 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

# Mulighed B: Eller installer direkte fra kildekode med Go
CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest

# 2. Start docker:dind inde i gvx med et enkeltfil-ext4lite-volumen
#    Videresender ~/.gvx/docker.sock (tilstand 0600) til gæstens port 2375
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. Peg den standard Docker CLI og Docker Compose på brugerrums-socket'en
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}}'
# Output: overlayfs Alpine Linux ... (containeriseret)

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

---

## Hvorfor standard rootless-værktøjer fejler i begrænsede sandboxes

Når en ingeniør eller en autonom kodningsagent skal køre `docker build`, `docker run` eller `docker compose` inde i en hærdet container eller et multi-tenant runner-miljø, fejler standardløsninger ved fem særskilte kernelbarrierer.

### 1. Ingen værts-Docker-daemon-socket

Kørsel af `docker ps` eller `docker build` fejler straks, når værtens `/var/run/docker.sock` ikke er monteret i miljøet:

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

### 2. `PR_SET_NO_NEW_PRIVS` blokerer `sudo` og setuid-hjælpere

Hærdede containere (`securityContext.allowPrivilegeEscalation: false` i Kubernetes, `--security-opt=no-new-privileges` i Docker, eller `exebox`- / `bwrap`-sandboxes) sætter Linux-bitten `PR_SET_NO_NEW_PRIVS` på procestræet. Forsøg på at eskalere via `sudo` eller setuid-binære filer (`newuidmap`, `newgidmap`, `fusermount3`) fejler:

```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. Seccomp- og sysctl-politikker blokerer `unshare(CLONE_NEWUSER)`

Værktøjer, der markedsføres som "rootless" (`Rootless Docker`, `Podman`, `Buildah`, `nerdctl`, `bubblewrap`), er stadig afhængige af Linux **brugernamespaces** (`CLONE_NEWUSER`). De fejler, når:

- Den ydre containers standard seccomp-profil blokerer `unshare(2)` eller `clone(CLONE_NEWUSER)`.
- Ubuntu 24.04+ aktiverer `kernel.apparmor_restrict_unprivileged_userns = 1`.
- Debian- eller RHEL-noder sætter `kernel.unprivileged_userns_clone = 0` eller `user.max_user_namespaces = 0`.
- `newuidmap` kan ikke skrive til `/proc/self/uid_map`, fordi `PR_SET_NO_NEW_PRIVS` er aktiv.

De resulterende fejl ser sådan ud:

```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)` er blokeret uden værtens `CAP_SYS_ADMIN`

Selvom et miljø tillader `CLONE_NEWUSER`, fejler montering af en frisk `/proc` inde i et indlejret mount-namespace, hvis den ydre container har maskeret `/proc`-undertræer (`/proc/acpi`, `/proc/kcore`, `/proc/keys`) eller fjernet `mount(2)` i 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. Manglende `/dev/fuse` ødelægger `fuse-overlayfs`

Rootless container-motorer, der ikke kan montere kernens `overlayfs`, falder tilbage til `fuse-overlayfs`. I minimale containere eller Kubernetes-pods uden videreførelse af `/dev/fuse`-enheden fejler `fuse-overlayfs` straks:

```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
```

---

## Sådan kører `gvx` `docker:dind` helt i brugerrum

`gvx` omgår alle fem kernelbarrierer, fordi den ikke beder værtens Linux-kerne om at oprette namespaces, montere filsystemer eller konfigurere netværksgrænseflader. I stedet kører `rungv` en intern Linux-kerne i brugerrum (gVisor `Sentry`) og en netværksstak i brugerrum (`netstack`) inde i én almindelig unprivilegeret proces.

![Kørsel af unprivilegeret Docker-in-Docker med gvx](/assets/images/gvx/gvx-unprivileged-dind.png)

### 1. Namespaces og monteringstabel i hukommelsen (`VFS2`)

Når `dockerd`, `containerd` og `runc` udføres inde i `rungv`, opfanger `systrap` deres systemkald og sender dem til den in-process `Sentry`-kerne i stedet for værtskernen:

- `unshare(2)`, `clone(2)`, `pivot_root(2)`, `chroot(2)` og `mount(2)` opererer på Sentry'ens opgavehierarki i hukommelsen og `VFS2`-monteringstabellen (`procfs`, `sysfs`, `tmpfs`, `devpts`, `cgroupfs` v2), mens værtsprocessen `rungv` kører under din unprivilegerede værts-UID.

### 2. Indbygget `overlayfs` på enkeltfils-`ext4lite`-volumener

Når du angiver `-v gvx-docker:/var/lib/docker` (eller `--mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker`), monterer `gvx` sit indbyggede **`ext4lite`**-filsystem på `/var/lib/docker`.

Da `ext4lite` implementerer et JBD2-journaliseret `ext4`-filsystem inde i en enkelt værtsfil (`gvx-docker.ext4`) med understøttelse af `trusted.overlay.*`-udvidede attributter, tegn-whiteout-enheder (`0:0`), `d_type` og `RENAME_EXCHANGE`, bruger `dockerd` sin indbyggede **`overlayfs`**-lagringsdriver inde i gæsten. Flertrins `docker build`-operationer deler Copy-on-Write-lag uden `/dev/fuse` og uden den diskplads-eksplosion, som Dockers `vfs`-fallback-driver medfører.

### 3. `docker0`-bro i brugerrum og `iptables-legacy`

Når `docker:dind` starter inde i `gvx`:

- Gæst-`dockerd` opretter `docker0`-broen (`172.17.0.1/16`), tilslutter virtuelle `veth`-par til indlejrede containere og programmerer `NAT`- (`DOCKER`, `POSTROUTING`) og `FILTER`-kæder med `addrtype`- og `conntrack`-match inde i `gvx`'s `netstack` i brugerrum.
- Angivelse af `-p unix:~/.gvx/docker.sock:2375` binder et Unix-domænesocket på værten (tilstand `0600`) og videresender forbindelser direkte til port `2375` på `dockerd` inde i sandboxen.

---

## Sammenligning med eksisterende containerværktøjer

| Egenskab / begrænsning                                       | Standard Docker (`dockerd`)  | Rootless Docker / Podman  | Kaniko / Buildah (unprivilegeret) | `gvx` (`rungv` + `docker:dind`)      |
| :----------------------------------------------------------- | :--------------------------- | :------------------------ | :-------------------------------- | :----------------------------------- |
| **Kræver værts-`root` eller `sudo`**                         | Ja                           | Nej                       | Nej                               | **Nej**                              |
| **Virker med `PR_SET_NO_NEW_PRIVS=1`**                       | Nej                          | Nej (`newuidmap` fejler)  | Delvis                            | **Ja**                               |
| **Virker når `unshare(CLONE_NEWUSER)` er blokeret (`EPERM`)** | Ikke relevant                | Nej                       | Buildah: Nej / Kaniko: Ja         | **Ja**                               |
| **Virker uden `/dev/fuse`**                                  | Ja (bruger kernens `overlay2`) | Kun via langsom `vfs`-kopiering | Ja                          | **Ja (`overlayfs` på `ext4lite`)**   |
| **Understøtter `docker build` (BuildKit)**                   | Ja                           | Ja (hvis `userns` er tilladt) | Kun image-build (ingen runtime) | **Ja**                         |
| **Understøtter `docker run` og `docker compose up`**         | Ja                           | Ja (hvis `userns` er tilladt) | **Nej**                       | **Ja**                               |
| **Kører på Windows og macOS uden en Linux-VM**               | Nej (kræver WSL2 / VM)       | Nej (kræver Linux-VM)     | Nej                               | **Ja (`whpx`, `hvf`, `systrap`)**    |

---

## Præcise krav til værtskernen på Linux

`gvx` kræver ingen forhøjede kapabiliteter (`CapEff: 0000000000000000`) og ingen brugernamespaces. På en Linux-vært er `rungv` afhængig af kun tre standard unprivilegerede kernelprimitiver:

1. **`ptrace(PTRACE_ATTACH)` på sin egen underordnede tråd én gang under initialisering**: `systrap` starter ved opstart en lokal stub-hjælpetråd, tilslutter sig den for at initialisere den delte hukommelseskontekst og afkobler sig igen. Hvis værten sætter `kernel.yama.ptrace_scope = 3` (som deaktiverer al `ptrace` systemvidt) eller blokerer `ptrace` i et ydre seccomp-filter, kan `systrap`-initialiseringen ikke gennemføres. (Under `yama.ptrace_scope = 1`, standarden på Ubuntu og Debian, lykkes `gvx`, fordi sporing fra forælder til barn er tilladt.)
2. **`seccomp(SECCOMP_SET_MODE_FILTER)`**: Bruges til at opfange gæstens systemkald fra containerens stub-tråde. Som beskrevet i [del 3 af denne serie](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03) virker `gvx` både når `SECCOMP_FILTER_FLAG_NEW_LISTENER` er tilgængelig, og når den returnerer `EBUSY` inde i en indlejret sandbox.
3. **`mmap(..., PROT_EXEC, ...)`**: Bruges til at mappe gæstens ELF-segmenter og `systrap`s delte hukommelses-trampolin.

---

## End-to-end-eksempel: Build og test af en app med flere containere uden root

Følgende script kører en komplet `docker build` og en integrationstest med to containere (`redis` + applikationscontainer) inde i en unprivilegeret brugersession:

```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

# Byg imaget ved hjælp af overlayfs på ext4lite
docker build -t demo-ping:latest .

# Opret et bro-netværk, start Redis, og verificer forbindelsen mellem containerne
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
# Output: PONG

# Ryd op i arbejdsbelastningscontainerne og stop sandboxen
docker rm -f cache
docker network rm demo-net
kill -INT $DIND_PID
```

---

## Yderligere læsning i denne serie

1. **[gvx: Kør Linux-containere, Docker og Kubernetes i én unprivilegeret proces](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)**: Arkitekturoversigt over `gvx`, dens tværplatforms eksekveringsmotorer (`whpx`, `hvf`, `systrap`), `VFS2` (`erofs`, `ext4lite`, `hostfs` v2) og `netstack`.
2. **[Kør Docker, docker build og docker compose uden root eller brugernamespaces](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)** (dette indlæg)
3. **[Kør Docker-in-Docker inde i gVisor og indlejrede sandboxes med gvx](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)**: Hvordan `gvx` løser `modprobe`, `iptables-nft`, `overlay2` på `gofer` og indlejret `SECCOMP_FILTER_FLAG_NEW_LISTENER` (`EBUSY`) inde i `gVisor` (`runsc`).
4. **[Hurtige isolerede Docker-miljøer til RL-udrulninger af AI-agenter med gvx](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)**: Skalering af parallelle `SWE-bench`- og RL-udrulningsmiljøer med copy-on-write `.ext4`-volumen-nulstillinger på 100 millisekunder.
