第 3 章 - 基本用法
第 3 章:基本用法
本章介绍 Bubblewrap 的命令行语法、核心参数、命名空间基础用法和文件系统隔离的基本模式。掌握本章内容后,你就能创建实用的沙箱环境。
3.1 命令行语法
基本格式
bwrap [选项...] -- <命令> [参数...]
所有 bwrap 选项必须出现在 -- 之前,-- 之后是要在沙箱内执行的命令及其参数。
参数顺序
Bubblewrap 的参数是有序的,按从左到右的顺序依次执行挂载和设置操作。这一点非常重要:
# 正确:先绑定只读根文件系统,再覆盖 /tmp
bwrap --ro-bind / / --tmpfs /tmp -- bash
# 错误的思路:参数顺序会影响最终的文件系统布局
# bwrap --tmpfs /tmp --ro-bind / / -- bash
# ↑ /tmp 会被 ro-bind / 覆盖,tmpfs 白设了
3.2 核心参数速查
命名空间参数
| 参数 | 作用 | 等效缩写 |
|---|---|---|
--unshare-user | 创建新的用户命名空间 | — |
--unshare-pid | 创建新的 PID 命名空间 | — |
--unshare-net | 创建新的网络命名空间 | — |
--unshare-uts | 创建新的 UTS 命名空间 | — |
--unshare-cgroup | 创建新的 cgroup 命名空间 | — |
--unshare-ipc | 创建新的 IPC 命名空间 | — |
--unshare-all | 分离所有命名空间 | — |
文件系统参数
| 参数 | 作用 | 示例 |
|---|---|---|
--ro-bind SRC DST | 只读绑定挂载 | --ro-bind /usr /usr |
--bind SRC DST | 读写绑定挂载 | --bind ~/data /data |
--dev DST | 创建最小 dev 文件系统 | --dev /dev |
--proc DST | 挂载 proc 文件系统 | --proc /proc |
--tmpfs DST | 创建 tmpfs | --tmpfs /tmp |
--mqueue DST | 创建 mqueue 文件系统 | --mqueue /dev/mqueue |
--dir DST | 创建目录 | --dir /tmp/mydir |
--file FD DST | 从文件描述符创建文件 | --file 0 /etc/config |
--symlink SRC DST | 创建符号链接 | --symlink /usr/lib /lib |
用户/权限参数
| 参数 | 作用 |
|---|---|
--uid UID | 设置沙箱内的用户 ID |
--gid GID | 设置沙箱内的组 ID |
--hostname NAME | 设置沙箱内的主机名 |
--chdir DIR | 设置沙箱内的工作目录 |
--setenv KEY VALUE | 设置环境变量 |
--unsetenv KEY | 删除环境变量 |
--clearenv | 清除所有环境变量 |
资源限制参数
| 参数 | 作用 |
|---|---|
--size SIZE | 设置 tmpfs 大小(如 --size 256,单位 MB) |
--remount-ro DEST | 重新以只读方式挂载 |
--die-with-parent | 父进程退出时沙箱也退出 |
--new-session | 创建新的会话(session) |
--cap-add CAP | 添加权能 |
--cap-drop CAP | 删除权能 |
3.3 第一个沙箱
最小化沙箱
# 最简单的沙箱——什么都不隔离,只是用 bwrap 启动一个 bash
bwrap --ro-bind / / -- bash
# 在沙箱内,一切看起来和宿主一样(因为只读绑定了宿主的整个文件系统)
ls /
cat /etc/hostname
whoami
带隔离的沙箱
# 一个有基本隔离的沙箱
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
--unshare-pid \
--unshare-uts \
--hostname sandbox \
--chdir /home \
bash
在沙箱内验证隔离:
# PID 隔离——只看到自己的进程
ps aux
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# 1000 1 0.0 0.0 ... ... ? S ... 0:00 bash
# 1000 2 0.0 0.0 ... ... ? R+ ... 0:00 ps aux
# UTS 隔离——独立的主机名
hostname
# sandbox
# tmpfs 隔离——/tmp 是空的
ls /tmp
# (空)
# 写入测试
echo "hello from sandbox" > /tmp/test.txt
cat /tmp/test.txt
# hello from sandbox
exit
退出沙箱后,宿主的 /tmp 中不会有 test.txt——因为沙箱的 /tmp 是内存中的 tmpfs。
3.4 文件系统隔离基础
挂载点参数详解
只读绑定(--ro-bind)
将宿主的文件或目录以只读方式映射到沙箱中。沙箱进程无法修改这些文件:
bwrap \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--ro-bind /bin /bin \
--ro-bind /etc /etc \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
bash
读写绑定(--bind)
将宿主的目录以读写方式映射到沙箱中。沙箱进程可以修改这些文件,修改会反映到宿主:
# 将宿主的 ~/projects 绑定到沙箱的 /projects(读写)
bwrap \
--ro-bind / / \
--bind ~/projects /projects \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
bash
# 在沙箱内
ls /projects
# 你的项目文件...
echo "sandbox edit" > /projects/test.txt
exit
# 在宿主检查
cat ~/projects/test.txt
# sandbox edit
tmpfs
创建一个内存中的临时文件系统。数据在沙箱退出后消失,大小默认受限于系统内存:
bwrap \
--ro-bind / / \
--tmpfs /tmp \
--tmpfs /var/tmp \
--tmpfs /run \
--tmpfs /home/user/.cache \
bash
# 查看 tmpfs 信息
df -h /tmp
# Filesystem Size Used Avail Use% Mounted on
# tmpfs 16G 0 16G 0% /tmp
指定 tmpfs 大小:
# 限制 /tmp 大小为 256 MB
bwrap --ro-bind / / --tmpfs /tmp --size 256 -- bash
dev 文件系统
--dev /dev 创建一个最小的 dev 文件系统,只包含必要的设备文件:
bwrap --ro-bind / / --dev /dev -- bash
ls -la /dev/
# total 0
# drwxr-xr-x 2 root root 80 ...
# crw-rw-rw- 1 root root 1, 3 ... null
# crw-rw-rw- 1 root root 1, 5 ... zero
# crw-rw-rw- 1 root root 1, 7 ... full
# crw-rw-rw- 1 root root 1, 8 ... random
# crw-rw-rw- 1 root root 1, 9 ... urandom
# lrwxrwxrwx 1 root root 0 ... fd -> /proc/self/fd
# crw-rw-rw- 1 root root 5, 0 ... ptmx
# drwxr-xr-x 2 root root 0 ... pts
# drwxr-xr-x 2 root root 0 ... shm
# lrwxrwxrwx 1 root root 0 ... stdin -> /proc/self/fd/0
# lrwxrwxrwx 1 root root 0 ... stdout -> /proc/self/fd/1
# lrwxrwxrwx 1 root root 0 ... stderr -> /proc/self/fd/2
proc 文件系统
--proc /proc 为沙箱的 PID 命名空间挂载 proc 文件系统:
bwrap \
--ro-bind / / \
--unshare-pid \
--proc /proc \
--dev /dev \
bash
# /proc 只显示沙箱内的进程
ls /proc/ | head -5
# 1
# bus
# cgroups
# cmdline
3.5 实用沙箱示例
示例 1:只读文件浏览器
# 一个只能读不能写的文件浏览器
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
--unshare-pid \
--unshare-uts \
--hostname readonly-sandbox \
bash
示例 2:代码编译沙箱
# 在隔离环境中编译 C 代码
# 源码目录可写,其余只读
cat > /tmp/hello.c << 'EOF'
#include <stdio.h>
int main() {
printf("Hello from sandbox!\n");
return 0;
}
EOF
bwrap \
--ro-bind / / \
--bind /tmp /tmp \
--dev /dev \
--proc /proc \
--unshare-pid \
bash -c 'gcc /tmp/hello.c -o /tmp/hello && /tmp/hello'
# Hello from sandbox!
示例 3:不可信脚本执行
# 安全地执行不可信的 shell 脚本
cat > /tmp/untrusted.sh << 'EOF'
#!/bin/bash
echo "I'm running in a sandbox!"
echo "My PID is $$"
echo "I can see these files in /tmp:"
ls /tmp
# 即使脚本尝试恶意操作,也会被限制
rm -rf / 2>/dev/null || echo "rm / failed (expected)"
EOF
chmod +x /tmp/untrusted.sh
bwrap \
--ro-bind / / \
--tmpfs /tmp \
--unshare-all \
--proc /proc \
--dev /dev \
--die-with-parent \
bash /tmp/untrusted.sh
示例 4:网络工具隔离
# 允许网络访问但隔离文件系统
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
--unshare-pid \
curl -s https://example.com | head -5
示例 5:数据处理沙箱
# 在沙箱中处理敏感数据
mkdir -p /tmp/sandbox-data
# 准备数据
echo "sensitive data line 1" > /tmp/sandbox-data/input.txt
echo "sensitive data line 2" >> /tmp/sandbox-data/input.txt
# 在沙箱中处理
bwrap \
--ro-bind / / \
--bind /tmp/sandbox-data /data \
--tmpfs /tmp \
--dev /dev \
--proc /proc \
bash -c '
wc -l /data/input.txt
sort /data/input.txt > /tmp/sorted.txt
cat /tmp/sorted.txt
'
# 清理
rm -rf /tmp/sandbox-data
3.6 环境变量控制
保持宿主环境变量
# 默认情况下,bwrap 会继承宿主的环境变量
bwrap --ro-bind / / -- bash -c 'echo $HOME $USER $PATH'
# /home/user user /usr/local/bin:/usr/bin:/bin
清除并自定义环境变量
# 清除所有环境变量,只设置需要的
bwrap \
--ro-bind / / \
--clearenv \
--setenv HOME /home/sandbox \
--setenv USER sandbox \
--setenv PATH /usr/bin:/bin \
--setenv LANG en_US.UTF-8 \
--tmpfs /home/sandbox \
--chdir /home/sandbox \
bash
# 在沙箱内
env
# HOME=/home/sandbox
# USER=sandbox
# PATH=/usr/bin:/bin
# LANG=en_US.UTF-8
# PWD=/home/sandbox
隔离敏感环境变量
# 防止敏感环境变量泄露到沙箱
bwrap \
--ro-bind / / \
--unsetenv AWS_SECRET_ACCESS_KEY \
--unsetenv DB_PASSWORD \
--unsetenv API_TOKEN \
--tmpfs /tmp \
bash -c 'echo "AWS key: $AWS_SECRET_ACCESS_KEY"'
# AWS key:
3.7 主机名隔离
# 设置沙箱内的主机名
bwrap \
--ro-bind / / \
--unshare-uts \
--hostname "my-sandbox" \
bash -c 'hostname'
# my-sandbox
# 验证宿主的主机名未受影响
hostname
# your-real-hostname
💡 注意:
--hostname隐含了--unshare-uts,可以不显式指定--unshare-uts。
3.8 工作目录设置
# 设置沙箱启动时的工作目录
bwrap \
--ro-bind / / \
--tmpfs /tmp/workspace \
--chdir /tmp/workspace \
bash -c 'pwd'
# /tmp/workspace
# 如果指定的目录不存在,bwrap 会报错
bwrap --ro-bind / / --chdir /nonexistent -- true
# bwrap: Can't chdir to /nonexistent: No such file or directory
3.9 组合使用模式
通用隔离模式
一个实用的通用沙箱模板:
# 通用隔离沙箱
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
--tmpfs /var/tmp \
--tmpfs /run \
--unshare-pid \
--unshare-uts \
--hostname sandbox \
--die-with-parent \
--new-session \
"$@"
保存为脚本:
#!/bin/bash
# sandbox.sh - 通用沙箱启动脚本
exec bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
--unshare-pid \
--unshare-uts \
--hostname sandbox \
--die-with-parent \
--new-session \
"$@"
使用:
# 在沙箱中运行任意命令
./sandbox.sh bash
./sandbox.sh python3
./sandbox.sh vim /tmp/test.txt
./sandbox.sh gcc -o /tmp/prog source.c
3.10 参数顺序陷阱
这是 Bubblewrap 最容易踩的坑之一。参数是按顺序处理的,后面的操作会覆盖前面的操作:
# ❌ 错误:ro-bind / 会覆盖前面的 tmpfs /tmp
bwrap --tmpfs /tmp --ro-bind / / -- bash
# /tmp 现在是宿主 /tmp 的只读镜像
# ✅ 正确:先设置基础文件系统,再覆盖特定挂载点
bwrap --ro-bind / / --tmpfs /tmp -- bash
# /tmp 是 tmpfs
参数处理流程图
参数从左到右依次处理:
--ro-bind / / → 根文件系统 = 宿主的只读镜像
↓
--dev /dev → /dev = 最小设备文件系统
↓
--proc /proc → /proc = 沙箱 PID 的 proc
↓
--tmpfs /tmp → /tmp = 空的 tmpfs(覆盖了宿主的 /tmp)
↓
--bind ~/data /data → /data = 宿主 ~/data 的读写绑定
↓
--dir /tmp/mydir → 在 tmpfs 中创建 /tmp/mydir
↓
执行用户命令
3.11 注意事项
⚠️ 重要提醒
参数顺序很重要:后面的挂载操作会覆盖前面的。始终先设置基础文件系统,再覆盖特定目录。
--ro-bind / /是常见起点:大多数沙箱以这个参数开始,然后在此基础上添加或覆盖。不要忘记
--dev和--proc:很多程序需要/dev/null、/dev/urandom和/proc。没有这些,程序可能会出错。--die-with-parent推荐加上:防止沙箱进程在父进程退出后变成孤儿进程。--new-session推荐加上:防止沙箱进程通过终端信号影响宿主。读写绑定的文件会写回宿主:
--bind的修改是真实的,不是隔离的。如果需要写入隔离,使用 tmpfs。
3.12 扩展阅读
上一章:第 2 章 - 安装与环境配置 | 下一章:第 4 章 - 命名空间详解