导读:本期聚焦于关中王创作的《如何编写自定义Seccomp Profile来限制容器系统调用?》,敬请观看详情。Seccomp Profile 本质上是一张运行在内核里的系统调用过滤表,进程每次发起系统调用都会先被这张表检查一遍,再决定放行、拒绝还是返回错误。默认的容器 Seccomp 配置虽然过滤了不少高风险调用,但业务一旦依赖某些冷门调用,就容易出现容器启动即退出或者功能异常。本文从 defaultAction 与 syscalls 两个核心字段切入,逐步说明如何列出必须放行的调用、如何做参数过滤、如何处理多架构兼容,并给出 Docker 与 Kubernetes 两个场景的完整配置示例。文中还会介绍通过 /proc/self/status、strace 以及 audit 日志来判断调用是否被拦截,帮你快速定位 profile 加载或规则匹配问题。读完可以直接套用模板生成适合自己服务的 Seccomp Profile,避免直接放开全部权限。

Seccomp 的全称是 Secure Computing Mode,是 Linux 内核提供的一种系统调用过滤机制。容器运行时在启动进程前会把 Seccomp Profile 加载进内核,之后进程每次执行系统调用都会先经过规则匹配,策略可以放行、返回错误、直接杀掉进程或只记录日志。默认的 Docker Seccomp Profile 已经禁用了 perf_event_open、clone3 等高风险调用,但默认策略并不一定贴合业务,例如某些数据库或网络工具需要额外的系统调用,如果直接沿用默认配置,可能表现为容器启动后立刻退出或功能异常。

如何编写自定义Seccomp Profile来限制容器系统调用?

编写自定义 Profile 的难点通常不是 JSON 语法,而是如何在不破坏业务的前提下缩小攻击面。下面从核心字段、实际编写、运行时应用和排错四个角度展开。

Seccomp Profile 的核心结构:defaultAction 与 syscalls

一个完整的 Seccomp Profile 是一个 JSON 文件,最关键的字段是 defaultAction 和 syscalls。defaultAction 决定没有匹配到任何规则时的默认行为,它支持 SCMP_ACT_ALLOW、SCMP_ACT_ERRNO、SCMP_ACT_KILL、SCMP_ACT_LOG 和 SCMP_ACT_NOTIFY 等动作。SCMP_ACT_ALLOW 表示放行,SCMP_ACT_ERRNO 会返回一个 errno 给调用进程,SCMP_ACT_KILL 直接终止进程,SCMP_ACT_LOG 只记录不拦截,而 SCMP_ACT_NOTIFY 可以把决策交给用户态程序。

从安全角度说,默认动作应该尽量收紧。把 defaultAction 设为 SCMP_ACT_ERRNO 或 SCMP_ACT_KILL,再把业务确实需要的系统调用通过 syscalls 数组逐条放行,是比较推荐的做法。如果默认动作是 SCMP_ACT_ALLOW,那整个 profile 基本就失去了过滤意义,只适合在调试阶段临时使用,因为它只是把黑名单里的调用禁掉,其他调用仍然全部放行。

syscalls 数组里每个元素都包含 names、action,还可以包含 args、includes、excludes 等条件字段。names 是系统调用名列表,action 是针对这些调用的动作。下面是一个非常精简的示例,只允许最基本的读写、内存映射和退出调用,其他全部返回 EPERM。

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": [
    "SCMP_ARCH_X86_64",
    "SCMP_ARCH_X86",
    "SCMP_ARCH_X32"
  ],
  "syscalls": [
    {
      "names": [
        "read",
        "write",
        "openat",
        "close",
        "mmap",
        "munmap",
        "brk",
        "exit",
        "exit_group"
      ],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

这个模板其实过于严格,很多程序连动态链接库都无法正常加载,但正好能说明默认动作与白名单的关系。真正生产环境使用前,还需要用 strace 抓取业务进程的真实调用,把必要调用补充进去。

动手编写一个最小可用的 Seccomp Profile

从零开始写 profile 的第一步是拿到进程实际使用的系统调用列表。如果应用是以二进制方式运行,可以直接用 strace 跟踪启动过程;如果是解释型语言,比如 Python 或 Node.js,需要跟踪解释器进程,而不是源码脚本。下面命令会把所有调用记录到 /tmp/app.strace,再提取出调用名去重。

strace -f -e trace=all -o /tmp/app.strace ./app
grep -oP '\w+(?=\()' /tmp/app.strace | sort -u

拿到调用名清单后,把 defaultAction 设为 SCMP_ACT_ERRNO,再把清单里的调用全部加入 syscalls 数组并设置 action 为 SCMP_ACT_ALLOW。这里有一个容易忽略的地方:很多系统调用在不同架构下编号不同,但 Seccomp Profile 用的是调用名,不是编号,所以写名称通常可以跨架构通用。不过 architectures 字段仍然要列全,否则容器可能会因为架构不匹配而加载失败。

某些系统调用还需要进一步做参数级过滤。比如 ioctl、fcntl、prctl 这类调用功能很多,放行整个调用等于把大门开得很宽。可以在 syscalls 元素中加入 args 数组,指定参数索引、比较运算符和期望值,只允许特定的请求码通过。下面示例只允许 ioctl 的第一个参数等于 TCGETS 的情况,其他请求返回 EPERM。

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "syscalls": [
    {
      "names": ["ioctl"],
      "action": "SCMP_ACT_ALLOW",
      "args": [
        {
          "index": 1,
          "op": "SCMP_CMP_EQ",
          "value": 21517,
          "valueTwo": 0
        }
      ]
    }
  ]
}

参数过滤的问题在于不同平台上的请求码可能不一样,比如 TCGETS 在 x86_64 上是 0x5401,也就是十进制的 21517,到了 ARM64 上就可能变化。因此,带有 args 条件的 profile 通常需要提前做多架构测试,或者干脆不要在通用 profile 中做过于精细的参数过滤,否则迁移架构后会遇到莫名其妙的权限错误。

在 Docker 和 Kubernetes 中应用 Seccomp Profile

Docker 容器可以直接通过 --security-opt 参数加载自定义 Seccomp Profile。下面命令启动一个 Nginx 容器,并指定本地的 profile 文件路径。路径必须是容器运行时所在主机上的绝对路径,且文件内容要能被 Docker 读取。

docker run --rm --security-opt seccomp=/path/to/my-seccomp.json --name demo nginx

如果希望 Docker 守护进程对某个容器全局生效,可以在 /etc/docker/daemon.json 中配置 seccomp-profile,但这样会影响所有未显式指定 profile 的容器。更常见的做法是在容器编排层面,由 Kubernetes 或 containerd 统一管理 Seccomp 文件。

Kubernetes 使用 securityContext.seccompProfile 字段来指定 Seccomp 策略。其中 type 可以取 RuntimeDefault、Localhost 或 Unconfined。如果需要加载自定义 JSON,就必须使用 Localhost,并把文件放到每个节点上的 /var/lib/kubelet/seccomp 目录中,然后通过 localhostProfile 指定文件名。下面是一个完整的 Pod 示例。

apiVersion: v1
kind: Pod
metadata:
  name: seccomp-demo
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: my-seccomp.json
  containers:
  - name: app
    image: nginx

使用 RuntimeDefault 时,容器运行时会加载它自带的默认 Seccomp Profile,不需要你手动维护文件,适合大多数标准工作负载。只有当默认策略跟业务冲突,或者需要更严格限制时,才需要走 Localhost 自定义文件这条路。注意节点上的 seccomp 文件必须真实存在,否则 Pod 会一直卡在 ContainerCreating 状态,kubelet 日志中会提示找不到对应文件。

常见配置错误与排查方法

最常见的问题之一是把 defaultAction 设成 SCMP_ACT_ERRNO 后,容器能启动但应用执行到某个功能就报错。此时不要急着放开全部调用,可以先看进程是否收到了 EPERM 或 EACCES。如果确认是 Seccomp 拦截导致,可以用 strace 观察进程卡在哪个系统调用,也可以直接查看内核审计日志。

grep Seccomp /proc/self/status
ausearch -m SECCOMP -ts recent

/proc/self/status 里的 Seccomp 字段值为 0 表示没有启用过滤,值为 1 表示启用严格模式,值为 2 表示启用 filter 模式。容器应用通常都处于 filter 模式。如果值为 0,说明 profile 没有成功加载,需要回头检查启动参数或 Kubernetes 配置。

另一个容易踩坑的点是架构字段缺失。默认的 Docker profile 通常会列出 x86_64、x86 和 x32 三种架构,但在 ARM 节点上运行时,可能需要补充 SCMP_ARCH_ARM64、SCMP_ARCH_ARM 等值。如果 architectures 中没有包含当前进程架构,Seccomp 加载会直接失败,容器启动阶段就会报错。

还要避免用 SCMP_ACT_KILL 覆盖所有未匹配调用。虽然它在安全上更彻底,但会让调试变得非常困难,进程被内核直接杀死时甚至没有 errno 返回值。对大多数业务来说,SCMP_ACT_ERRNO 配合合理的 errno 值,比如 EPERM 或 ENOSYS,更容易暴露问题,也更方便应用层做错误处理。

优化建议:从默认 Docker Profile 出发定制

自己从零拼凑系统调用白名单虽然直观,但非常容易漏掉动态链接器、glibc 初始化、DNS 解析等底层依赖调用。更稳妥的做法是拿容器运行时自带的默认 Seccomp Profile 作为基线,在它基础上做减法或加法。Docker 的默认 profile 文件可以从 moby 项目的源码中找到,containerd 也有类似的默认 seccomp 配置文件,通常位于安装包或源码的 contrib/seccomp 目录下。

拿到默认 profile 后,先把 defaultAction 保持为 SCMP_ACT_ERRNO,然后观察业务是否需要额外的调用。如果某个调用被默认策略拦截,可以在 syscalls 数组中追加一条放行规则;如果某个默认放行的调用明显不需要,可以把它改成 SCMP_ACT_ERRNO 或者从白名单里删掉,同时保留一条显式的 SCMP_ACT_ERRNO 规则做文档说明。

也可以用 seccomp-tools 这类辅助工具直接 dump 正在运行进程的 Seccomp 过滤条件。下面命令会打印出指定 PID 进程当前的 Seccomp 规则,适合在已经跑起来的容器里做反向分析,看看最终生效的策略到底允许了哪些调用。

seccomp-tools dump --pid 1234

自定义 Seccomp Profile 的价值在于把容器权限收敛到业务真正需要的范围。虽然短期会增加一些调试成本,但一旦 profile 稳定下来,就能有效降低内核漏洞被利用的风险。建议把 profile 文件纳入版本控制,和业务代码一起走 CI 流程,每次变更后先在测试环境跑完完整用例,再推广到生产节点。

Seccomp容器安全系统调用过滤修改时间:2026-09-24 12:34:16

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0924/61313.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。