引言:为什么容器安全不只靠 cgroups?

Docker 容器里的进程真的"隔离"了吗?答案远比你想象的复杂。cgroups v2 解决了资源配额管理(CPU、内存、I/O),但容器内的进程依然共享同一个 Linux 内核,理论上可以发起任何系统调用。如果容器内应用被攻破,攻击者可能通过 ptracekexec_loadinit_module 等系统调用对宿主机发起容器逃逸。seccomp(Secure Computing Mode)正是 Linux 内核提供的系统调用过滤机制,它像一道精密的防火墙,只放行容器"真正需要"的系统调用,从根源上缩小容器的攻击面。

一、seccomp 架构演进:从 Mode 1 到 BPF 过滤器

1.1 初代 seccomp:严格模式(Mode 1)

Linux 2.6.12 引入的第一代 seccomp 只允许 readwrite_exitsigreturn 四个无条件系统调用。这太严苛了,几乎无法用于任何真实应用,因而长期无人问津。

1.2 seccomp-bpf:BPF 赋予的灵活性

Linux 3.5(2012年)引入 seccomp-bpf,将经典的 Berkeley Packet Filter 虚拟机引入系统调用过滤领域。与网络 BPF 过滤数据包类似,seccomp-bpf 过滤系统调用,但可以基于系统调用号及六个 64 位参数进行精细的位运算、比较和跳转。返回值可以是 SECCOMP_RET_ALLOWSECCOMP_RET_KILLSECCOMP_RET_TRAPSECCOMP_RET_ERRNOSECCOMP_RET_TRACESECCOMP_RET_LOG,覆盖了放行、终止、信号、返回错误、通知追踪者和仅记录等所有语义。

关键架构洞察:seccomp-bpf 在内核的 __secure_computing() 中,每次系统调用入口都会执行 BPF 程序。BPF 虚拟机保证过滤程序在有限时间内完成(经典 BPF 最大 4096 指令),使系统调用入口的延迟增量控制在百纳秒级。

二、动手编写 seccomp-bpf 过滤程序

2.1 最小可用示例:用 seccomp 禁止 execve

#include 
#include 
#include 
#include 
#include 

void install_seccomp_filter(void) {
    struct sock_filter filter[] = {
        // 加载系统调用号到累加器
        BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
                 offsetof(struct seccomp_data, nr)),
        // 如果是 execve (59 on x86_64),杀死进程
        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 59, 0, 1),
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
        // 其他系统调用放行
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
    };
    struct sock_fprog prog = {
        .len = sizeof(filter) / sizeof(filter[0]),
        .filter = filter,
    };
    prctl(PR_SET_NO_NEWPRIVS, 1, 0, 0, 0);
    prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
}

2.2 基于参数的高级过滤

// 禁止 clone 带 CLONE_NEWNS | CLONE_NEWUSER flag(防止创建新 namespace)
struct sock_filter namespace_filter[] = {
    BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
             offsetof(struct seccomp_data, nr)),
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_clone, 1, 0),
    // 不是 clone,放行
    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
    // 是 clone,检查第二个参数(flags)
    BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
             offsetof(struct seccomp_data, args[0])),
    BPF_STMT(BPF_ALU | BPF_AND | BPF_K,
             CLONE_NEWNS | CLONE_NEWUSER),
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1),
    // 包含敏感 flag,拒绝
    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (EPERM & 0xffff)),
    // 不包含敏感 flag,放行
    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
};

2.3 使用 libseccomp 安全地构建过滤规则

#include 

void build_docker_like_policy(void) {
    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);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    // 对特定调用返回 EPERM 但不杀死(便于进程优雅降级)
    seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(ptrace), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(mount), 0);
    
    seccomp_load(ctx);
    seccomp_release(ctx);
}

三、seccomp 返回值语义与生产级行为

返回值行为典型用途
SECCOMP_RET_ALLOW放行系统调用白名单条目
SECCOMP_RET_KILL_THREAD终止调用线程(不会发信号到进程)硬安全策略
SECCOMP_RET_KILL_PROCESS终止整个进程Linux 4.14+ 提供更精确的进程级终止
SECCOMP_RET_TRAP发送 SIGSYS 信号可捕获的信号,用于通知父进程
SECCOMP_RET_ERRNO(val)系统调用失败并设置 errno优雅降级策略
SECCOMP_RET_NOTIF_FD通知用户态监听器(seccomp-unotify)Linux 5.0+,模拟 mount 等调用
SECCOMP_RET_LOG记录但放行审计模式,灰度部署

四、seccomp-unotify:用户态系统调用模拟

Linux 5.0 引入的 seccomp-unotify 机制(SECCOMP_RET_NOTIF_FD)是 seccomp 发展史上的一次范式转变:被拦截的系统调用不再是简单的"放行/拒绝",而是可以转发到用户态进程由它模拟执行。这为容器运行时实现 mountptracereboot 等受控系统调用提供了可能。

// 简化的 unotify 工作流
int setup_unotify(int sec                        

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部