在无 Root 权限且不使用用户命名空间的情况下运行 Docker、docker build 和 docker compose

快速解决方案:三步安全运行无特权 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 或非 root UID 限制)。相反,rungv会锁定其自身的主机进程(PR_SET_NO_NEW_PRIVS+ 严格的 BPFseccomp白名单),并在 Google 内存安全的 gVisor(Sentry) 用户空间内核中运行dockerd。所有访客UID 0能力、overlayfs挂载(ext4lite)以及iptables规则都严格隔离在沙箱进程内部。
1# 1. 在用户空间中安装 rungv(预构建的 SLSA 认证发布版或 go install)
2# 选项 A:验证并解压 GitHub Releases 中 SLSA 认证的预构建二进制文件
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. 在 gvx 中启动 docker:dind,并使用单文件 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
为何标准 Rootless 工具在受限沙箱中失效
当工程师或自主编码智能体需要在强化容器或多租户运行器中运行 docker build、docker run 或 docker compose 时,标准变通方案会在五个不同的内核障碍处失效。
1. 无主机 Docker 守护进程套接字
当主机的 /var/run/docker.sock 未挂载到环境中时,运行 docker ps 或 docker build 会立即失败:
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 辅助程序
强化容器(Kubernetes 中的 securityContext.allowPrivilegeEscalation: false、Docker 中的 --security-opt=no-new-privileges,或 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 用户命名空间(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。 - 由于
PR_SET_NO_NEW_PRIVS处于激活状态,newuidmap无法写入/proc/self/uid_map。
产生的错误信息如下所示:
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. 若无主机 CAP_SYS_ADMIN,mount(2) 被阻止
即使环境允许 CLONE_NEWUSER,如果外层容器屏蔽了 /proc 子树(/proc/acpi、/proc/kcore、/proc/keys)或在 seccomp 中丢弃了 mount(2),则在嵌套挂载命名空间内挂载新的 /proc 也会失败:
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 失效
无法挂载内核 overlayfs 的 Rootless 容器引擎会回退到 fuse-overlayfs。在缺少 /dev/fuse 设备直通的最小化容器或 Kubernetes Pod 中,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. 基于单文件 ext4lite 卷的原生 overlayfs
当您传入 -v gvx-docker:/var/lib/docker(或 --mount type=volume,src=/disk/gvx-docker.ext4,dst=/var/lib/docker)时,gvx 会在 /var/lib/docker 处挂载其内置的 ext4lite 文件系统。
由于 ext4lite 在单个主机文件(gvx-docker.ext4)内实现了带 JBD2 日志的 ext4 文件系统,并支持 trusted.overlay.* 扩展属性、字符型 whiteout 设备(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对,并在gvx的用户空间netstack内通过addrtype和conntrack匹配项配置NAT(DOCKER、POSTROUTING)和FILTER链。 - 传入
-p unix:~/.gvx/docker.sock:2375会绑定一个主机 Unix 域套接字(权限0600),并将连接直接转发至沙箱内dockerd的2375端口。
与现有容器工具的比较
| 能力 / 约束 | 标准 Docker(dockerd) | Rootless Docker / Podman | Kaniko / Buildah(无特权) | gvx(rungv + docker:dind) |
|---|---|---|---|---|
需要主机 root 或 sudo | 是 | 否 | 否 | 否 |
在 PR_SET_NO_NEW_PRIVS=1 下可用 | 否 | 否(newuidmap 失败) | 部分支持 | 是 |
在 unshare(CLONE_NEWUSER) 被阻止(EPERM)时可用 | 不适用 | 否 | Buildah:否 / Kaniko:是 | 是 |
无 /dev/fuse 时可用 | 是(使用内核 overlay2) | 仅通过较慢的 vfs 复制 | 是 | 是(基于 ext4lite 的 overlayfs) |
支持 docker build(BuildKit) | 是 | 是(若允许 userns) | 仅镜像构建(无运行时) | 是 |
支持 docker run 和 docker compose up | 是 | 是(若允许 userns) | 否 | 是 |
| 无需 Linux 虚拟机即可在 Windows 和 macOS 上运行 | 否(需要 WSL2 / 虚拟机) | 否(需要 Linux 虚拟机) | 否 | 是(whpx、hvf、systrap) |
Linux 上确切的主机内核要求
gvx 不需要任何提升的能力(CapEff: 0000000000000000),也不需要任何用户命名空间。在 Linux 主机上,rungv 仅依赖三种标准的无特权内核原语:
- 初始化期间对其自身子线程执行一次
ptrace(PTRACE_ATTACH):systrap在启动时会派生一个本地存根辅助线程,附加到该线程以初始化共享内存上下文,然后分离。如果主机设置了kernel.yama.ptrace_scope = 3(这会在系统范围内禁用所有ptrace),或在外层 seccomp 过滤器中阻止了ptrace,则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# 在 ext4lite 上使用 overlayfs 构建镜像
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。 - 在无 Root 或用户命名空间的情况下运行 Docker、docker build 和 docker compose(本文)
- 在 gVisor 和嵌套沙箱中使用 gvx 运行 Docker-in-Docker:
gvx如何解决gVisor(runsc)内的modprobe、iptables-nft、gofer上的overlay2,以及嵌套的SECCOMP_FILTER_FLAG_NEW_LISTENER(EBUSY)问题。 - 使用 gvx 为 AI 智能体 RL 推演提供快速隔离的 Docker 环境:借助 100 毫秒写时复制
.ext4卷重置,扩展并行的SWE-bench和 RL 推演环境。