GoSuda

Dockerin, docker buildin ja docker composen suorittaminen ilman juurioikeuksia tai user namespaceja

By Lemon Mint views ...

Pikaratkaisu: turvallinen juurikäyttäjätön Docker kolmessa vaiheessa

Jos työskentelet lukitussa Linux-kontissa, CI-runnerissa, Kubernetes Podissa tai tekoälyavustajan koodausagentin hiekkalaatikossa (esimerkiksi pilvipohjaisessa agentin laskentaympäristössä), jossa root, sudo, user namespaces (unshare(CLONE_NEWUSER)), mount(2) ja /dev/fuse on estetty, gvx (rungv) on turvallinen, vähän vaivaa vaativa ja tokenitehokas ratkaisu kokonaisen docker:dind-moottorin ajamiseen käyttäjätilassa.

Vähän vaivaa vaativa, tokenitehokas ratkaisu tekoälyavustajille koodaukseen: Kun autonominen koodausagentti kohtaa virheen Cannot connect to the Docker daemon, yritys asentaa ja korjata docker.io-, podman- tai rootlesskit-paketteja rajoitetussa kontissa kuluttaa tyypillisesti 15 ja 30 työkalukutsun vuoroa sekä kymmeniä tuhansia kontekstitokeneita. Tämä tapahtuu kierrettäessä systemctl-, ulimit-, iptables-, unshare- ja runc-virheiden parissa, ja lopulta yritys epäonnistuu tai joudutaan kirjoittamaan Dockerfile-tiedostot käsin uudelleen. gvx:llä (rungv) agentti suorittaa yhden deterministisen kolmivaiheisen käynnistyksen yhdellä vuorolla ja ajaa sen jälkeen heti muuttumattomina tavalliset komennot docker build, docker run ja docker compose.

Miksi gvx on suunniteltu turvalliseksi (ei yksityisoikeuksien korotuksen kiertotietä): Toisin kuin isäntäkoneen /var/run/docker.sock-tiedoston liittäminen tai docker run --privileged -komennon käyttö (jotka antavat juurikäyttäjän veroisen hallinnan isäntäytimen yli), gvx ei ohita eikä heikennä mitään isäntäjärjestelmän turvallisuuskäytäntöä (PR_SET_NO_NEW_PRIVS, seccomp, AppArmor tai ei-juurikäyttäjän UID-rajoitukset). Sen sijaan rungv lukitsee oman isäntäprosessinsa (PR_SET_NO_NEW_PRIVS + tiukka BPF-seccomp-sallintalista) ja ajaa dockerd-palvelun Googlen muistiturvallisessa gVisor (Sentry) -käyttäjätilan ytimessä. Kaikki vierasjärjestelmän UID 0 -valtuudet, overlayfs-liitokset (ext4lite) ja iptables-säännöt pysyvät tiukasti eristettyinä hiekkalaatikkoprosessin sisällä.

 1# 1. Asenna rungv käyttäjätilaan (valmiiksi käännetty SLSA-todennettu julkaisu tai go install)
 2# Vaihtoehto A: Todenna ja pura SLSA-todennettu valmiiksi käännetty binääri GitHub Releases -sivulta
 3gh attestation verify rungv-v0.2.0-linux-amd64.tar.gz --repo gfire-sigs/gvx \
 4  --signer-workflow gfire-sigs/gvx/.github/workflows/release.yml \
 5  --source-ref refs/tags/v0.2.0 --deny-self-hosted-runners
 6tar -xzf rungv-v0.2.0-linux-amd64.tar.gz && install -m 0755 rungv-v0.2.0-linux-amd64/rungv ~/.local/bin/rungv
 7
 8# Vaihtoehto B: Tai asenna suoraan lähdekoodista Go-kielellä
 9CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest
10
11# 2. Käynnistä docker:dind gvx:n sisällä yhdellä tiedostolla toteutetulla ext4lite-volyymillä
12#    Välittää ~/.gvx/docker.sock-tiedoston (oikeudet 0600) vierasportille 2375
13mkdir -p ~/.gvx
14~/.local/bin/rungv run \
15  -p unix:~/.gvx/docker.sock:2375 \
16  -p 127.0.0.1:9000-9019:9000-9019 \
17  -v gvx-docker:/var/lib/docker \
18  -e DOCKER_TLS_CERTDIR= \
19  docker:dind --tls=false &
20
21# 3. Osoita tavallinen Docker CLI ja Docker Compose käyttäjätilan socketiin
22export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
23until docker info >/dev/null 2>&1; do sleep 0.5; done
24docker info --format '{{.Driver}} {{.OperatingSystem}}'
25# Tulos: overlayfs Alpine Linux ... (containerized)
26
27docker build -t app:test .
28docker run --rm app:test
29docker compose up -d

Miksi tavalliset juurikäyttäjättömät työkalut epäonnistuvat rajoitetuissa hiekkalaatikoissa

Kun insinööri tai autonominen koodausagentti tarvitsee ajaa docker build-, docker run- tai docker compose -komentoja vahvistetussa kontissa tai monivuokraajaajurissa, tavalliset kiertotavat epäonnistuvat viidessä erillisessä ytimen esteessä.

1. Isännän Docker-daemonin socket puuttuu (myös systemctl ja service docker start epäonnistuvat)

docker ps- tai docker build -komento epäonnistuu välittömästi, kun isännän /var/run/docker.sock ei ole liitetty ympäristöön. Yritys käynnistää docker.io komennoilla systemctl tai service docker start epäonnistuu, koska PID 1 ei ole systemd ja /etc/init.d/docker osuu kontin kovaan ulimit -n -rajaan:

1Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
2ERROR: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
3System has not been booted with systemd as init system (PID 1). Can't operate.
4Failed to connect to bus: Host is down
5/etc/init.d/docker: 62: ulimit: error setting limit (Operation not permitted)

2. PR_SET_NO_NEW_PRIVS estää sudo-komennon ja setuid-apuohjelmat

Vahvistetut kontit (securityContext.allowPrivilegeEscalation: false Kubernetesissa, --security-opt=no-new-privileges Dockerissa tai exebox- ja bwrap-hiekkalaatikot) asettavat Linuxin PR_SET_NO_NEW_PRIVS-bitin prosessipuuhun. Yritys korottaa oikeuksia sudo-komennolla tai setuid-binääreillä (newuidmap, newgidmap, fusermount3) epäonnistuu:

1sudo: The "no new privileges" flag is set, which prevents sudo from running as root.
2sudo: 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- ja sysctl-käytännöt estävät unshare(CLONE_NEWUSER)-kutsun

Työkalut, jotka markkinoidaan "juurikäyttäjättöminä" (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap), luottavat silti Linuxin user namespaces -toimintoon (CLONE_NEWUSER). Ne epäonnistuvat aina, kun:

  • Ulomman kontin oletusarvoinen seccomp-profiili estää unshare(2)- tai clone(CLONE_NEWUSER)-kutsun.
  • Ubuntu 24.04 ja uudemmat ottavat käyttöön asetuksen kernel.apparmor_restrict_unprivileged_userns = 1.
  • Debian- tai RHEL-solmuissa on asetettu kernel.unprivileged_userns_clone = 0 tai user.max_user_namespaces = 0.
  • newuidmap ei voi kirjoittaa tiedostoon /proc/self/uid_map, koska PR_SET_NO_NEW_PRIVS on aktiivinen.

Syntyvät virheet näyttävät tältä:

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

4. mount(2) ja pivot_root(2) estetään docker build -komennon aikana ilman isännän CAP_SYS_ADMIN-oikeutta

Vaikka ympäristö sallisi CLONE_NEWUSER-toiminnon tai dockerd pakotettaisiin käynnistymään asetuksilla --iptables=false --bridge=none --storage-driver=vfs, docker build epäonnistuu ensimmäisessä RUN-ohjeessa. Syynä on se, että uuden /proc-tiedostojärjestelmän liittäminen tai pivot_root(2)-kutsu sisäkkäisessä mount-nimiavaruudessa on estetty:

1ERROR: failed to solve: process "/bin/sh -c ..." did not complete successfully:
2mount("/proc", "/proc", "proc", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL): Operation not permitted
3OCI 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
4OCI runtime create failed: runc create failed: unable to start container process: error during container init: error jailing process inside rootfs: pivot_root .: invalid argument
5bwrap: Creating new namespace failed: Operation not permitted

5. Puuttuva /dev/fuse rikkoo fuse-overlayfs-toiminnon

Juurikäyttäjättömät konttimoottorit, jotka eivät voi liittää ytimen overlayfs-tiedostojärjestelmää, siirtyvät käyttämään fuse-overlayfs-toimintoa. Minimaalisissa konteissa tai Kubernetes-podeissa ilman /dev/fuse-laitteen läpivientiä fuse-overlayfs epäonnistuu välittömästi:

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

Miten gvx ajaa docker:dind-moottorin kokonaan käyttäjätilassa

gvx ohittaa kaikki viisi ydinestettä, koska se ei pyydä isäntäjärjestelmän Linux-ydintä luomaan nimiavaruuksia, liittämään tiedostojärjestelmiä tai määrittämään verkkoliittymiä. Sen sijaan rungv ajaa sisäisen käyttäjätilan Linux-ytimen (gVisor Sentry) ja käyttäjätilan verkkopinon (netstack) yhden tavallisen, juurikäyttäjättömän prosessin sisällä.

Juurikäyttäjättömän Docker-in-Docker-ajon suoritus gvx:llä

1. Muistissa olevat nimiavaruudet ja liitostaulu (VFS2)

Kun dockerd, containerd ja runc suoritetaan rungv:n sisällä, systrap siepaa niiden järjestelmäkutsut ja ohjaa ne prosessin sisäiseen Sentry-ytimeen isäntäytimen sijaan:

  • unshare(2), clone(2), pivot_root(2), chroot(2) ja mount(2) toimivat Sentry-ytimen muistissa olevan tehtävähierarkian ja VFS2-liitostaulun (procfs, sysfs, tmpfs, devpts, cgroupfs v2) kanssa, kun isäntäprosessi rungv toimii juurikäyttäjättömällä isäntä-UID:llä.

2. Natiivi overlayfs yhden tiedoston ext4lite-volyymeillä

Kun välität parametrin -v gvx-docker:/var/lib/docker (tai --mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker), gvx liittää sisäänrakennetun ext4lite-tiedostojärjestelmän kohteeseen /var/lib/docker.

Koska ext4lite toteuttaa JBD2-päiväkirjoitetun ext4-tiedostojärjestelmän yhden isäntätiedoston (gvx-docker.ext4) sisällä ja tukee trusted.overlay.*-laajennettuja attribuutteja, merkkilaitteita whiteout-merkintöihin (0:0), d_type-tyyppiä ja RENAME_EXCHANGE-toimintoa, dockerd käyttää vierasjärjestelmässä natiivia overlayfs-tallennusajuria. Monivaiheiset docker build -operaatiot jakavat Copy-on-Write-kerrokset ilman /dev/fuse-laitetta ja ilman Dockerin vara-ajurin vfs levytilan räjähdysmäistä kasvua.

3. Käyttäjätilan docker0-silta ja iptables-legacy

Kun docker:dind käynnistyy gvx:n sisällä:

  • Vieras-dockerd luo docker0-sillan (172.17.0.1/16), liittää virtuaaliset veth-parit sisäkkäisiä kontteja varten ja ohjelmoi NAT- (DOCKER, POSTROUTING) ja FILTER-ketjut addrtype- ja conntrack-vastaavuuksilla gvx:n käyttäjätilan netstack-pinon sisällä.
  • Parametrin -p unix:~/.gvx/docker.sock:2375 välittäminen sitoo isäntäkoneen Unix-domain-socketin (oikeudet 0600) ja välittää yhteydet suoraan dockerd-palvelun porttiin 2375 hiekkalaatikon sisällä.

Vertailu olemassa oleviin konttityökaluihin

Ominaisuus / rajoiteTavallinen Docker (dockerd)Rootless Docker / PodmanKaniko / Buildah (käyttäjätaso)gvx (rungv + docker:dind)
Isännän root tai sudo vaaditaanKylläEiEiEi
Toimii PR_SET_NO_NEW_PRIVS=1-asetuksellaEiEi (newuidmap epäonnistuu)OsittainKyllä
Toimii, kun unshare(CLONE_NEWUSER) on estetty (EPERM)Ei sovelluEiBuildah: Ei / Kaniko: KylläKyllä
Toimii ilman /dev/fuse-laitettaKyllä (käyttää ytimen overlay2-ajuria)Vain hitaan vfs-kopioinnin kauttaKylläKyllä (overlayfs ext4lite-tiedostojärjestelmällä)
Tukee docker build -komentoa (BuildKit)KylläKyllä (jos userns sallitaan)Vain imagen rakennus (ei ajoaikaa)Kyllä
Tukee docker run- ja docker compose up -komentojaKylläKyllä (jos userns sallitaan)EiKyllä
Toimii Windowsissa ja macOS:ssä ilman Linux-virtuaalikonettaEi (vaatii WSL2:n / virtuaalikoneen)Ei (vaatii Linux-virtuaalikoneen)EiKyllä (whpx, hvf, systrap)

Tarkat isäntäytimen vaatimukset Linuxissa

gvx ei vaadi lainkaan korotettuja valtuuksia (CapEff: 0000000000000000) eikä yhtään user namespacea. Linux-isännässä rungv nojaa vain kolmeen tavanomaiseen juurikäyttäjättömään ytimen primitiiviin:

  1. ptrace(PTRACE_ATTACH) omaan lapsisäikeeseen kerran alustuksen aikana: systrap käynnistää paikallisen tukisäikeen (stub helper thread) käynnistyessään, kytkeytyy siihen jaetun muistin kontekstin alustamiseksi ja irrottautuu. Jos isäntä asettaa kernel.yama.ptrace_scope = 3 (joka poistaa kaiken ptrace-käytön järjestelmänlaajuisesti) tai estää ptrace-kutsun ulomman seccomp-suodattimen avulla, systrap-alustus ei voi valmistua. (Kun yama.ptrace_scope = 1, joka on oletusarvo Ubuntussa ja Debianissa, gvx onnistuu, koska vanhemman ja lapsen välinen jäljitys on sallittu.)
  2. seccomp(SECCOMP_SET_MODE_FILTER): Käytetään vierasjärjestelmän järjestelmäkutsujen sieppaamiseen konttien tukisäikeistä. Kuten tämän sarjan osassa 3 on käsitelty, gvx toimii sekä silloin, kun SECCOMP_FILTER_FLAG_NEW_LISTENER on saatavilla, että silloin, kun se palauttaa EBUSY-virheen sisäkkäisessä hiekkalaatikossa.
  3. mmap(..., PROT_EXEC, ...): Käytetään vierasjärjestelmän ELF-segmenttien ja systrap-jaetun muistin trampoliinin kuvaamiseen.

Päästä päähän -esimerkki: monikonttisovelluksen rakentaminen ja testaaminen ilman juurikäyttäjää

Seuraava skripti suorittaa täydellisen docker build -komennon ja kahden kontin integraatiotestin (redis + sovelluskontti) juurikäyttäjättömässä käyttäjäistunnossa:

 1mkdir -p ~/.gvx
 2rungv run \
 3  -p unix:~/.gvx/docker.sock:2375 \
 4  -v gvx-docker:/var/lib/docker \
 5  -e DOCKER_TLS_CERTDIR= \
 6  docker:dind --tls=false &
 7DIND_PID=$!
 8
 9export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
10until docker info >/dev/null 2>&1; do sleep 0.5; done
11
12mkdir -p /tmp/demo-app && cd /tmp/demo-app
13cat <<'EOF' > Dockerfile
14FROM alpine:3.20
15RUN apk add --no-cache redis
16CMD ["redis-cli", "-h", "cache", "ping"]
17EOF
18
19# Rakenna imagi käyttäen overlayfs-tiedostojärjestelmää ext4lite-tiedostojärjestelmän päällä
20docker build -t demo-ping:latest .
21
22# Luo siltaverkko, käynnistä Redis ja varmista yhteys konttien välillä
23docker network create demo-net
24docker run -d --name cache --network demo-net redis:7-alpine
25docker run --rm --network demo-net demo-ping:latest
26# Tulos: PONG
27
28# Siivoa työkuormakontit ja pysäytä hiekkalaatikko
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID

Lisälukemista tästä sarjasta

  1. gvx: Linux-konttien, Dockerin ja Kubernetesin ajaminen yhdessä juurikäyttäjättömässä prosessissa: Arkkitehtuurikatsaus gvx:stä, sen alustoristiriippumattomista suoritusmoottoreista (whpx, hvf, systrap), VFS2:sta (erofs, ext4lite, hostfs v2) ja netstack:sta.
  2. Dockerin, docker buildin ja docker composen ajaminen ilman juurikäyttäjää tai user namespaceja (tämä kirjoitus)
  3. Docker-in-Dockerin ajaminen gVisorin ja sisäkkäisten hiekkalaatikoiden sisällä gvx:n avulla: Miten gvx ratkaisee modprobe-, iptables-nft- ja overlay2-toiminnon gofer-palvelussa sekä sisäkkäisen SECCOMP_FILTER_FLAG_NEW_LISTENER-kutsun (EBUSY) gVisor (runsc) -ympäristön sisällä.
  4. Nopeat eristetyt Docker-ympäristöt tekoälyagenttien RL-ajoihin gvx:n avulla: Rinnakkaisten SWE-bench- ja RL-ajoympäristöjen skaalaaminen 100 millisekunnin copy-on-write .ext4-volyymien nollauksilla.