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

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

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

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

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

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

目录


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

容器共享宿主机内核。容器里的进程虽然拥有独立的文件系统、网络命名空间和进程视图,但最终仍然会通过系统调用进入同一个 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 程序通常由以下部分组成:

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

2.2 eBPF 的安全性来自哪里?

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

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

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

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

2.3 用 bpftrace 观察进程执行

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

bash 复制代码
sudo bpftrace -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 规则:

text 复制代码
/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. 持续观察误报和业务变更。
bash 复制代码
# 查看 profile 状态
sudo aa-status

# 将 profile 切换为观察模式
sudo aa-complain /etc/apparmor.d/usr.sbin.my-service

# 将 profile 切换为强制模式
sudo aa-enforce /etc/apparmor.d/usr.sbin.my-service

3.3 Docker 中使用 AppArmor

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

bash 复制代码
docker run --rm \
  --security-opt apparmor=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 还需要根据应用实际调用情况设计,不建议直接拿示例当生产白名单。

json 复制代码
{
  "defaultAction": "SCMP_ACT_ALLOW",
  "syscalls": [
    {
      "names": ["mount"],
      "action": "SCMP_ACT_ERRNO",
      "errnoRet": 1
    }
  ]
}

启动容器:

bash 复制代码
docker run --rm \
  --security-opt seccomp=./seccomp.json \
  nginx:stable

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

4.4 Seccomp 与 capability 的关系

两者控制层次不同:

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

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

bash 复制代码
docker run --rm \
  --cap-drop=ALL \
  --security-opt no-new-privileges:true \
  --security-opt seccomp=./seccomp.json \
  image:tag

4.5 Seccomp 的局限

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

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

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

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

推荐组合

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

一个现实的安全闭环

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

六、容器安全基线示例

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

bash 复制代码
docker run --rm \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --cap-drop=ALL \
  --security-opt no-new-privileges:true \
  --security-opt seccomp=./seccomp.json \
  --security-opt apparmor=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 云原生 容器安全 eBPF AppArmor Seccomp Docker Kubernetes 内核安全 DevSecOps

相关推荐
H.莓飛1 小时前
【Linux】命令行参数、环境变量与程序地址空间
linux·c语言·chrome·后端·centos
‎ദ്ദിᵔ.˛.ᵔ₎1 小时前
linux 进程间通信
linux·网络
Ruiery1 小时前
Linux 6.6内核 CPU 深度解析(八):percpu 变量 — 每个 CPU 一份数据,无锁访问
linux·运维·服务器
91刘仁德1 小时前
传输层协议深度拆解:端口寻址、UDP 报文与 TCP 可靠性机制全解析
linux·网络·c++·网络协议·tcp/ip·udp·c
AIgorithmGEEK2 小时前
[Linux]HTTP 应用层协议全解(中篇)
linux·网络·网络协议·http
玖石书2 小时前
linux 安装uv(python虚拟环境管理)
linux·python·uv
91刘仁德2 小时前
HTTPS 加密原理与 CA 数字证书:从对称加密到完整通信流程
网络·笔记·网络协议·http·https
奈落242 小时前
【RAG 深度修炼】专栏 · 第 9 期(收官):安全专题与生产 Checklist 总集——从能跑的 Demo 到睡得着觉的生产系统
大数据·网络·人工智能·安全·ai编程
皓月盈江2 小时前
Hexo博客Butterfly5.7.0主题网站信息:本站访客数和本站总浏览量的数据一直加载不显示的解决方法
linux·hexo·butterfly·busuanzi·vercount·本站访客数·本站总浏览量