Hotdry.
security

Linux沙箱隔离:seccomp、cgroups与namespaces在不信任运行时的实战

针对多租户不信任代理运行时,提供namespaces进程隔离、cgroups资源限额、seccomp-BPF系统调用过滤的Linux沙箱工程参数、实施清单与监控要点。

在多租户或不信任代理(untrusted agent)运行时,如 AI 编码代理或用户上传代码执行,Linux 沙箱是高效的第一道防线。通过结合 namespaces 实现进程视图隔离、cgroups v2 施加资源限额、seccomp-BPF 严格过滤系统调用,可以显著降低内核暴露面,同时保持低开销。相较单纯 Docker,这种原生组合更轻量,适用于高 QPS 场景。本文聚焦单一技术栈的实战落地,给出参数阈值、配置清单与集成步骤,避免内核漏洞横向扩散。

Namespaces:进程与资源视图隔离

Namespaces 是沙箱的基础,提供 “私有视野” 而非硬件边界。核心使用 user、pid、mount、net、uts、ipc namespaces 组合,确保每个 job 独立。

  • User namespace:将沙箱内 uid 0 映射到宿主机非特权用户(如 uid 1000 + 动态分配),防止提权。创建后立即 drop ambient capabilities,禁止额外 uid 映射范围。
  • PID namespace:沙箱内进程树以独立 PID 1 启动,外界不可见。结合 reaper 进程回收僵尸。
  • Mount namespace:私有文件系统,使用 pivot_root 切换到预置最小 rootfs(包含 /bin/lib 等),remount 为 nosuid,nodev,noexec;/tmp、/run 用 tmpfs(大小限 1-10MB)。
  • Network namespace:禁用网络或 veth pair+iptables/eBPF 限出站;不信任代码默认无 net。
  • UTS/IPC:独立 hostname 与 IPC 空间,防信号 / 共享内存泄漏。

实施参数:

  • clone/unshare flags: CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_NEWUTS | CLONE_NEWIPC
  • User map: /proc/PID/uid_map 写 "0 100000 65536"(沙箱 0-> 宿主 100000,范围 65k)
  • Mount: pivot_root (new_root, old_root),后 umount -l old_root

如 Shayon Mukherjee 所述,namespaces 仅为 “visibility walls”,内核 bug 仍可绕过,故需搭配其他层。

Cgroups v2:精细资源限额与 DoS 防护

Cgroups v2 统一 hierarchy,便于多租户分层:顶级 /tenants/tenant1/jobs/job-uuid。

针对 CPU / 内存饥饿、fork bomb,配置如下阈值(per-job,调整依负载):

资源 文件路径 示例值 说明
Memory memory.max 256M 硬限,超阈 OOM-kill cgroup
Memory memory.high 192M 软限,压力前 throttle
CPU cpu.max "50000 100000" 50ms/100ms 周期限 50%
CPU cpu.weight 100 租户相对权重
PIDs pids.max 128 防 fork bomb
IO io.max "8:0 r 10M w 5M" 设备读写 Bps 限
Net net_cls.classid 0x10001 TC 分类整形(可选)

操作清单:

  1. mkdir /sys/fs/cgroup/sandbox/jobs/job-uuid
  2. echo 256M > memory.max; echo 192M > memory.high
  3. echo "50000 100000" > cpu.max
  4. echo $$ > cgroup.procs(attach 进程)
  5. 监控:cgroup.events (oom, max)触发清理

多租户下,预创 /tenants/tenantX,动态子 cgroup。结合 rlimits(如 RLIMIT_NPROC=100)双保险。

Seccomp-BPF:系统调用 Allowlist 防火墙

Seccomp 是纳秒级 syscall 过滤器,从 SCMP_ACT_KILL 默认拒,逐加必要调用。针对 Python/Node 等,测试 workload 记录 strace,精简至 50-100 syscall。

最小核心 allowlist(C-like 二进制):

  • read, write, close, mmap, munmap, brk, exit, exit_group, rt_sigaction, rt_sigreturn, clock_gettime, futex
  • openat/at_fstat(限 dirfd & O_RDONLY)

高级:arg 过滤,如 openat flags & O_WRONLY==0; deny clone3 (CLONE_NEW*)=nested ns。

libseccomp 示例(C):

#include <seccomp.h>
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
// ... 30+ calls
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(ptrace), 0); // 示例deny
seccomp_load(ctx);
execl("/usr/bin/python3", "python3", script, NULL);

加载时机:namespaces+priv drop 后、exec 前。per-runtime profile(python.json 等),audit 日志 /sys/kernel/debug/tracing/seccomp。

Deny 高危:ptrace, kexec_load, init_module, bpf, io_uring_setup, mount(post-setup)。

集成实施:Job 生命周期清单

小主管进程(Go/C/Rust)处理:

  1. 接收 job(code, lang, timeout=30s)
  2. 动态 uid/cgroup/uuid 分配
  3. cgroup 创建 & 限额写
  4. fork child
  5. Child: unshare namespaces → uid_map/setgroups → pivot_root/tmpfs → cap drop/PR_SET_NO_NEW_PRIVS → seccomp_load → exec
  6. Parent: wait cgroup 事件 /timeout,kill -9,rm cgroup/mounts

伪代码(bash 简化,prod 用 native):

#!/bin/bash
CG=/sys/fs/cgroup/sandbox/job$$
mkdir $CG; echo 256M > $CG/memory.max; ...
unshare -U -p -m -n -u -i --map-root-user -- bash -c "
mount --bind /minimal/root /; pivot_root / /proc/1/fd/0; # setup fs
echo 0 > /proc/self/sessionid; # no new privs
# seccomp via bpftool or ld.so preload
exec /bin/python3 $1"
echo $! > $CG/cgroup.procs
wait; rmdir $CG

监控要点:

  • cgroupfs 通知:inotify on cgroup.events
  • Seccomp: tracepoint seccomp:ret
  • Prometheus: cgroup stats (memory.current, cpu.stat)
  • 回滚:job fail>5%? 紧缩 memory.high 20%,或 fallback gVisor

权衡、风险与增强

开销:namespaces<1ms,cgroups/seccomp0 开销。1000QPS 可,vs Docker10x 慢。

风险:

  1. Allowed syscall vuln(如 CVE in read),限 seccomp bypass 罕见。
  2. Config 漏:writable host mount 或宽松 uid map。

增强:gVisor(user-kernel,syscall~60),内仍用以上;或 Firecracker microVM(VM 边界,冷启动 <250ms snapshot)。“namespaces 仅 visibility walls,非安全边界”。

生产如 nsjail/GitHub Awesome-sandbox 验证此栈。

资料来源: [1] https://www.shayon.dev/post/2026/52/lets-discuss-sandbox-isolation/ “namespaces are visibility walls” [2] https://github.com/google/nsjail

(正文字数:约 1250)

查看归档