---
id: 40e9d44f33fa847eb498f09bac5bf144
author: Lemon Mint
title: 在无 Root 权限且不使用用户命名空间的情况下运行 Docker、docker build 和 docker compose
description: 如何在非特权用户身份下，于隔离的 gVisor 用户空间沙箱中安全地运行完整的 Docker Engine（dockerd、BuildKit、overlayfs 及 docker compose），且在 root、sudo、unshare(CLONE_NEWUSER)、mount(2) 和 /dev/fuse 均被阻止的情况下。
language: zh
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
---

## 快速解决方案：三步安全运行无特权 Docker

如果您在受限的 Linux 容器、CI 运行器、Kubernetes Pod 或 AI 智能体沙箱中工作，且其中 `root`、`sudo`、用户命名空间（`unshare(CLONE_NEWUSER)`）、`mount(2)` 和 `/dev/fuse` 均被禁用，那么 **[gvx](https://github.com/gfire-sigs/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` + 严格的 BPF `seccomp` 白名单），并在 Google 内存安全的 **gVisor（`Sentry`）** 用户空间内核中运行 `dockerd`。所有访客 `UID 0` 能力、`overlayfs` 挂载（`ext4lite`）以及 `iptables` 规则都严格隔离在沙箱进程内部。

```sh
# 1. 在用户空间中安装 rungv（预构建的 SLSA 认证发布版或 go install）
# 选项 A：验证并解压 GitHub Releases 中 SLSA 认证的预构建二进制文件
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

# 选项 B：或使用 Go 从源代码直接安装
CGO_ENABLED=0 GOBIN=~/.local/bin go install z.gfire.dev/gvx/cmd/rungv@latest

# 2. 在 gvx 中启动 docker:dind，并使用单文件 ext4lite 卷
#    将 ~/.gvx/docker.sock（权限 0600）转发至访客端口 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. 将标准 Docker CLI 和 Docker Compose 指向用户空间套接字
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}}'
# 输出：overlayfs Alpine Linux ... (containerized)

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

---

## 为何标准 Rootless 工具在受限沙箱中失效

当工程师或自主编码智能体需要在强化容器或多租户运行器中运行 `docker build`、`docker run` 或 `docker compose` 时，标准变通方案会在五个不同的内核障碍处失效。

### 1. 无主机 Docker 守护进程套接字

当主机的 `/var/run/docker.sock` 未挂载到环境中时，运行 `docker ps` 或 `docker build` 会立即失败：

```text
Cannot 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`）提权会失败：

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

产生的错误信息如下所示：

```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. 若无主机 `CAP_SYS_ADMIN`，`mount(2)` 被阻止

即使环境允许 `CLONE_NEWUSER`，如果外层容器屏蔽了 `/proc` 子树（`/proc/acpi`、`/proc/kcore`、`/proc/keys`）或在 seccomp 中丢弃了 `mount(2)`，则在嵌套挂载命名空间内挂载新的 `/proc` 也会失败：

```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. 缺少 `/dev/fuse` 导致 `fuse-overlayfs` 失效

无法挂载内核 `overlayfs` 的 Rootless 容器引擎会回退到 `fuse-overlayfs`。在缺少 `/dev/fuse` 设备直通的最小化容器或 Kubernetes Pod 中，`fuse-overlayfs` 会立即失败：

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

---

## `gvx` 如何在用户空间中完整运行 `docker:dind`

`gvx` 绕过了全部五个内核障碍，因为它不要求主机 Linux 内核创建命名空间、挂载文件系统或配置网络接口。相反，`rungv` 会在一个普通的无特权进程中运行一个内部用户空间 Linux 内核（gVisor `Sentry`）以及一个用户空间网络栈（`netstack`）。

![使用 gvx 运行无特权 Docker-in-Docker](/assets/images/gvx/gvx-unprivileged-dind.png)

### 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`、`cgroupfs` v2），而主机 `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` 仅依赖三种标准的无特权内核原语：

1. **初始化期间对其自身子线程执行一次 `ptrace(PTRACE_ATTACH)`**：`systrap` 在启动时会派生一个本地存根辅助线程，附加到该线程以初始化共享内存上下文，然后分离。如果主机设置了 `kernel.yama.ptrace_scope = 3`（这会在系统范围内禁用所有 `ptrace`），或在外层 seccomp 过滤器中阻止了 `ptrace`，则 `systrap` 初始化无法完成。（在 `yama.ptrace_scope = 1` 的情况下，即 Ubuntu 和 Debian 的默认设置，`gvx` 可以成功运行，因为允许父进程对子进程进行追踪。）
2. **`seccomp(SECCOMP_SET_MODE_FILTER)`**：用于捕获来自容器存根线程的访客系统调用。正如[本系列第 3 部分](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)所述，`gvx` 在 `SECCOMP_FILTER_FLAG_NEW_LISTENER` 可用时以及在嵌套沙箱内返回 `EBUSY` 时均可工作。
3. **`mmap(..., PROT_EXEC, ...)`**：用于映射访客 ELF 段和 `systrap` 共享内存跳板。

---

## 端到端示例：在无 Root 权限下构建并测试多容器应用

以下脚本在无特权的用户会话中运行一次完整的 `docker build` 以及包含两个容器（`redis` + 应用容器）的集成测试：

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

# 在 ext4lite 上使用 overlayfs 构建镜像
docker build -t demo-ping:latest .

# 创建网桥网络，启动 Redis，并验证容器之间的连通性
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
# 输出：PONG

# 清理工作负载容器并停止沙箱
docker rm -f cache
docker network rm demo-net
kill -INT $DIND_PID
```

---

## 本系列的延伸阅读

1. **[gvx：在一个无特权进程中运行 Linux 容器、Docker 和 Kubernetes](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)**：`gvx` 的架构概述，其跨平台执行引擎（`whpx`、`hvf`、`systrap`），`VFS2`（`erofs`、`ext4lite`、`hostfs` v2），以及 `netstack`。
2. **[在无 Root 或用户命名空间的情况下运行 Docker、docker build 和 docker compose](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)**（本文）
3. **[在 gVisor 和嵌套沙箱中使用 gvx 运行 Docker-in-Docker](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)**：`gvx` 如何解决 `gVisor`（`runsc`）内的 `modprobe`、`iptables-nft`、`gofer` 上的 `overlay2`，以及嵌套的 `SECCOMP_FILTER_FLAG_NEW_LISTENER`（`EBUSY`）问题。
4. **[使用 gvx 为 AI 智能体 RL 推演提供快速隔离的 Docker 环境](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)**：借助 100 毫秒写时复制 `.ext4` 卷重置，扩展并行的 `SWE-bench` 和 RL 推演环境。
