Запуск Docker, docker build и docker compose без прав root и пространств имён пользователей

Быстрое решение: безопасный непривилегированный Docker в три шага
Если вы работаете внутри ограниченного Linux-контейнера, CI-раннера, Kubernetes Pod или песочницы AI-агента, где заблокированы root, sudo, пользовательские пространства имён (unshare(CLONE_NEWUSER)), mount(2) и /dev/fuse, gvx (rungv) является безопасным решением с эшелонированной защитой для запуска полноценного движка docker:dind в пользовательском пространстве.
Почему
gvxбезопасен по замыслу (это не обходной путь для повышения привилегий): в отличие от монтирования хостового/var/run/docker.sockили запускаdocker run --privileged(которые предоставляют управление ядром хоста с правами, эквивалентными root),gvxне обходит и не ослабляет никакие политики безопасности хоста (PR_SET_NO_NEW_PRIVS,seccomp, AppArmor или ограничения непривилегированного UID). Вместо этогоrungvблокирует собственный процесс на хосте (PR_SET_NO_NEW_PRIVS+ строгий список разрешённых системных вызовов BPFseccomp) и запускаетdockerdвнутри пользовательского ядра gVisor (Sentry) от Google, написанного на безопасном по отношению к памяти языке. Все возможности гостевогоUID 0, монтированияoverlayfs(ext4lite) и правилаiptablesостаются строго изолированными внутри процесса песочницы.
1# 1. Установка rungv в пользовательское пространство (предсобранный релиз с SLSA-аттестацией или go install)
2# Вариант A: проверка и распаковка предсобранного бинарного файла с SLSA-аттестацией из 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# Вариант B: или установка непосредственно из исходного кода с помощью Go
9CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest
10
11# 2. Запуск docker:dind внутри gvx с томом ext4lite в виде одного файла
12# Пробрасывает ~/.gvx/docker.sock (режим 0600) на гостевой порт 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. Направление стандартного Docker CLI и Docker Compose на сокет пользовательского пространства
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# Вывод: overlayfs Alpine Linux ... (containerized)
26
27docker build -t app:test .
28docker run --rm app:test
29docker compose up -d
Почему стандартные инструменты для работы без root не работают в ограниченных песочницах
Когда инженер или автономный агент для написания кода должен выполнить docker build, docker run или docker compose внутри усиленного контейнера или многопользовательского раннера, стандартные обходные пути выходят из строя на пяти различных барьерах ядра.
1. Отсутствие сокета демона Docker на хосте
Команды docker ps или docker build немедленно завершаются ошибкой, если хостовый /var/run/docker.sock не подключён к окружению:
1Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
2. PR_SET_NO_NEW_PRIVS блокирует sudo и вспомогательные программы с setuid
Усиленные контейнеры (securityContext.allowPrivilegeEscalation: false в Kubernetes, --security-opt=no-new-privileges в Docker или песочницы exebox / bwrap) устанавливают бит Linux PR_SET_NO_NEW_PRIVS для дерева процессов. Попытка повысить привилегии через sudo или исполняемые файлы с setuid (newuidmap, newgidmap, fusermount3) завершается ошибкой:
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 и sysctl блокируют unshare(CLONE_NEWUSER)
Инструменты, позиционируемые как «rootless» (Rootless Docker, Podman, Buildah, nerdctl, bubblewrap), по-прежнему опираются на пользовательские пространства имён Linux (user namespaces, CLONE_NEWUSER). Они завершаются ошибкой, когда:
- профиль seccomp по умолчанию внешнего контейнера блокирует
unshare(2)илиclone(CLONE_NEWUSER); - в Ubuntu 24.04 и новее включён параметр
kernel.apparmor_restrict_unprivileged_userns = 1; - на узлах Debian или RHEL установлены
kernel.unprivileged_userns_clone = 0илиuser.max_user_namespaces = 0; newuidmapне может выполнить запись в/proc/self/uid_map, поскольку активенPR_SET_NO_NEW_PRIVS.
Возникающие ошибки выглядят так:
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) заблокирован без CAP_SYS_ADMIN на хосте
Даже если среда разрешает CLONE_NEWUSER, монтирование нового /proc внутри вложенного пространства монтирования завершается ошибкой, если внешний контейнер скрыл поддеревья /proc (/proc/acpi, /proc/kcore, /proc/keys) или исключил mount(2) из 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 ломает fuse-overlayfs
Rootless-движки контейнеров, которые не могут смонтировать ядерный overlayfs, переходят на fuse-overlayfs. В минимальных контейнерах или Kubernetes Pod без проброса устройства /dev/fuse fuse-overlayfs немедленно завершается ошибкой:
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
Как gvx полностью запускает docker:dind в пользовательском пространстве
gvx обходит все пять барьеров ядра, поскольку не просит ядро Linux хоста создавать пространства имён, монтировать файловые системы или настраивать сетевые интерфейсы. Вместо этого rungv запускает внутреннее пользовательское ядро Linux (gVisor Sentry) и пользовательский сетевой стек (netstack) внутри одного обычного непривилегированного процесса.

1. Пространства имён и таблица монтирования в памяти (VFS2)
Когда dockerd, containerd и runc выполняются внутри rungv, systrap перехватывает их системные вызовы и направляет их во внутрипроцессное ядро Sentry, а не в ядро хоста:
unshare(2),clone(2),pivot_root(2),chroot(2)иmount(2)работают с иерархией задачSentryв памяти и таблицей монтированияVFS2(procfs,sysfs,tmpfs,devpts,cgroupfsv2), тогда как хостовый процессrungvвыполняется под вашим непривилегированным UID на хосте.
2. Нативный overlayfs на томах ext4lite в виде одного файла
Когда вы передаёте -v gvx-docker:/var/lib/docker (или --mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker), gvx монтирует встроенную файловую систему ext4lite в /var/lib/docker.
Поскольку ext4lite реализует файловую систему ext4 с журналом JBD2 внутри одного файла хоста (gvx-docker.ext4) с поддержкой расширенных атрибутов trusted.overlay.*, символьных устройств-вайтаутов (0:0), d_type и RENAME_EXCHANGE, dockerd использует собственный драйвер хранения overlayfs внутри гостевой системы. Многоэтапные операции docker build используют общие слои Copy-on-Write без /dev/fuse и без чрезмерного расхода дискового пространства, характерного для резервного драйвера Docker vfs.
3. Пользовательский мост docker0 и iptables-legacy
Когда docker:dind запускается внутри gvx:
- гостевой
dockerdсоздаёт мостdocker0(172.17.0.1/16), подключает виртуальные парыvethдля вложенных контейнеров и настраивает цепочкиNAT(DOCKER,POSTROUTING) иFILTERс сопоставлениямиaddrtypeиconntrackвнутри пользовательскогоnetstackgvx; - параметр
-p unix:~/.gvx/docker.sock:2375привязывает хостовый сокет домена Unix (режим0600) и перенаправляет соединения напрямую на порт2375dockerdвнутри песочницы.
Сравнение с существующими инструментами для контейнеров
| Возможность / Ограничение | Standard Docker (dockerd) | Rootless Docker / Podman | Kaniko / Buildah (Unprivileged) | gvx (rungv + docker:dind) |
|---|---|---|---|---|
Требуется root или sudo на хосте | Да | Нет | Нет | Нет |
Работает с PR_SET_NO_NEW_PRIVS=1 | Нет | Нет (newuidmap завершается ошибкой) | Частично | Да |
Работает, когда unshare(CLONE_NEWUSER) заблокирован (EPERM) | Неприменимо | Нет | Buildah: нет / Kaniko: да | Да |
Работает без /dev/fuse | Да (использует ядерный overlay2) | Только через медленный копирующий vfs | Да | Да (overlayfs на ext4lite) |
Поддерживает docker build (BuildKit) | Да | Да (если разрешён userns) | Только сборка образов (без runtime) | Да |
Поддерживает docker run и docker compose up | Да | Да (если разрешён userns) | Нет | Да |
| Работает на Windows и macOS без виртуальной машины Linux | Нет (требуется WSL2 / ВМ) | Нет (требуется ВМ Linux) | Нет | Да (whpx, hvf, systrap) |
Точные требования к ядру хоста Linux
gvx не требует повышенных возможностей (CapEff: 0000000000000000) и пользовательских пространств имён. На хосте Linux rungv опирается только на три стандартных непривилегированных примитива ядра:
ptrace(PTRACE_ATTACH)на собственный дочерний поток один раз при инициализации:systrapзапускает локальный вспомогательный поток-заглушку при старте, подключается к нему для инициализации контекста разделяемой памяти и отключается. Если хост устанавливаетkernel.yama.ptrace_scope = 3(что отключаетptraceво всей системе) или блокируетptraceво внешнем фильтре seccomp, инициализацияsystrapне может завершиться. (Приyama.ptrace_scope = 1, значении по умолчанию в Ubuntu и Debian,gvxработает, поскольку трассировка от родительского процесса к дочернему разрешена.)seccomp(SECCOMP_SET_MODE_FILTER): используется для перехвата системных вызовов гостевой системы из потоков-заглушек контейнера. Как описано в части 3 этой серии,gvxработает как тогда, когда доступенSECCOMP_FILTER_FLAG_NEW_LISTENER, так и тогда, когда он возвращаетEBUSYво вложенной песочнице.mmap(..., PROT_EXEC, ...): используется для отображения сегментов ELF гостевой системы и трамплина разделяемой памятиsystrap.
Сквозной пример: сборка и тестирование многоконтейнерного приложения без root
Следующий скрипт выполняет полную docker build и интеграционный тест из двух контейнеров (redis + контейнер приложения) внутри непривилегированной пользовательской сессии:
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# Сборка образа с использованием overlayfs на ext4lite
20docker build -t demo-ping:latest .
21
22# Создание сетевого моста, запуск Redis и проверка связи между контейнерами
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# Вывод: PONG
27
28# Очистка рабочих контейнеров и остановка песочницы
29docker rm -f cache
30docker network rm demo-net
31kill -INT $DIND_PID
Дополнительные материалы по этой серии
- gvx: запуск контейнеров Linux, Docker и Kubernetes в одном непривилегированном процессе: архитектурный обзор
gvx, его кроссплатформенных движков выполнения (whpx,hvf,systrap),VFS2(erofs,ext4lite,hostfsv2) иnetstack. - Запуск Docker, docker build и docker compose без root и пользовательских пространств имён (этот пост)
- Запуск Docker-in-Docker внутри gVisor и вложенных песочниц с gvx: как
gvxрешает задачиmodprobe,iptables-nft,overlay2наgoferи вложенногоSECCOMP_FILTER_FLAG_NEW_LISTENER(EBUSY) внутриgVisor(runsc). - Быстрые изолированные среды Docker для развёртываний RL-агентов ИИ с gvx: масштабирование параллельных сред
SWE-benchи RL-развёртываний со сбросом томов.ext4с copy-on-write за 100 миллисекунд.