强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Bubblewrap 容器入门教程 / 第 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_CHOWN0改变文件所有者❌ 无
CAP_DAC_OVERRIDE1绕过文件读/写/执行权限❌ 无
CAP_FSETID4设置文件的 setuid/setgid 位❌ 无
CAP_KILL5发送信号到任意进程❌ 无
CAP_SETGID6设置组 ID❌ 无
CAP_SETUID7设置用户 ID❌ 无
CAP_NET_BIND_SERVICE10绑定 1024 以下端口❌ 无
CAP_NET_ADMIN12网络管理❌ 无
CAP_NET_RAW13使用原始套接字❌ 无
CAP_SYS_ADMIN21大量管理操作❌ 无
CAP_SYS_PTRACE19跟踪进程❌ 无
CAP_SYS_MODULE16加载内核模块❌ 无
CAP_SYS_RAWIO17原始 I/O 操作❌ 无
CAP_MKNOD27创建设备文件❌ 无
CAP_AUDIT_WRITE29写入审计日志❌ 无
CAP_SYS_CHROOT18使用 chroot❌ 无
CAP_SYS_NICE23改变进程优先级❌ 无

在 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)

资源限制类型

资源限制方式用途
CPUCPU 时间配额防止 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 注意事项

⚠️ 重要提醒

  1. seccomp 不能阻止所有攻击:如果内核本身存在漏洞,seccomp 过滤可能被绕过。

  2. setuid bwrap 是安全风险:如果 bwrap 有漏洞,setuid 模式可能导致提权。优先使用 user namespace。

  3. --cap-drop ALL 是最佳实践:除非有明确需求,否则移除所有权能。

  4. 不要在沙箱中运行完全不可信的代码:Bubblewrap 提供的是纵深防御,不是绝对安全边界。高安全需求应使用虚拟机。

  5. 审计你的沙箱配置:定期使用安全审计工具检查沙箱配置。

  6. 保持内核更新:许多安全修复依赖于最新的内核版本。

  7. MAC 策略可以进一步加固:SELinux/AppArmor 策略可以在命名空间之上提供额外的安全层。


7.12 扩展阅读


上一章:第 6 章 - 网络隔离 | 下一章:第 8 章 - Flatpak 集成