Linux seccomp机制原理

Linux seccomp(secure computing)是一种内核安全机制,用于严格限制进程可用的系统调用(syscalls)。它的核心目标是通过减少内核攻击面来增强安全性。当一个进程(或其子进程)进入 seccomp 模式后,它只能调用被明确允许的极少数系统调用。任何尝试调用未授权的系统调用都会立即被内核终止(默认行为)。

核心原理:

  1. 系统调用过滤:

    • Linux 应用程序的所有特权操作(文件访问、网络通信、进程管理等)都通过系统调用请求内核服务。
    • 攻击者常常利用程序漏洞(如缓冲区溢出)执行任意代码,并尝试调用危险的系统调用(如 execve, socket, ptrace, keyctl)来提升权限、突破沙箱或进行其他恶意操作。
    • seccomp 允许为进程定义一个过滤器规则集,明确指定哪些系统调用是允许的,哪些是禁止的。只有白名单上的调用才能被执行。
  2. 基于 BPF 的过滤引擎:

    • seccomp 的现代实现(称为 seccomp-bpf)利用 Berkeley Packet Filter (BPF) 作为其过滤引擎。
    • 开发者编写一个 BPF 程序。这个程序不是处理网络数据包,而是评估每一个将要执行的系统调用
    • BPF 程序对每个系统调用进行检查(主要基于系统调用号 syscall_nr,有时也会检查参数 args),然后返回一个预定义的裁决:
      • SECCOMP_RET_ALLOW: 允许该系统调用执行。
      • SECCOMP_RET_KILL_PROCESS / SECCOMP_RET_KILL_THREAD: 立即终止进程或线程(现代首选 KILL_PROCESS)。
      • SECCOMP_RET_ERRNO: 阻止调用,并返回指定的 errno 给用户空间(模拟调用失败)。
      • SECCOMP_RET_TRAP: 发送一个可捕获的 SIGSYS 信号给进程,允许用户空间处理程序响应。
      • SECCOMP_RET_TRACE: 用于通知 ptrace 跟踪器,让跟踪器决定是否允许调用(常用于更复杂的沙箱)。
      • SECCOMP_RET_LOG: 允许调用,但记录该调用请求(用于审计或调试)。
    • BPF 程序在内核空间以 JIT 编译的形式高效执行,性能开销很小。
  3. 应用 seccomp 过滤器:

    • 进程通过 prctl(PR_SET_SECCOMP, ...) 或更现代的 seccomp(SECCOMP_SET_MODE_FILTER, ...) 系统调用将自己置于 seccomp-bpf 模式。
    • 调用时,需要传递一个指向已编译 BPF 程序(指令数组)的指针。
    • 一旦过滤器被应用,该进程及其所有未来创建的子进程(除非它们自己调用 execve 重置状态)都将受到这些规则的约束。 这是一个关键且不可逆的操作(严格模式下)。
  4. 执行流程(简化):

    1. 用户空间进程发起一个系统调用(如 open)。
    2. 内核在真正执行该系统调用的处理程序之前,会检查该进程是否启用了 seccomp-bpf 过滤器。
    3. 如果启用了,内核将暂停该系统调用的执行。
    4. 内核加载为该进程配置的 BPF 过滤器程序。
    5. 内核将系统调用号(和可选参数)作为输入传递给 BPF 程序。
    6. BPF 程序执行其过滤逻辑并返回裁决(ALLOW, KILL, ERRNO 等)。
    7. 内核根据 BPF 的裁决行动:
      • ALLOW: 恢复该系统调用的正常执行。
      • KILL: 立即终止进程/线程。
      • ERRNO: 不执行系统调用,直接返回指定的错误号给用户空间。
      • 其他裁决: 执行相应操作(发送信号、通知 ptrace 等)。

seccomp 模式:

  • SECCOMP_MODE_DISABLED: 默认状态,无限制。
  • SECCOMP_MODE_STRICT: 原始模式(已过时)。只允许 read, write, _exit (以及 sigreturn)。任何其他系统调用导致 SIGKILL。功能有限,很少直接使用。
  • SECCOMP_MODE_FILTER: 现代模式,即 seccomp-bpf。使用 BPF 程序定义灵活的过滤策略,是实际使用的标准模式。

为什么 seccomp 重要?

  1. 最小权限原则: 即使攻击者成功在进程中执行了任意代码,seccomp 也能阻止其调用关键的、危险的系统调用,极大地限制其破坏能力(提权、逃逸、建立持久化)。
  2. 减少内核攻击面: 内核庞大而复杂,历史漏洞众多。seccomp 直接阻止进程接触大量潜在存在漏洞的系统调用接口。
  3. 沙箱构建基础: 是现代沙箱技术(如 Chrome/Chromium 的 Renderer 进程沙箱、Firefox, systemd 服务单元、Docker/containerd 的容器运行时)的核心组件之一。它与其他机制(如 Namespaces, Capabilities, SELinux/AppArmor)协同工作,构建纵深防御。

关键特点与注意事项:

  • 进程粒度: 过滤器应用于单个进程及其后代(直到 execve)。
  • 不可逆性 (严格模式下): 一旦通过 prctl/seccomp 进入 SECCOMP_MODE_FILTER,进程自身通常无法移除或放宽过滤器(这是设计的安全特性)。子进程继承过滤器。
  • execve 的影响: 调用 execve 加载新程序后,新程序继承父进程的 seccomp 过滤器状态(如果父进程设置了 SECCOMP_FILTER_FLAG_TSYNC 或类似标志,线程也会同步)。新程序可以在其初始化阶段选择应用更严格的过滤器(但不能移除或放宽继承来的过滤器)。
  • 性能: seccomp-bpf 的 BPF 过滤器经过高度优化(JIT编译),性能开销通常很低(<1%),使其适用于高性能场景。
  • 复杂性: 正确设计和维护一个安全的 seccomp 策略(BPF程序)需要深入了解应用程序行为和 Linux 系统调用。错误的策略可能导致合法应用功能失效。
  • 与 Namespaces/Capabilities 协同: seccomp 专注于限制 系统调用接口。它通常与:
    • Namespaces: 限制进程的视图(网络、挂载点、PID 等)。
    • Capabilities: 拆分 root 特权为细粒度单元。
      结合使用,共同构建强大的隔离环境。

总结:

Linux seccomp(特别是 seccomp-bpf)是一种强大的内核级安全机制,它允许进程主动限制自己及其子进程所能使用的系统调用。通过加载一个用 BPF 编写的自定义过滤器程序,进程可以精确控制哪些系统调用被允许执行,哪些会被阻止并触发终止或错误。这种强制性的最小权限策略是构建安全沙箱、容器和强化应用程序抵御攻击的关键技术,通过显著缩小内核暴露给潜在恶意代码的攻击面来提升系统整体安全性。