第 7 章 - 安全策略
第 7 章:安全策略
本章深入讲解 Bubblewrap 的安全机制,包括 seccomp 系统调用过滤、Linux 权能(Capabilities)限制、资源限制(cgroups)以及与 SELinux/AppArmor 的协同使用。
7.1 安全模型概览
Bubblewrap 采用纵深防御(Defense in Depth)策略,通过多层安全机制限制沙箱进程:
安全层次:
┌─────────────────────────────────────────────┐
│ 第 1 层: 命名空间隔离 │
│ 文件系统、进程、网络、用户、主机名、IPC、cgroup │
├─────────────────────────────────────────────┤
│ 第 2 层: 权能限制(Capabilities) │
│ 移除沙箱进程的所有特权能力 │
├─────────────────────────────────────────────┤
│ 第 3 层: seccomp 系统调用过滤 │
│ 阻止危险的系统调用 │
├─────────────────────────────────────────────┤
│ 第 4 层: 资源限制(cgroups) │
│ CPU、内存、进程数限制 │
├─────────────────────────────────────────────┤
│ 第 5 层: MAC 策略(SELinux / AppArmor) │
│ 强制访问控制 │
└─────────────────────────────────────────────┘
7.2 Linux 权能(Capabilities)
什么是权能
Linux 权能机制将传统的 root 超级权限拆分为细粒度的能力单元。进程只需要被授予其所需的特定权能,而不是完整的 root 权限。
常用权能列表
| 权能 | 值 | 说明 | 沙箱默认状态 |
|---|---|---|---|
CAP_CHOWN | 0 | 改变文件所有者 | ❌ 无 |
CAP_DAC_OVERRIDE | 1 | 绕过文件读/写/执行权限 | ❌ 无 |
CAP_FSETID | 4 | 设置文件的 setuid/setgid 位 | ❌ 无 |
CAP_KILL | 5 | 发送信号到任意进程 | ❌ 无 |
CAP_SETGID | 6 | 设置组 ID | ❌ 无 |
CAP_SETUID | 7 | 设置用户 ID | ❌ 无 |
CAP_NET_BIND_SERVICE | 10 | 绑定 1024 以下端口 | ❌ 无 |
CAP_NET_ADMIN | 12 | 网络管理 | ❌ 无 |
CAP_NET_RAW | 13 | 使用原始套接字 | ❌ 无 |
CAP_SYS_ADMIN | 21 | 大量管理操作 | ❌ 无 |
CAP_SYS_PTRACE | 19 | 跟踪进程 | ❌ 无 |
CAP_SYS_MODULE | 16 | 加载内核模块 | ❌ 无 |
CAP_SYS_RAWIO | 17 | 原始 I/O 操作 | ❌ 无 |
CAP_MKNOD | 27 | 创建设备文件 | ❌ 无 |
CAP_AUDIT_WRITE | 29 | 写入审计日志 | ❌ 无 |
CAP_SYS_CHROOT | 18 | 使用 chroot | ❌ 无 |
CAP_SYS_NICE | 23 | 改变进程优先级 | ❌ 无 |
在 bwrap 中使用权能
# 查看当前进程的权能
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
bash -c 'cat /proc/self/status | grep Cap'
# CapInh: 0000000000000000
# CapPrm: 0000000000000000
# CapEff: 0000000000000000
# CapBnd: 0000000000000000
# CapAmb: 0000000000000000
# 所有值为 0,表示沙箱进程没有任何权能
添加特定权能
# 添加 CAP_NET_BIND_SERVICE,允许绑定低端口
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
--cap-add CAP_NET_BIND_SERVICE \
bash -c 'cat /proc/self/status | grep CapEff'
# CapEff: 0000000000000400
# 解码权能
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--cap-add CAP_NET_BIND_SERVICE \
bash -c '
capsh --decode=0000000000000400 2>/dev/null || \
python3 -c "print(f\"{0x0000000000000400:064b}\")"
'
删除特定权能
# 即使在 user namespace 内映射为 root,也要显式删除权能
bwrap \
--ro-bind / / \
--unshare-user \
--uid 0 \
--dev /dev \
--proc /proc \
--cap-drop ALL \
bash -c 'cat /proc/self/status | grep CapEff'
# CapEff: 0000000000000000
权能安全矩阵
| 操作 | 所需权能 | 默认可执行 |
|---|---|---|
| 改变文件所有者 | CAP_CHOWN | ❌ |
| 绑定低端口 | CAP_NET_BIND_SERVICE | ❌ |
| 发送信号到其他进程 | CAP_KILL | ❌ |
| 挂载文件系统 | CAP_SYS_ADMIN | ❌ |
| 修改系统时间 | CAP_SYS_TIME | ❌ |
| 加载内核模块 | CAP_SYS_MODULE | ❌ |
| 改变进程优先级 | CAP_SYS_NICE | ❌ |
| 创建设备节点 | CAP_MKNOD | ❌ |
| 配置网络 | CAP_NET_ADMIN | ❌ |
| 使用原始套接字 | CAP_NET_RAW | ❌ |
7.3 seccomp 系统调用过滤
什么是 seccomp
seccomp(Secure Computing Mode)是 Linux 内核提供的系统调用过滤机制。seccomp-BPF 允许使用 BPF(Berkeley Packet Filter)程序定义细粒度的过滤规则。
系统调用分类
| 类别 | 系统调用 | 风险等级 | 沙箱策略 |
|---|---|---|---|
| 文件操作 | read, write, open, close, stat | 低 | ✅ 允许 |
| 内存管理 | mmap, munmap, brk, mprotect | 低 | ✅ 允许 |
| 进程管理 | fork, execve, exit, waitpid | 中 | ✅ 允许 |
| 网络 | socket, connect, bind, listen | 中 | ⚠️ 按需 |
| 文件系统 | mount, umount2, pivot_root | 高 | ❌ 禁止 |
| 模块 | init_module, finit_module, delete_module | 极高 | ❌ 禁止 |
| 系统管理 | reboot, kexec_load | 极高 | ❌ 禁止 |
| 调试 | ptrace, process_vm_readv | 高 | ❌ 禁止 |
使用 seccomp 过滤文件
创建 seccomp 过滤规则文件,然后传递给 bwrap:
# 安装 seccomp 工具
# Fedora: sudo dnf install libseccomp-devel
# Debian: sudo apt install libseccomp-dev
# 创建一个简单的 seccomp 过滤规则
# 使用 JSON 格式(需要 bwrap 编译时支持 libseccomp)
cat > /tmp/seccomp.json << 'EOF'
{
"defaultAction": "SCMP_ACT_ALLOW",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": ["mount", "umount2", "pivot_root", "chroot"],
"action": "SCMP_ACT_ERRNO"
},
{
"names": ["reboot", "kexec_load", "kexec_file_load"],
"action": "SCMP_ACT_ERRNO"
},
{
"names": ["init_module", "finit_module", "delete_module"],
"action": "SCMP_ACT_ERRNO"
},
{
"names": ["ptrace", "process_vm_readv", "process_vm_writev"],
"action": "SCMP_ACT_ERRNO"
},
{
"names": ["ioperm", "iopl"],
"action": "SCMP_ACT_ERRNO"
}
]
}
EOF
⚠️ 注意:Bubblewrap 的 seccomp 支持取决于编译选项。通过
--seccomp参数传递过滤规则,但具体用法可能因版本而异。
seccomp 策略模板
# 策略 1: 白名单模式(最安全)
# 只允许已知安全的系统调用
cat > /tmp/seccomp-whitelist.json << 'EOF'
{
"defaultAction": "SCMP_ACT_ERRNO",
"syscalls": [
{
"names": [
"read", "write", "open", "close", "stat", "fstat", "lstat",
"poll", "lseek", "mmap", "mprotect", "munmap", "brk",
"ioctl", "access", "pipe", "select", "sched_yield",
"mremap", "msync", "mincore", "madvise",
"dup", "dup2", "nanosleep", "getpid", "clone",
"fork", "execve", "exit", "wait4", "kill",
"uname", "fcntl", "flock", "fsync", "fdatasync",
"getcwd", "chdir", "mkdir", "rmdir", "link", "unlink",
"readlink", "chmod", "chown", "lchown", "umask",
"gettimeofday", "getuid", "getgid", "geteuid", "getegid",
"getppid", "getpgrp", "setsid", "setreuid", "setregid",
"getgroups", "setgroups", "setresuid", "setresgid",
"getpgid", "setpgid", "getsid", "sigaltstack",
"rt_sigaction", "rt_sigprocmask", "rt_sigreturn",
"set_tid_address", "futex", "epoll_create", "epoll_wait",
"epoll_ctl", "clock_gettime", "clock_getres",
"exit_group", "openat", "mkdirat", "newfstatat",
"unlinkat", "renameat", "readlinkat", "fchmodat",
"faccessat", "pselect6", "utimensat", "epoll_create1",
"pipe2", "dup3", "pread64", "pwrite64", "readv", "writev",
"sendfile", "signalfd4", "epoll_pwait", "timerfd_create",
"eventfd2", "accept4", "epoll_ctl", "clock_nanosleep",
"getrandom", "memfd_create"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
EOF
# 策略 2: 黑名单模式(更实用)
# 允许大部分操作,只禁止危险的系统调用
cat > /tmp/seccomp-blacklist.json << 'EOF'
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": [
"mount", "umount2", "pivot_root", "chroot",
"reboot", "kexec_load", "kexec_file_load",
"init_module", "finit_module", "delete_module",
"ptrace", "process_vm_readv", "process_vm_writev",
"ioperm", "iopl", "swapon", "swapoff",
"acct", "settimeofday", "clock_settime",
"nfsservctl", "quotactl", "lookup_dcookie",
"perf_event_open", "bpf", "userfaultfd"
],
"action": "SCMP_ACT_ERRNO"
}
]
}
EOF
验证 seccomp 过滤
# 验证危险系统调用被阻止
bwrap \
--ro-bind / / \
--unshare-all \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
bash -c '
# 尝试 mount 系统调用
mount -t tmpfs tmpfs /tmp/test 2>&1
# mount: /tmp/test: permission denied.
# 尝试 reboot
reboot 2>&1 || echo "reboot 被阻止"
# 尝试加载内核模块
modprobe dummy 2>&1 || echo "modprobe 被阻止"
'
7.4 资源限制(cgroups)
资源限制类型
| 资源 | 限制方式 | 用途 |
|---|---|---|
| CPU | CPU 时间配额 | 防止 CPU 耗尽 |
| 内存 | 最大内存使用量 | 防止 OOM |
| 进程数 | 最大进程/线程数 | 防止 fork 炸弹 |
| I/O | 磁盘读写带宽 | 防止 I/O 耗尽 |
| 文件描述符 | 最大打开文件数 | 防止 fd 耗尽 |
Bubblewrap 中的资源限制
Bubblewrap 本身不直接提供 cgroup 资源限制参数,但可以通过以下方式实现:
方法 1: 使用 ulimit
# 使用 ulimit 限制资源(bash 内置)
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
bash -c '
# 限制进程数
ulimit -u 50
# 限制文件大小
ulimit -f 10240 # 10MB
# 限制打开文件数
ulimit -n 256
# 限制虚拟内存
ulimit -v 524288 # 512MB
# 验证限制
ulimit -a
'
方法 2: 外部 cgroup 配置
#!/bin/bash
# 使用 systemd-run 为 bwrap 创建 cgroup
# 使用 systemd-run 限制资源
systemd-run --user --scope \
-p MemoryMax=512M \
-p CPUQuota=50% \
-p TasksMax=100 \
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
bash
# 或者使用 cgroup v2 手动配置
# echo "+memory +cpu" > /sys/fs/cgroup/cgroup.subtree_control
# mkdir /sys/fs/cgroup/sandbox
# echo 536870912 > /sys/fs/cgroup/sandbox/memory.max # 512MB
# echo 50000 100000 > /sys/fs/cgroup/sandbox/cpu.max # 50% CPU
# echo $$ > /sys/fs/cgroup/sandbox/cgroup.procs
方法 3: 使用 –die-with-parent 防止资源泄漏
# 确保父进程退出时沙箱也退出
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
--die-with-parent \
bash -c 'echo "I will die when my parent exits"'
防止 Fork 炸弹
# 限制进程数,防止 fork 炸弹
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
bash -c '
ulimit -u 20 # 最多 20 个进程
# fork 炸弹测试(安全,因为有进程数限制)
:(){ :|:& };: 2>&1 || echo "Fork bomb limited by ulimit"
'
7.5 SELinux 集成
SELinux 概述
SELinux(Security-Enhanced Linux)是一个强制访问控制(MAC)系统,为进程和文件提供细粒度的安全标签。
Bubblewrap 与 SELinux 的交互
# 检查 SELinux 状态
getenforce
# Enforcing
# 查看 bwrap 进程的 SELinux 上下文
bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
bash -c 'id -Z'
# unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
为 bwrap 创建 SELinux 策略
# 创建自定义 SELinux 策略模块
cat > bwrap_sandbox.te << 'EOF'
policy_module(bwrap_sandbox, 1.0)
# 定义沙箱域
type sandbox_t;
type sandbox_exec_t;
domain_type(sandbox_t)
domain_entry_file(sandbox_t, sandbox_exec_t)
# 允许沙箱域访问 tmpfs
allow sandbox_t tmpfs_t:file { read write create };
allow sandbox_t tmpfs_t:dir { read write create add_name };
# 允许沙箱域访问 proc
allow sandbox_t proc_t:file read;
# 允许沙箱域访问 dev
allow sandbox_t device_t:chr_file { read write };
# 不允许沙箱域访问网络(可选)
# neverallow sandbox_t self:tcp_socket { create connect };
EOF
# 编译和加载策略
# checkmodule -M -m -o bwrap_sandbox.mod bwrap_sandbox.te
# semodule_package -o bwrap_sandbox.pp -m bwrap_sandbox.mod
# semodule -i bwrap_sandbox.pp
SELinux 布尔值
# 查看与容器/沙箱相关的 SELinux 布尔值
getsebool -a | grep -E "(container|sandbox|bwrap)"
# container_manage_cgroup --> off
# container_connect_any --> off
# 临时设置布尔值
setsebool container_manage_cgroup on
# 永久设置
setsebool -P container_manage_cgroup on
7.6 AppArmor 集成
AppArmor 概述
AppArmor 是另一个 Linux 安全模块,基于路径的访问控制。Ubuntu 和 SUSE 默认使用 AppArmor。
查看 AppArmor 策略
# 检查 AppArmor 状态
sudo aa-status
# 查看 bwrap 相关的策略
sudo aa-status | grep -i bwrap
# 查看具体的策略文件
cat /etc/apparmor.d/bwrap 2>/dev/null || echo "No AppArmor profile for bwrap"
创建 AppArmor 策略
# 创建 bwrap 沙箱的 AppArmor 策略
cat > /tmp/apparmor-bwrap-sandbox << 'EOF'
#include <tunables/global>
profile bwrap-sandbox flags=(attach_disconnected) {
#include <abstractions/base>
#include <abstractions/nameservice>
# 允许读取系统文件
/usr/** r,
/lib/** r,
/lib64/** r,
/bin/** r,
/sbin/** r,
/etc/** r,
# 允许访问 /dev 中的必要文件
/dev/null rw,
/dev/zero rw,
/dev/full rw,
/dev/random r,
/dev/urandom r,
/dev/tty rw,
/dev/pts/** rw,
# 允许访问 /proc
/proc/** r,
/proc/self/** rw,
# 允许 tmpfs 操作
/tmp/** rw,
# 禁止访问敏感文件
deny /etc/shadow r,
deny /etc/gshadow r,
deny /proc/kallsyms r,
deny /proc/kcore r,
# 禁止网络操作(可选)
deny network,
# 禁止挂载操作
deny mount,
deny umount,
}
EOF
# 加载策略
# sudo cp /tmp/apparmor-bwrap-sandbox /etc/apparmor.d/
# sudo apparmor_parser -r /etc/apparmor.d/bwrap-sandbox
7.7 防止信息泄露
隐藏内核信息
# 防止读取内核符号和内存
bwrap \
--ro-bind / / \
--tmpfs /proc \
--ro-bind /proc/cpuinfo /proc/cpuinfo \
--ro-bind /proc/meminfo /proc/meminfo \
--ro-bind /proc/self /proc/self \
--ro-bind /proc/thread-self /proc/thread-self \
--dev /dev \
bash -c '
# 可以读取的基本信息
cat /proc/cpuinfo | head -5
cat /proc/meminfo | head -5
# 无法读取敏感信息
cat /proc/kallsyms 2>&1 || echo "kallsyms 不可访问"
cat /proc/kcore 2>&1 || echo "kcore 不可访问"
'
隐藏用户信息
# 不暴露其他用户的信息
bwrap \
--ro-bind / / \
--tmpfs /var/log \
--tmpfs /var/mail \
--tmpfs /var/spool \
--dev /dev \
--proc /proc \
bash -c '
# lastlog 不可用
lastlog 2>&1 | head -3 || echo "lastlog 不可用"
# who 不显示其他用户
who 2>&1 || echo "who 不可用"
'
7.8 安全加固脚本
生产级安全沙箱脚本
#!/bin/bash
# secure-sandbox.sh - 生产级安全沙箱
# 用法: ./secure-sandbox.sh <命令> [参数...]
set -euo pipefail
# 安全函数
die() { echo "Error: $*" >&2; exit 1; }
# 检查 bwrap
command -v bwrap >/dev/null 2>&1 || die "bwrap not found"
# 执行带安全加固的沙箱
exec bwrap \
--ro-bind / / \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
--tmpfs /var/tmp \
--tmpfs /run \
--tmpfs /home/user/.ssh \
--tmpfs /home/user/.gnupg \
--tmpfs /home/user/.aws \
--tmpfs /home/user/.kube \
--unshare-all \
--hostname sandbox \
--die-with-parent \
--new-session \
--cap-drop ALL \
--clearenv \
--setenv HOME /home/user \
--setenv USER user \
--setenv PATH /usr/local/bin:/usr/bin:/bin \
--setenv LANG "${LANG:-en_US.UTF-8}" \
--setenv TERM "${TERM:-xterm-256color}" \
"$@"
使用示例
# 在安全沙箱中运行 Python
./secure-sandbox.sh python3
# 在安全沙箱中运行 bash
./secure-sandbox.sh bash
# 在安全沙箱中执行脚本
./secure-sandbox.sh bash -c 'echo "Hello from secure sandbox"'
7.9 安全审计
检查沙箱的安全性
#!/bin/bash
# audit-sandbox.sh - 审计沙箱配置的安全性
echo "=== Bubblewrap 沙箱安全审计 ==="
echo ""
# 1. 检查 User Namespace
echo "1. User Namespace 支持:"
if unshare --user --map-root-user echo "OK" &>/dev/null; then
echo " ✅ 支持(推荐使用 user namespace 模式)"
else
echo " ❌ 不支持(可能使用 setuid 模式,安全性较低)"
fi
# 2. 检查 bwrap 是否为 setuid
echo ""
echo "2. bwrap setuid 状态:"
BWRAP_PATH=$(which bwrap)
BWRAP_PERMS=$(stat -c '%a' "$BWRAP_PATH")
if [[ "$BWRAP_PERMS" == *"4"* ]]; then
echo " ⚠️ bwrap 是 setuid ($BWRAP_PERMS),存在提权风险"
else
echo " ✅ bwrap 不是 setuid ($BWRAP_PERMS)"
fi
# 3. 检查 seccomp 支持
echo ""
echo "3. seccomp 支持:"
if [ -f /proc/self/status ] && grep -q Seccomp /proc/self/status; then
echo " ✅ 内核支持 seccomp"
else
echo " ❌ 内核不支持 seccomp"
fi
# 4. 检查 SELinux/AppArmor
echo ""
echo "4. MAC 系统:"
if command -v getenforce &>/dev/null; then
SELINUX_STATUS=$(getenforce)
echo " SELinux: $SELINUX_STATUS"
elif command -v aa-status &>/dev/null; then
echo " AppArmor: $(sudo aa-status --enabled 2>/dev/null && echo 'enabled' || echo 'unknown')"
else
echo " 无 MAC 系统"
fi
# 5. 检查内核参数
echo ""
echo "5. 内核参数:"
echo " unprivileged_userns_clone: $(cat /proc/sys/kernel/unprivileged_userns_clone 2>/dev/null || echo 'N/A')"
echo " max_user_namespaces: $(cat /proc/sys/user/max_user_namespaces 2>/dev/null || echo 'N/A')"
echo ""
echo "=== 审计完成 ==="
7.10 安全威胁模型
| 威胁 | 防护措施 | 有效性 |
|---|---|---|
| 文件系统访问 | 只读绑定 + tmpfs | ⭐⭐⭐⭐⭐ |
| 进程间窥探 | PID namespace | ⭐⭐⭐⭐⭐ |
| 网络攻击 | Network namespace | ⭐⭐⭐⭐ |
| 权限提升 | User namespace + cap-drop | ⭐⭐⭐⭐ |
| 内核漏洞利用 | seccomp | ⭐⭐⭐ |
| 侧信道攻击 | 有限保护 | ⭐ |
| 资源耗尽 | ulimit/cgroup | ⭐⭐⭐⭐ |
| 信息泄露 | 隐藏敏感文件 | ⭐⭐⭐⭐ |
7.11 注意事项
⚠️ 重要提醒
seccomp 不能阻止所有攻击:如果内核本身存在漏洞,seccomp 过滤可能被绕过。
setuid bwrap 是安全风险:如果 bwrap 有漏洞,setuid 模式可能导致提权。优先使用 user namespace。
--cap-drop ALL是最佳实践:除非有明确需求,否则移除所有权能。不要在沙箱中运行完全不可信的代码:Bubblewrap 提供的是纵深防御,不是绝对安全边界。高安全需求应使用虚拟机。
审计你的沙箱配置:定期使用安全审计工具检查沙箱配置。
保持内核更新:许多安全修复依赖于最新的内核版本。
MAC 策略可以进一步加固:SELinux/AppArmor 策略可以在命名空间之上提供额外的安全层。
7.12 扩展阅读
- capabilities(7)
- seccomp(2)
- SELinux Project
- AppArmor Wiki
- Linux Security Modules
- Container Security by Liz Rice
上一章:第 6 章 - 网络隔离 | 下一章:第 8 章 - Flatpak 集成