GoSuda

Menjalankan Docker, docker build, dan docker compose Tanpa Root atau User Namespaces

By Lemon Mint views ...

Solusi Cepat: Jalankan Docker Tanpa Hak Istimewa dalam Tiga Langkah

Jika Anda bekerja di dalam container Linux yang terkunci, CI runner, Kubernetes Pod, atau sandbox agen AI tempat root, sudo, user namespace (unshare(CLONE_NEWUSER)), mount(2), dan /dev/fuse diblokir, gvx (rungv) adalah solusi yang aman dan berlapis pertahanan untuk menjalankan mesin docker:dind penuh di ruang pengguna (user space).

Mengapa gvx Aman secara Desain (Bukan Cara Eskalasi Hak Istimewa): Berbeda dengan memasang host /var/run/docker.sock atau menjalankan docker run --privileged (yang memberikan kendali setara root atas kernel host), gvx tidak melewati atau melemahkan kebijakan keamanan host mana pun (PR_SET_NO_NEW_PRIVS, seccomp, AppArmor, atau batas UID non-root). Sebaliknya, rungv mengunci proses host-nya sendiri (PR_SET_NO_NEW_PRIVS + allowlist BPF seccomp yang ketat) dan menjalankan dockerd di dalam kernel user-space gVisor (Sentry) milik Google yang aman dari kesalahan memori. Seluruh kapabilitas UID 0 di dalam guest, mount overlayfs (ext4lite), dan aturan iptables tetap terisolasi secara ketat di dalam proses sandbox.

 1# 1. Pasang rungv di ruang pengguna (rilis prebuilt ter-attest SLSA atau go install)
 2# Opsi A: Verifikasi dan ekstrak binary prebuilt ter-attest SLSA dari GitHub Releases
 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# Opsi B: Atau pasang langsung dari source dengan Go
 9CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest
10
11# 2. Jalankan docker:dind di dalam gvx dengan volume ext4lite berupa satu file
12#    Meneruskan ~/.gvx/docker.sock (mode 0600) ke port guest 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. Arahkan Docker CLI standar dan Docker Compose ke socket ruang pengguna
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# Output: overlayfs Alpine Linux ... (containerized)
26
27docker build -t app:test .
28docker run --rm app:test
29docker compose up -d

Mengapa Alat Rootless Standar Gagal di Sandbox Terbatas

Ketika seorang engineer atau agen coding otonom perlu menjalankan docker build, docker run, atau docker compose di dalam container yang dikeraskan atau runner multi-tenant, solusi standar gagal pada lima hambatan kernel yang berbeda.

1. Tidak Ada Socket Docker Daemon di Host

Menjalankan docker ps atau docker build langsung gagal ketika /var/run/docker.sock host tidak di-mount ke dalam lingkungan:

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

2. PR_SET_NO_NEW_PRIVS Memblokir sudo dan Helper Setuid

Container yang dikeraskan (securityContext.allowPrivilegeEscalation: false di Kubernetes, --security-opt=no-new-privileges di Docker, atau sandbox exebox / bwrap) mengatur bit Linux PR_SET_NO_NEW_PRIVS pada pohon proses. Upaya eskalasi melalui sudo atau binary setuid (newuidmap, newgidmap, fusermount3) akan gagal:

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. Kebijakan Seccomp dan Sysctl Memblokir unshare(CLONE_NEWUSER)

Alat yang dipasarkan sebagai "rootless" (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap) tetap bergantung pada user namespace Linux (CLONE_NEWUSER). Alat tersebut gagal setiap kali:

  • Profil seccomp default container luar memblokir unshare(2) atau clone(CLONE_NEWUSER).
  • Ubuntu 24.04+ mengaktifkan kernel.apparmor_restrict_unprivileged_userns = 1.
  • Node Debian atau RHEL mengatur kernel.unprivileged_userns_clone = 0 atau user.max_user_namespaces = 0.
  • newuidmap tidak dapat menulis ke /proc/self/uid_map karena PR_SET_NO_NEW_PRIVS aktif.

Galat yang dihasilkan terlihat seperti berikut:

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) Diblokir Tanpa CAP_SYS_ADMIN pada Host

Meskipun suatu lingkungan mengizinkan CLONE_NEWUSER, mount /proc baru di dalam mount namespace bersarang tetap gagal jika container luar telah menyembunyikan subtree /proc (/proc/acpi, /proc/kcore, /proc/keys) atau menghapus mount(2) dari seccomp:

1mount("/proc", "/proc", "proc", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL): Operation not permitted
2OCI 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
3bwrap: Creating new namespace failed: Operation not permitted

5. /dev/fuse yang Hilang Merusak fuse-overlayfs

Engine container rootless yang tidak dapat me-mount overlayfs kernel akan beralih ke fuse-overlayfs. Pada container minimal atau pod Kubernetes tanpa passthrough perangkat /dev/fuse, fuse-overlayfs langsung gagal:

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

Cara gvx Menjalankan docker:dind Sepenuhnya di Ruang Pengguna

gvx melewati kelima hambatan kernel tersebut karena tidak meminta kernel Linux host untuk membuat namespace, me-mount filesystem, atau mengonfigurasi antarmuka jaringan. Sebaliknya, rungv menjalankan kernel Linux internal berbasis ruang pengguna (gVisor Sentry) dan tumpukan jaringan ruang pengguna (netstack) di dalam satu proses unprivileged biasa.

Menjalankan Docker-in-Docker Unprivileged dengan gvx

1. Namespace dan Tabel Mount dalam Memori (VFS2)

Ketika dockerd, containerd, dan runc dijalankan di dalam rungv, systrap mencegat system call mereka dan mengarahkannya ke kernel Sentry dalam proses, bukan ke kernel host:

  • unshare(2), clone(2), pivot_root(2), chroot(2), dan mount(2) beroperasi pada hierarki task dalam memori milik Sentry serta tabel mount VFS2 (procfs, sysfs, tmpfs, devpts, cgroupfs v2), sementara proses host rungv berjalan di bawah UID host unprivileged Anda.

2. overlayfs Native pada Volume ext4lite Berupa Satu File

Ketika Anda memberikan -v gvx-docker:/var/lib/docker (atau --mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker), gvx me-mount filesystem bawaan ext4lite pada /var/lib/docker.

Karena ext4lite mengimplementasikan filesystem ext4 berjurnal JBD2 di dalam satu file host (gvx-docker.ext4) dengan dukungan atribut terperluas trusted.overlay.*, perangkat whiteout karakter (0:0), d_type, dan RENAME_EXCHANGE, dockerd menggunakan driver penyimpanan overlayfs native di dalam guest. Operasi docker build multi-tahap berbagi layer Copy-on-Write tanpa /dev/fuse dan tanpa ledakan penggunaan ruang disk seperti pada driver fallback vfs Docker.

3. Bridge docker0 Ruang Pengguna dan iptables-legacy

Ketika docker:dind dimulai di dalam gvx:

  • dockerd di dalam guest membuat bridge docker0 (172.17.0.1/16), memasang pasangan veth virtual untuk container bersarang, dan memprogram rantai NAT (DOCKER, POSTROUTING) serta FILTER dengan pencocokan addrtype dan conntrack di dalam netstack ruang pengguna milik gvx.
  • Opsi -p unix:~/.gvx/docker.sock:2375 mengikat socket Unix domain host (mode 0600) dan meneruskan koneksi langsung ke port 2375 milik dockerd di dalam sandbox.

Perbandingan dengan Alat Container yang Ada

Kapabilitas / BatasanDocker Standar (dockerd)Rootless Docker / PodmanKaniko / Buildah (Unprivileged)gvx (rungv + docker:dind)
Memerlukan root atau sudo di hostYaTidakTidakTidak
Berfungsi dengan PR_SET_NO_NEW_PRIVS=1TidakTidak (newuidmap gagal)SebagianYa
Berfungsi ketika unshare(CLONE_NEWUSER) diblokir (EPERM)Tidak berlakuTidakBuildah: Tidak / Kaniko: YaYa
Berfungsi tanpa /dev/fuseYa (menggunakan overlay2 kernel)Hanya melalui vfs yang lambatYaYa (overlayfs pada ext4lite)
Mendukung docker build (BuildKit)YaYa (jika userns diizinkan)Hanya pembuatan image (tanpa runtime)Ya
Mendukung docker run dan docker compose upYaYa (jika userns diizinkan)TidakYa
Berjalan di Windows dan macOS tanpa VM LinuxTidak (memerlukan WSL2 / VM)Tidak (memerlukan VM Linux)TidakYa (whpx, hvf, systrap)

Persyaratan Kernel Host Linux yang Tepat

gvx tidak memerlukan kapabilitas istimewa apa pun (CapEff: 0000000000000000) dan tidak memerlukan user namespace. Pada host Linux, rungv hanya bergantung pada tiga primitif kernel unprivileged standar:

  1. ptrace(PTRACE_ATTACH) pada thread child-nya sendiri satu kali selama inisialisasi: systrap menjalankan thread helper stub lokal saat startup, melakukan attach padanya untuk menginisialisasi konteks memori bersama, lalu melakukan detach. Jika host mengatur kernel.yama.ptrace_scope = 3 (yang menonaktifkan seluruh ptrace pada sistem) atau memblokir ptrace dalam filter seccomp luar, inisialisasi systrap tidak dapat diselesaikan. (Dengan yama.ptrace_scope = 1, nilai default di Ubuntu dan Debian, gvx berhasil karena tracing dari parent ke child diizinkan.)
  2. seccomp(SECCOMP_SET_MODE_FILTER): Digunakan untuk menangkap system call guest dari thread stub container. Sebagaimana dibahas dalam Bagian 3 seri ini, gvx berfungsi baik ketika SECCOMP_FILTER_FLAG_NEW_LISTENER tersedia maupun ketika mengembalikan EBUSY di dalam sandbox bersarang.
  3. mmap(..., PROT_EXEC, ...): Digunakan untuk memetakan segmen ELF guest dan trampolin memori bersama systrap.

Contoh Lengkap: Membangun dan Menguji Aplikasi Multi-Container Tanpa Root

Skrip berikut menjalankan docker build lengkap dan pengujian integrasi dua container (redis + container aplikasi) di dalam sesi pengguna unprivileged:

 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# Bangun image menggunakan overlayfs pada ext4lite
20docker build -t demo-ping:latest .
21
22# Buat jaringan bridge, jalankan Redis, dan verifikasi konektivitas antar container
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# Output: PONG
27
28# Bersihkan container beban kerja dan hentikan sandbox
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID

Bacaan Lanjutan dalam Seri Ini

  1. gvx: Menjalankan Linux Container, Docker, dan Kubernetes dalam Satu Proses Unprivileged: Ikhtisar arsitektur gvx, mesin eksekusi lintas platformnya (whpx, hvf, systrap), VFS2 (erofs, ext4lite, hostfs v2), dan netstack.
  2. Menjalankan Docker, docker build, dan docker compose Tanpa Root atau User Namespace (artikel ini)
  3. Menjalankan Docker-in-Docker di dalam gVisor dan Sandbox Bersarang dengan gvx: Cara gvx menyelesaikan modprobe, iptables-nft, overlay2 pada gofer, dan SECCOMP_FILTER_FLAG_NEW_LISTENER bersarang (EBUSY) di dalam gVisor (runsc).
  4. Lingkungan Docker Terisolasi yang Cepat untuk Rollout RL Agen AI dengan gvx: Penskalaan lingkungan rollout RL dan SWE-bench paralel dengan reset volume .ext4 Copy-on-Write dalam 100 milidetik.