news 2026/10/5 21:45:49

我是怎么理解 eBPF、AppArmor 和 Seccomp 的?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
我是怎么理解 eBPF、AppArmor 和 Seccomp 的?

我是怎么理解 eBPF、AppArmor 和 Seccomp 的?

先说结论:不要把 eBPF 当成 AppArmor,也不要把 Seccomp 当成完整的沙箱。我的理解是:Seccomp 缩小系统调用面,AppArmor 限制文件和能力,eBPF 负责把运行时发生的事情记录下来。

写在前面:这不是一篇“看完就会逃逸”的文章

我最开始接触这几个东西,是因为一次很普通的容器故障:服务在宿主机上运行正常,放进容器后却突然报Permission denied。当时第一反应是把权限放开,结果服务是起来了,但自己也说不清到底放开了什么。

后来我才慢慢把这件事拆开:先看进程调用了什么,再看 syscall 有没有被拦,最后看文件、能力和 profile。这个过程里,eBPF、Seccomp 和 AppArmor 才从几个容易混在一起的名词,变成了三个不同位置的工具。

目录

  • 一、为什么要同时理解这三项技术?
  • 二、eBPF:在内核事件上构建可观测性与安全能力
  • 三、AppArmor:以配置文件约束进程行为
  • 四、Seccomp:用系统调用过滤缩小内核攻击面
  • 五、三者放在一起:分层防御模型
  • 六、容器安全基线示例
  • 七、总结
  • 参考资料

一、为什么要同时理解这三项技术?

容器共享宿主机内核。容器里的进程虽然拥有独立的文件系统、网络命名空间和进程视图,但最终仍然会通过系统调用进入同一个 Linux 内核。

一次典型的攻击链可能是:

  1. 应用依赖存在漏洞,攻击者获得容器内代码执行能力;
  2. 进程尝试读取敏感文件、访问宿主机设备,或调用高风险系统调用;
  3. 攻击者继续利用内核或配置错误,扩大权限;
  4. 运行时没有审计与告警,直到数据泄露才被发现。

三项技术分别解决不同问题:

技术核心问题典型能力不能替代什么
eBPF发生了什么?是否异常?低侵入观测、审计、网络处理、运行时检测不是默认的访问控制策略,也不是自动沙箱
AppArmor进程可以访问哪些对象?文件路径、能力、网络、信号等强制访问控制不能完整限制所有系统调用
Seccomp进程可以调用哪些系统调用?允许/拒绝 syscall,过滤参数,降低内核攻击面不能表达复杂的文件路径权限

一句话记忆:

Seccomp 管入口,AppArmor 管资源,eBPF 管观测和动态策略。


二、eBPF:在内核事件上构建可观测性与安全能力

2.1 eBPF 到底是什么?

eBPF(extended Berkeley Packet Filter)是一套运行在 Linux 内核中的安全、可验证、事件驱动的程序执行机制。它允许开发者把小型程序加载到内核中的特定挂载点,在不修改内核源码、通常也不需要重启内核的情况下,观测或处理系统行为。

常见挂载点包括:

  • Tracepoint:内核预定义的稳定事件,例如进程执行、系统调用、网络事件;
  • kprobe/kretprobe:动态探测内核函数的进入和返回;
  • Uprobe/uretprobe:探测用户态程序或共享库函数;
  • LSM BPF:参与 Linux Security Module 决策;
  • TC/XDP:在网络协议栈较早阶段处理数据包;
  • cgroup hooks:围绕容器或 cgroup 进行网络、套接字和设备相关控制。

一个 eBPF 程序通常由以下部分组成:

用户态加载器 │ 通过 bpf() 系统调用加载 ▼ 内核 verifier(验证器) │ 检查边界、循环、内存访问和可达性 ▼ eBPF 程序 + map + ring buffer │ ├── 采集内核事件 ├── 更新状态 └── 将事件送回用户态

2.2 eBPF 的安全性来自哪里?

eBPF 并不是“任意内核代码注入”。程序加载前通常会经过 verifier 检查,重点包括:

  • 是否存在越界内存访问;
  • 指针类型和生命周期是否正确;
  • 是否可能执行不可控的无限循环;
  • 是否调用了当前程序类型不允许的 helper;
  • 是否满足权限和内核配置要求。

但这不意味着 eBPF 没有风险。生产环境仍然要关注:

  • 谁拥有加载 eBPF 程序所需的权限;
  • 内核版本、BTF、helper 能力是否一致;
  • 运行时探针是否引入过高开销;
  • 探针采集的数据是否包含敏感信息;
  • 容器是否被授予了不必要的CAP_BPF、CAP_SYS_ADMIN等能力。

2.3 用 bpftrace 观察进程执行

下面的示例用于观察系统中执行过的程序。它适合在测试环境快速定位“谁启动了什么”,不建议直接作为生产审计方案。

sudobpftrace-e' tracepoint:syscalls:sys_enter_execve { printf("pid=%d comm=%s file=%s\\n", pid, comm, str(args->filename)); } '

在容器环境中,可以进一步结合 PID、cgroup、容器元数据进行归属判断。实际落地时,建议使用成熟工具或自研 agent,统一处理事件去重、采样、脱敏和告警。

我自己排查问题时,一般先从execve这种事件开始看。因为“容器里到底启动了什么”往往比一上来研究一大堆 profile 更容易得到线索。比如服务偷偷启动了 shell、健康检查脚本路径不对,通常几分钟就能看出来。

2.4 eBPF 适合做什么?

  • 进程启动、文件访问、网络连接、提权行为审计;
  • 容器运行时检测,例如在容器中执行 shell、写入敏感路径;
  • 网络可观测性和高性能数据面处理;
  • 性能分析,例如 CPU、I/O、锁竞争和调度延迟;
  • 与 LSM、cgroup、网络 hook 结合,构建动态安全策略。

2.5 eBPF 的边界

eBPF 首先是一种内核扩展和事件处理机制,而不是“一键安全开关”。

  • 只部署观测程序,并不会自动阻止危险行为;
  • 事件采集不等于完整审计,丢事件、采样和权限都会影响结论;
  • 直接使用 kprobe 依赖内核实现细节,跨版本稳定性不如 tracepoint;
  • 复杂策略需要清晰的失败模式,否则可能出现误杀或性能问题;
  • eBPF 程序本身也必须纳入版本管理、权限管理和发布审计。

三、AppArmor:以配置文件约束进程行为

3.1 AppArmor 的基本模型

AppArmor 是 Linux Security Module(LSM)之一,采用路径为中心的强制访问控制模型。管理员为一个程序定义 profile,指定它可以读取、写入、执行哪些路径,以及允许使用哪些能力、网络类型和信号。

与传统 Unix 权限相比,AppArmor 的关键差异是:

  • Unix 权限回答“文件所有者和权限位是否允许”;
  • AppArmor 进一步回答“这个进程即使拥有 Unix 权限,是否仍被 profile 允许”。

常见 profile 规则:

/usr/bin/my-service { # 只读配置 /etc/my-service/** r, # 允许写入运行时目录 /var/lib/my-service/** rwk, # 允许执行指定程序 /usr/bin/helper ix, # 拒绝访问密钥目录 deny /root/.ssh/** r, # 允许建立网络连接 network inet stream, }

权限字符常见含义:

权限含义
r读取
w写入
k文件锁
m映射到内存并执行
x执行,具体继承方式由规则决定
ix执行后继承当前 profile
px执行后切换到指定 profile

一个我觉得比较实用的排查顺序

遇到权限问题时,我现在基本不再直接给容器加privileged。先确认实际行为,再一点点加规则,最后用完整链路回归验证。这样慢一点,但出了问题还能解释,也方便后续收紧权限。


3.2 Complain 模式与 Enforce 模式

开发和上线前建议采用两阶段:

  1. Complain:记录违反规则的行为,但不阻止;
  2. 根据审计日志补充最小权限规则;
  3. Enforce:正式阻止未授权行为;
  4. 持续观察误报和业务变更。
# 查看 profile 状态sudoaa-status# 将 profile 切换为观察模式sudoaa-complain /etc/apparmor.d/usr.sbin.my-service# 将 profile 切换为强制模式sudoaa-enforce /etc/apparmor.d/usr.sbin.my-service

3.3 Docker 中使用 AppArmor

宿主机已加载 profile 后,可以在启动容器时指定:

dockerrun--rm\--security-optapparmor=my-container-profile\--read-only\--cap-drop=ALL\nginx:stable

Kubernetes 中可以通过运行时类相关配置或安全上下文使用 AppArmor。不同 Kubernetes 版本和容器运行时的配置方式存在差异,生产环境应以集群版本文档和节点实际 profile 状态为准。

3.4 AppArmor 的优势与局限

优势:

  • 规则以路径为中心,对应用运维人员相对直观;
  • 可以精细限制配置、密钥、设备和执行文件;
  • 能与容器运行时结合,形成应用级隔离;
  • 不需要改造应用代码。

局限:

  • 路径模型不等于文件对象模型,硬链接、挂载和命名空间场景需要谨慎验证;
  • profile 过于宽松时,攻击者仍可能利用允许路径;
  • profile 过于严格时,容易因业务升级产生启动失败;
  • AppArmor 不是系统调用过滤器,不能替代 Seccomp;
  • 宿主机必须启用并正确配置 AppArmor,容器内写 profile 并不会自动获得宿主机强制效果。

四、Seccomp:用系统调用过滤缩小内核攻击面

4.1 为什么要限制系统调用?

Linux 用户态程序通过系统调用使用内核能力。一个普通 Web 服务可能只需要文件、网络、内存和线程相关系统调用,但如果它同时拥有挂载、加载内核模块、调试任意进程等能力,漏洞被利用后的攻击面会显著扩大。

Seccomp(secure computing)允许进程设置系统调用过滤器。当进程发起 syscall 时,过滤器可以根据:

  • 系统调用编号;
  • 架构;
  • syscall 参数;
  • 当前过滤器动作;

决定允许、拒绝、返回错误、触发审计或终止进程。

4.2 Seccomp 的两种主要模式

  • Strict mode:只允许极少数系统调用,适用范围非常有限;
  • Filter mode:通过 BPF 过滤器定义更灵活的规则,容器场景主要使用这一模式。

Seccomp 使用的是经典 BPF 过滤逻辑,和 eBPF 有关联但不是同一个东西。不要因为都出现 BPF 就把两者当成同一套技术:Seccomp 的目标是 syscall 过滤,eBPF 的目标是可验证的内核程序与事件处理生态。

4.3 Docker 使用自定义 Seccomp profile

我第一次写 Seccomp 规则时,犯过一个很典型的错误:看到某个 syscall 可疑,就直接拒绝,结果服务启动阶段就挂了。后来改成先用默认 profile 跑完整测试,再针对确实不需要的调用做限制,排障成本低很多。

下面是一个简化示例,表示拒绝mount系统调用。完整 profile 还需要根据应用实际调用情况设计,不建议直接拿示例当生产白名单。

{"defaultAction":"SCMP_ACT_ALLOW","syscalls":[{"names":["mount"],"action":"SCMP_ACT_ERRNO","errnoRet":1}]}

启动容器:

dockerrun--rm\--security-optseccomp=./seccomp.json\nginx:stable

生产环境更推荐从“默认拒绝、按需放行”的思路设计,但必须先通过测试或运行时观测收集应用调用集,否则很容易误伤正常功能。

4.4 Seccomp 与 capability 的关系

两者控制层次不同:

  • Capability控制进程是否拥有某类特权;
  • Seccomp控制进程能否调用某些系统调用;
  • 某个 syscall 被允许,也不代表它一定有权限成功执行;
  • 某个 capability 被删除,也不代表相关 syscall 不会被调用。

因此,安全基线通常是同时做:

dockerrun--rm\--cap-drop=ALL\--security-opt no-new-privileges:true\--security-optseccomp=./seccomp.json\image:tag

4.5 Seccomp 的局限

  • 它看见的是 syscall,不擅长表达“只能访问某个目录”;
  • syscall 参数过滤复杂,架构差异和兼容性需要测试;
  • 某些高层行为会通过多个 syscall 完成,只过滤一个 syscall 可能不够;
  • 容器运行时默认 profile 不是万能的,不能替代应用自身的最小权限设计;
  • 过滤规则错误可能导致应用启动失败,甚至让故障排查变得困难。

五、三者放在一起:分层防御模型

一个更准确的关系图如下:

应用进程 │ ├── 发起系统调用 ──> Seccomp:这个 syscall 能不能进内核? │ ├── 访问文件/网络/能力 ──> AppArmor:这个资源是否允许访问? │ └── 行为产生事件 ──> eBPF:谁做了什么?是否需要告警或处置?

推荐组合

场景SeccompAppArmoreBPF
互联网暴露的 Web 服务必选推荐推荐
构建任务/CI Runner必选推荐推荐
多租户平台严格配置严格配置强烈推荐
低风险内部工具使用默认 profile按需按需
需要高性能网络处理保持 syscall 最小化保护宿主资源可用于 XDP/TC

一个现实的安全闭环

  1. 用 eBPF 或审计工具收集应用真实行为;
  2. 根据行为生成初始 syscall 和文件访问清单;
  3. 用 Seccomp 删除不需要的内核入口;
  4. 用 AppArmor 限制配置、密钥、设备和执行路径;
  5. 再用 eBPF 持续检测 profile 绕过、异常执行和横向行为;
  6. 将事件接入日志、告警和应急响应系统。

六、容器安全基线示例

下面的命令展示一组常见的加固参数。参数是否适用要以应用测试结果为准:

dockerrun--rm\--read-only\--tmpfs/tmp:rw,noexec,nosuid,size=64m\--cap-drop=ALL\--security-opt no-new-privileges:true\--security-optseccomp=./seccomp.json\--security-optapparmor=my-container-profile\--pids-limit=256\--memory=512m\--cpus=1\my-service:1.0

检查重点:

  • 是否真的需要写入根文件系统;
  • 是否真的需要某个 capability;
  • 是否存在必须使用的 setuid/setgid 程序;
  • 是否需要调试、挂载、加载模块或访问设备;
  • profile 被拒绝后,应用是否能输出可定位的错误日志;
  • 规则是否经过升级、回滚和灾备演练。

在 Kubernetes 中,还应结合:

  • allowPrivilegeEscalation: false;
  • readOnlyRootFilesystem: true;
  • runAsNonRoot: true;
  • 删除不必要的 Linux capabilities;
  • Pod Security Standards;
  • 节点级 AppArmor/Seccomp 配置;
  • 运行时检测与审计平台。

七、总结

  • Seccomp通过过滤系统调用,减少进程进入内核的攻击面;
  • AppArmor通过 profile 限制进程访问文件、网络、能力和其他资源;
  • eBPF通过内核 hook 提供高性能观测,并可扩展到网络、运行时检测和安全策略;
  • 三者不是互相替代,而是位于不同层次的防御能力;
  • 真正可靠的容器安全,依赖最小权限、分层控制、持续观测和可回滚的工程流程。

最终建议:把安全策略当成代码来维护。每次镜像、内核、运行时或业务依赖升级,都重新验证 Seccomp、AppArmor 和 eBPF 规则,而不是把一次成功启动当成安全证明。


参考资料

  1. Linux Kernel Documentation - eBPF:https://docs.kernel.org/bpf/index.html
  2. eBPF 官方文档:https://ebpf.io/what-is-ebpf/
  3. Linux Kernel Documentation - Seccomp:https://docs.kernel.org/userspace-api/seccomp_filter.html
  4. Docker Seccomp 安全配置:https://docs.docker.com/engine/security/seccomp/
  5. Ubuntu AppArmor 文档:https://documentation.ubuntu.com/server/how-to/security/apparmor/
  6. Kubernetes Security Context:https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
  7. Linux capabilities 手册:https://man7.org/linux/man-pages/man7/capabilities.7.html

CSDN 标签:Linux云原生容器安全eBPFAppArmorSeccompDockerKubernetes内核安全DevSecOps

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 21:43:46

[AI技术(二)]JSONRPC协议MCPRAGAgent:把MCP endpoint改到TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 21:25:57

更新了!带 Agent 的 Cursor 太疯狂了:TaoToken 统一 Key 接入教程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 21:22:54

工业数据采集方案:PIC18F46K20 与 MRAM MR25H40CDF 的 SPI 驱动实践

1. 项目缘起与方案选型思考1.1 为什么要在工业场景里折腾 MRAM 这颗"新料"做工业嵌入式这行的朋友应该都有体会,选存储芯片这件事,往往比选主控还让人头疼。EEPROM 擦写寿命撑不住高频采集,NOR Flash 写入前要擦块、掉电还容易丢数…

作者头像 李华
网站建设 2026/10/5 20:57:32

Python3字符串全攻略:不可变性、编码与高效拼接避坑指南

做数据分析、写爬虫、用Django做后台,甚至刷LeetCode的字符串题,你几乎绕不开Python3的字符串。看着简单,但真正上手你会发现坑比想象中多:编码乱码、不可变对象带来的修改陷阱、循环拼接的效率问题,每一项都能让你在线…

作者头像 李华