当容器跑着不可信代码,系统调用就是最大的攻击面。seccomp-BPF 是 Linux 内核提供的"系统调用防火墙",它工作在 Ring 0 与 Ring 3 的边界上,以极简的 BPF 字节码实现最低成本的进程隔离。
一、从 Capabilities 到 seccomp:Linux 安全的"做减法"哲学
现代 Linux 安全遵循一个核心原则:最小特权(Least Privilege)。传统的 Unix 权限模型把进程分为"root"和"非 root"两类,root 可以做任何事,这导致容器技术早期的最大痛点——一个逃逸的容器进程几乎等价于宿主机沦陷。
为了解决这个问题,Linux 安全栈经历了三个阶段的演化:
Capabilities (2.2 ) ──► seccomp (2.6.12 ) ──► Landlock (5.13 )
拆分特权 系统调用过滤 文件系统沙箱
粗粒度权限 细粒度能力限制 细粒度资源访问
Capabilities 将 root 特权拆分为 40 个独立的 capability(如 CAP_NET_ADMIN、CAP_SYS_ADMIN),使得容器可以只保留所需的最小权限集。但 Capabilities 的问题在于:即使只剩 CAP_SYS_ADMIN,进程仍有数百个系统调用可用,攻击面依然庞大。
seccomp 的出现解决了这个问题:它从系统调用入口上直接限制进程的能力,无论进程拥有哪些 Capabilities,被 seccomp 过滤的系统调用都无法执行。
二、seccomp 架构解析:内核中的代码路径
2.1 系统调用流程中的拦截点
当用户态进程执行 syscall 指令时,CPU 从 Ring 3 切换到 Ring 0,内核的入口处理如下:
// arch/x86/entry/entry_64.S 简化流程
ENTRY(entry_SYSCALL_64)
swapgs // 切换到内核 GS 基址
movq %rsp, PER_CPU_VAR(rsp_scratch)
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
// 构造 pt_regs 结构
pushq $__USER_DS // SS
pushq PER_CPU_VAR(rsp_scratch) // RSP
pushq %r11 // RFLAGS
pushq $__USER_CS // CS
pushq %rcx // RIP
...
call do_syscall_64 // ← C 语言入口
// arch/x86/kernel/common.c
__visible noinstr void do_syscall_64(struct pt_regs *regs)
{
// ① seccomp 检查点在这里
if (secure_computing(regs)) {
// 被过滤,记录并返回错误
syscall_enter_from_user_mode(regs, regs-

发表评论 取消回复