上个月,我们的生产集群发生了一件事:一个 Web 容器通过
CAP_SYS_PTRACE + /proc/1/root
组合拳,成功读取了宿主机的 /etc/shadow。
Seccomp
profile?全绿。openat()、read()、write()——全是白名单里的调用。Seccomp
看到的是”正常打开文件”,但它看不到
文件路径指向了宿主机的敏感数据。
这就是 Seccomp 的根本局限:无状态 syscall 过滤器,不理解上下文。而 eBPF,可以。
如果你还不熟悉 eBPF 基础,建议先读 eBPF:Linux 内核的隐藏武器。本文假设你已理解 Map、verifier、程序类型等基本概念。
一、为什么 Seccomp 不够用
在 Seccomp-BPF 与 Capabilities:容器安全的两道防线 中我们讲了 Seccomp-BPF 的原理。这里聚焦它在安全监控场景下的 结构性缺陷。
Seccomp 的世界观:syscall number + args,仅此而已
Seccomp-BPF 收到的数据结构是
struct seccomp_data:
struct seccomp_data {
int nr; // syscall 编号
__u32 arch; // 架构
__u64 instruction_pointer; // 指令地址
__u64 args[6]; // syscall 参数(原始值)
};注意 args[6] 是
原始寄存器值——openat() 的
args[1] 是文件路径的指针,不是字符串。经典 BPF
无法解引用用户态指针,Seccomp
根本不知道你在打开哪个文件。
Seccomp 看不到的维度
| 维度 | Seccomp 能看到? | eBPF 能看到? |
|---|---|---|
| syscall 编号和参数值 | ✅ | ✅ |
| 文件路径(解引用后) | ❌ | ✅
bpf_d_path() |
| 进程树 / 父进程信息 | ❌ | ✅
bpf_get_current_task() |
| 网络连接目标 IP:Port | ❌ | ✅
struct sock |
| 容器 ID / cgroup | ❌ | ✅
bpf_get_current_cgroup_id() |
| mount namespace 上下文 | ❌ | ✅ 通过
task_struct |
经典 BPF vs eBPF:不是同一个物种
Seccomp 用的是 经典 BPF(cBPF),1992 年设计的包过滤 VM,和现代 eBPF 不是同一物种:
| 特性 | 经典 BPF (Seccomp) | eBPF |
|---|---|---|
| 寄存器数量 | 2 (A, X) | 11 (r0-r10) |
| 栈大小 | 16 slot (64B) | 512 字节 |
| Map 支持 | ❌ | ✅ 多种类型 |
| Helper 函数 | ❌ | ✅ 数百个 |
| 指针解引用 | ❌ | ✅
bpf_probe_read_* |
| 循环 | ❌ | ✅ 有界循环 (5.3+) |
| 尾调用 | ❌ | ✅
bpf_tail_call |
Seccomp-BPF 中的 “BPF” 是历史遗留命名。它和现代 eBPF 的能力差了不止一个数量级。
快速启用检查清单
Seccomp-BPF 快速启用检查清单
项目 要求 验证命令 最低内核版本 3.5(基础),3.17( SECCOMP_SET_MODE_FILTER)uname -r内核配置 CONFIG_SECCOMP=y,CONFIG_SECCOMP_FILTER=yzgrep CONFIG_SECCOMP /proc/config.gz或grep CONFIG_SECCOMP /boot/config-$(uname -r)运行时检查 /proc/sys/kernel/seccomp/actions_avail存在cat /proc/sys/kernel/seccomp/actions_avail容器运行时 Docker 默认启用;containerd 需配置 seccomp_profiledocker info \| grep -i seccomp验证生效 查看进程的 seccomp 模式 grep Seccomp /proc/<PID>/status
二、LSM BPF:给 Linux 安全模块加上 eBPF 引擎
LSM(Linux Security
Modules)是内核的安全框架,SELinux、AppArmor 都是其实现。LSM
hook 在 syscall
入口之后执行——此时内核已完成参数解析,struct file
等对象已就绪。
从 Linux 5.7 开始,BPF_PROG_TYPE_LSM
允许你将 eBPF 程序挂载到任意 LSM
hook——不编译内核模块,不重启。
常用 hook 点:
| Hook 点 | 触发时机 | 典型用途 |
|---|---|---|
file_open |
打开文件时 | 阻止敏感文件访问 |
bprm_check_security |
执行新程序前 | 阻止特定二进制执行 |
socket_connect |
建立网络连接时 | 限制出站连接 |
task_alloc |
创建新进程时 | 限制进程创建 |
sb_mount |
挂载文件系统时 | 防止未授权挂载 |
SELinux/AppArmor vs BPF LSM
| 特性 | SELinux/AppArmor | BPF LSM |
|---|---|---|
| 策略加载 | 编译策略文件,重载服务 | bpf() syscall
动态加载 |
| 策略语言 | 专用 DSL(m4 宏 / profile) | C + eBPF |
| 修改策略需重启? | 通常需要 | 不需要 |
| 可编程性 | 低(规则匹配) | 高(图灵完备*) |
| 与 SELinux 共存 | 互斥(传统) | ✅ 可堆叠 |
*eBPF 的图灵完备性受 verifier 约束:有界循环、有限栈深度、禁止无限递归。
实战:用 BPF LSM 阻止 /etc/shadow 写入
// lsm_block_shadow_write.bpf.c — 阻止对 /etc/shadow 的写入
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#define SHADOW_PATH "/etc/shadow"
#define MAX_PATH_LEN 256
struct event { __u32 pid; __u32 uid; char comm[16]; char path[MAX_PATH_LEN]; };
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} events SEC(".maps");
// 返回 0 = 允许,负值 = 拒绝
SEC("lsm/file_permission")
int BPF_PROG(block_shadow_write, struct file *file, int mask)
{
if (!(mask & 2)) // 只关心写操作(MAY_WRITE)
return 0;
char path_buf[MAX_PATH_LEN] = {};
struct path f_path = BPF_CORE_READ(file, f_path);
if (bpf_d_path(&f_path, path_buf, sizeof(path_buf)) < 0)
return 0;
// 逐字节比较目标路径
const char target[] = SHADOW_PATH;
for (int i = 0; i < sizeof(target) - 1; i++) {
if (path_buf[i] != target[i])
return 0;
}
// 匹配!记录事件并阻断
struct event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (e) {
e->pid = bpf_get_current_pid_tgid() >> 32;
e->uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
bpf_get_current_comm(&e->comm, sizeof(e->comm));
__builtin_memcpy(&e->path, path_buf, MAX_PATH_LEN);
bpf_ringbuf_submit(e, 0);
}
return -13; // -EACCES
}
char LICENSE[] SEC("license") = "GPL";编译加载:
# 编译 eBPF 程序
clang -O2 -target bpf -D__TARGET_ARCH_x86 \
-c lsm_block_shadow_write.bpf.c -o lsm_block_shadow_write.bpf.o
# 确认内核支持 BPF LSM
cat /boot/config-$(uname -r) | grep BPF_LSM # 应输出 CONFIG_BPF_LSM=y
cat /sys/kernel/security/lsm # 应包含 bpf
# 加载 eBPF 程序
bpftool prog load restrict_write.o /sys/fs/bpf/restrict_write type lsm
# 查看已加载的 LSM 程序
bpftool prog list | grep lsm
# 注意:使用 libbpf skeleton 自动 attach 更常见
# bpf_object__open_file() → bpf_object__load() → bpf_program__attach_lsm()⚠️ BPF LSM 需要
CONFIG_BPF_LSM=y且lsm=包含bpf。Ubuntu 22.04+、Fedora 36+ 已默认启用。
快速启用检查清单
BPF LSM 快速启用检查清单
项目 要求 验证命令 最低内核版本 5.7( BPF_PROG_TYPE_LSM引入)uname -r内核配置 CONFIG_BPF_LSM=y,CONFIG_BPF=y,CONFIG_BPF_SYSCALL=yzgrep -E 'CONFIG_BPF_LSM\|CONFIG_BPF_SYSCALL' /proc/config.gzLSM 启动参数 lsm=列表中包含bpfcat /sys/kernel/security/lsm(应包含bpf)BTF 支持 推荐 CONFIG_DEBUG_INFO_BTF=y(CO-RE 所需)ls /sys/kernel/btf/vmlinuxbpftool 可用 用于加载和调试 eBPF 程序 bpftool prog list \| grep lsm修改启动参数(若 lsm 缺少 bpf) 编辑 GRUB 添加 lsm=lockdown,capability,landlock,yama,bpf编辑 /etc/default/grub,执行update-grub后重启
三、Falco:基于 syscall 的运行时威胁检测
如果 BPF LSM 是”手写汇编”,那 Falco 就是”高级语言”——开箱即用的安全检测框架。
架构拆解
Falco 的架构分三层:内核态驱动(数据采集)→ 用户态引擎(事件丰富化)→ 规则引擎(威胁检测)。
内核态驱动支持三种模式:
| 驱动模式 | 原理 | 性能 | 安全性 |
|---|---|---|---|
| 内核模块 (kmod) | 传统 .ko 模块,hook syscall 表 |
⭐⭐⭐ | ⚠️ 内核崩溃风险 |
| eBPF probe | tracepoint 挂载
sys_enter/sys_exit |
⭐⭐⭐ | ✅ verifier 保障 |
| 现代 eBPF | 用 CO-RE + Ring Buffer 替代 perf buffer | ⭐⭐⭐⭐ | ✅ 推荐 |
生产环境推荐”现代 eBPF”模式。内核模块虽性能略好,但一个 bug 就能 panic 整个节点。
用户态引擎 libsinsp 将原始 syscall 事件丰富化——文件描述符→路径、socket→IP:Port、PID→进程树、容器→ID/image/namespace。
规则引擎使用 YAML 格式,语法类似自然语言:
# 检测容器内读取敏感文件
- rule: Read sensitive file in container
desc: 检测容器内进程读取 /etc/shadow 等敏感文件
condition: >
open_read and container and sensitive_files
and not proc.name in (shadow_allowed_procs)
output: >
容器内敏感文件被读取 (user=%user.name cmd=%proc.cmdline
file=%fd.name container=%container.name)
priority: WARNING
tags: [filesystem, container]
# 检测容器内启动交互式 shell(可能的入侵信号)
- rule: Terminal shell in container
desc: 容器内不应出现交互式 shell
condition: >
spawned_process and container
and proc.name in (bash, sh, zsh, dash)
and proc.tty != 0
output: >
容器内检测到交互式 shell (user=%user.name
container=%container.name shell=%proc.name
parent=%proc.pname)
priority: CRITICAL
tags: [shell, container, mitre_execution]性能开销
在 8 核 / 16GB 的 Kubernetes 节点上(syscall 密集型工作负载):
| 指标 | 无 Falco | Falco (eBPF) | 开销 |
|---|---|---|---|
| CPU 使用率 | 基准 | +1.5~3% | 可接受 |
| 内存占用 | — | ~150MB | 固定 |
| syscall 延迟 | 基准 | +0.5~2μs | 几乎无感 |
| 网络吞吐 | 基准 | -0.2% | 可忽略 |
Falco 的局限
快速启用检查清单
Falco 快速启用检查清单
项目 要求 验证命令 最低内核版本 4.14(eBPF 驱动模式),5.8+(推荐,现代 eBPF 模式) uname -r内核配置 CONFIG_BPF=y,CONFIG_BPF_SYSCALL=y,CONFIG_BPF_JIT=yzgrep -E 'CONFIG_BPF=\|CONFIG_BPF_SYSCALL\|CONFIG_BPF_JIT' /proc/config.gzBTF 支持(现代 eBPF) CONFIG_DEBUG_INFO_BTF=y(5.8+ 推荐)ls /sys/kernel/btf/vmlinux安装方式 Helm(K8s)或 apt/rpm(裸机) helm install falco falcosecurity/falco -n falco驱动选择 优先 --set driver.kind=modern_ebpf;回退ebpf;最后kmodfalco --version后查看Driver行验证运行 Falco 进程存活且无报错 systemctl status falco或kubectl logs -n falco -l app.kubernetes.io/name=falco规则加载检查 默认规则文件可被解析 falco -L 2>&1 \| head -20(列出所有已加载规则)
- 仅能告警,不能阻断:基于 tracepoint 只能观测,检测到攻击时进程可能已完成恶意操作
- 用户态瓶颈:syscall 频率 >100K/s 时,内核→用户态传输可能丢事件
- 规则局限:复杂关联分析(“5 秒内 A 然后 B”)难以表达
- 进程血缘不完整:依赖
/proc重建进程树,短命进程可能来不及采集
四、Tetragon:Cilium 的安全可观测性引擎
Tetragon 是 Cilium 的安全组件,设计哲学和 Falco 截然不同:把尽可能多的决策下沉到内核态。
架构差异
快速启用检查清单
Tetragon 快速启用检查清单
项目 要求 验证命令 最低内核版本 5.4+(基础),5.11+(完整 kprobe multi-attach),5.13+(推荐) uname -r内核配置 CONFIG_BPF=y,CONFIG_BPF_SYSCALL=y,CONFIG_BPF_JIT=y,CONFIG_DEBUG_INFO_BTF=yzgrep -E 'CONFIG_BPF=\|CONFIG_DEBUG_INFO_BTF' /proc/config.gzBTF vmlinux Tetragon 依赖 BTF 做 CO-RE ls /sys/kernel/btf/vmlinuxbpftool 验证 检查 BPF 子系统是否可用 bpftool feature probe kernel \| grep -i bpf_prog_type安装方式 Helm(推荐) helm install tetragon cilium/tetragon -n kube-system验证运行 DaemonSet 所有 Pod Running kubectl get ds tetragon -n kube-system事件验证 能收到进程事件 kubectl exec -n kube-system ds/tetragon -c tetragon -- tetra getevents -o compact \| headTracingPolicy 加载 CRD 已注册 kubectl get tracingpolicies
| 特性 | Falco | Tetragon |
|---|---|---|
| 事件采集点 | tracepoint | kprobe + LSM + tracepoint |
| 过滤位置 | 用户态 | 内核态 |
| 实时阻断 | ❌ 仅告警 | ✅
bpf_send_signal(SIGKILL) |
| 进程血缘追踪 | 用户态重建(有缺口) | 内核态维护(完整) |
| 策略定义 | YAML 规则 | Kubernetes CRD |
| 整体 CPU 开销 | 1.5~3% | 1~2% |
TracingPolicy CRD:声明式安全策略
Tetragon 的核心抽象是 TracingPolicy
CRD——声明你想在内核中观测什么、在什么条件下采取什么行动:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: block-shadow-read
spec:
kprobes:
- call: "fd_install" # 文件描述符安装到进程时触发
syscall: false
args:
- index: 0
type: int
- index: 1
type: "file" # 自动解析文件路径
selectors:
- matchArgs:
- index: 1
operator: "Equal"
values:
- "/etc/shadow"
matchActions:
- action: Sigkill # 内核态直接杀死进程
- action: Post # 同时上报事件进程血缘追踪
Tetragon 在内核态维护一棵进程树,每个事件都附带完整的进程血缘链:
# 查看 Tetragon 的进程事件
kubectl exec -n kube-system ds/tetragon -c tetragon -- \
tetra getevents -o compact
# 输出示例(每个事件附带完整进程血缘链):
# 🚀 process default/nginx-7b8d6c /bin/bash
# └── /usr/sbin/nginx → /usr/bin/containerd-shim → /usr/bin/containerd
# 📝 open default/nginx-7b8d6c /bin/bash /etc/shadow
# 💀 sigkill default/nginx-7b8d6c /bin/bash (blocked by policy)不需要额外查询就能知道:谁(哪个容器的哪个进程)→ 从哪来(完整调用链)→ 做了什么 → 结果(是否被阻断)。
内核态进程族谱追踪:task_struct 遍历与 BPF Map 进程树
Tetragon 的进程血缘追踪不是在用户态通过
/proc 拼凑的(Falco
的方式),而是在内核态实时维护一棵进程树。这是它能做到”零缺口”血缘追踪的关键。
原理拆解:
Tetragon 在以下内核事件点挂载 eBPF 程序,实时维护一个 BPF Map 形式的进程树:
| 挂载点 | 触发时机 | 动作 |
|---|---|---|
sched_process_fork tracepoint |
新进程创建 | 在 Map 中添加子节点,记录 parent_pid |
sched_process_exec tracepoint |
进程执行新程序 | 更新节点的 binary 路径、参数 |
sched_process_exit tracepoint |
进程退出 | 标记节点为已退出(不立即删除,保留血缘链) |
task_newtask kprobe |
内核创建 task_struct |
捕获完整的 namespace 信息 |
BPF Map 中进程节点的伪代码结构:
// Tetragon 进程树节点(简化)
struct process_node {
__u32 pid;
__u32 tgid;
__u32 parent_pid; // 父进程 PID
__u32 parent_tgid;
__u64 start_time; // 进程启动时间(nsec,用于 PID 回收消歧)
__u64 cgroup_id; // 容器 cgroup ID
__u32 uid, gid;
char comm[16]; // 进程名
char binary[256]; // 完整可执行文件路径
__u32 flags; // is_container, is_host, exited...
__u32 depth; // 在进程树中的深度(用于防止无限回溯)
};task_struct
遍历:当一个安全事件触发时(比如
fd_install kprobe 检测到敏感文件访问),eBPF
程序需要向上遍历进程树来获取完整血缘链。它通过
bpf_get_current_task() 拿到当前
task_struct,然后沿着
task->real_parent 指针向上走:
// 伪代码:内核态进程血缘回溯
static __always_inline int walk_process_tree(struct trace_event *event)
{
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
struct task_struct *parent;
// 最多回溯 16 层(eBPF verifier 要求有界循环)
#pragma unroll
for (int i = 0; i < 16; i++) {
__u32 pid = BPF_CORE_READ(task, tgid);
// 从 BPF Map 查找预先维护的进程信息
struct process_node *node = bpf_map_lookup_elem(&process_map, &pid);
if (node) {
// 把这一层的进程信息写入事件的 ancestors 数组
event->ancestors[i].pid = node->pid;
event->ancestors[i].tgid = node->tgid;
__builtin_memcpy(event->ancestors[i].comm, node->comm, 16);
__builtin_memcpy(event->ancestors[i].binary, node->binary, 256);
}
// 沿 real_parent 向上走
parent = BPF_CORE_READ(task, real_parent);
if (!parent || parent == task)
break; // 到达 init 进程或自身循环
task = parent;
}
return 0;
}跨 fork/exec 的事件关联:传统的基于
/proc
的方案有一个致命缺陷——短命进程。一个进程如果在 1ms 内完成
fork → exec → 恶意操作 → exit,用户态轮询
/proc 根本来不及看到它。Tetragon
的内核态方案没有这个问题:sched_process_fork
在进程创建的瞬间就写入 Map,sched_process_exit
在退出时标记但不删除(延迟清理),中间任何事件都能查到完整血缘链。
Tetragon 的进程树 Map 示意(实际运行中的一个容器):
PID PPID Binary Cgroup
─────────────────────────────────────────────────────
1 0 /sbin/init host
3841 1 /usr/bin/containerd host
4102 3841 /usr/bin/containerd-shim host
4156 4102 /usr/sbin/nginx k8s_nginx_default
4201 4156 /usr/sbin/nginx (worker) k8s_nginx_default
4289 4201 /bin/bash k8s_nginx_default ← 可疑!
4312 4289 /usr/bin/curl k8s_nginx_default ← 可疑!
↓
完整血缘链:curl → bash → nginx(worker) → nginx → shim → containerd
一眼就能看出:nginx worker 不应该 fork 出 bash 和 curl
这个设计的代价是内存占用更高(每个进程节点约 300-400 字节,加上 Map 的固定开销),这也是为什么 Tetragon 的内存占用(~200MB)比 Falco(~150MB)略高。但换来的是零缺口的进程血缘追踪——在安全场景下,这个 tradeoff 非常值得。
实时阻断:内核态 vs 用户态的本质差距
Falco(用户态决策):事件传到用户态 → 规则匹配 →
输出告警——攻击可能已完成。Tetragon(内核态决策):eBPF
程序在 hook 点直接检查 →
bpf_send_signal(SIGKILL) →
攻击在内核态就被终止。
性能对比
各安全方案在 Kubernetes 集群(16 核 / 64GB 节点,混合工作负载)上的表现:
| 指标 | Auditd | Falco (eBPF) | Tetragon | eBPF-LSM |
|---|---|---|---|---|
| CPU 开销 | 3~5% | 1.5~3% | 1~2% | 0.5~1% |
| 内存占用 | ~80MB | ~150MB | ~200MB | ~10MB |
| 事件延迟 | ~10μs | ~3μs | <1μs | <0.5μs |
| 阻断能力 | ❌ | ❌ | ✅ | ✅ |
| 丢事件风险 | 高 | 中 | 低 | 极低 |
| 部署复杂度 | 低 | 中 | 中 | 高 |
Auditd 的高 CPU 开销来自其同步的日志写入机制。Falco 和 Tetragon 都使用异步 buffer,overhead 更可控。
五、实战:构建容器逃逸检测
以下使用 Tetragon TracingPolicy 构建一套实际的容器逃逸检测方案。
检测 nsenter / unshare 调用
nsenter 和 unshare
是容器逃逸最常见的手段:
# detect-namespace-escape.yaml
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: detect-namespace-escape
spec:
tracepoints:
- subsystem: "sched"
event: "sched_process_exec"
args:
- index: 0
type: "nop"
- index: 1
type: "string" # 执行的程序路径
selectors:
- matchArgs:
- index: 1
operator: "Postfix"
values:
- "/nsenter"
- "/unshare"
matchActions:
- action: Sigkill # 立即终止
- action: Post
rateLimit: "1m"检测敏感文件访问
# detect-sensitive-file-access.yaml
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: detect-sensitive-file-access
spec:
kprobes:
- call: "fd_install"
syscall: false
args:
- index: 0
type: int
- index: 1
type: "file"
selectors:
- matchArgs:
- index: 1
operator: "Prefix"
values:
- "/etc/shadow"
- "/etc/sudoers"
- "/proc/sysrq-trigger"
- "/proc/kcore"
matchNamespaces:
- namespace: Pid
operator: NotIn
values:
- "host_ns"
matchActions:
- action: Post
rateLimit: "30s"
- matchArgs: # /proc/*/mem 直接阻断
- index: 1
operator: "Postfix"
values:
- "/mem"
matchActions:
- action: Sigkill
- action: Post检测异常网络连接
# detect-suspicious-network.yaml
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: detect-suspicious-network
spec:
kprobes:
- call: "tcp_connect"
syscall: false
args:
- index: 0
type: "sock"
selectors:
- matchArgs: # SSRF → 直接杀掉
- index: 0
operator: "DAddr"
values:
- "169.254.169.254" # 云元数据端点
matchActions:
- action: Sigkill
- action: Post
- matchArgs: # K8s API → 仅告警
- index: 0
operator: "DPort"
values:
- "6443"
- "10250"
matchActions:
- action: Post
rateLimit: "1m"部署完整方案
# 安装 Tetragon 并应用策略
helm repo add cilium https://helm.cilium.io
helm install tetragon cilium/tetragon -n kube-system \
--set tetragon.btf=/sys/kernel/btf/vmlinux
kubectl apply -f detect-namespace-escape.yaml \
-f detect-sensitive-file-access.yaml \
-f detect-suspicious-network.yaml
# 实时查看安全事件
kubectl exec -n kube-system ds/tetragon -c tetragon -- \
tetra getevents -o compact --process-ancestors 3
# 测试:容器内尝试读取 /etc/shadow
kubectl exec -it test-pod -- cat /etc/shadow
# 预期:command terminated with exit code 137 (SIGKILL)六、选型指南:Seccomp vs eBPF-LSM vs Falco vs Tetragon
终极对比表
| 维度 | Seccomp-BPF | eBPF-LSM (自定义) | Falco | Tetragon |
|---|---|---|---|---|
| 最低内核版本 | 3.5 | 5.7 | 4.14 (eBPF) | 5.4+ |
| 挂载点 | syscall 入口 | LSM hook | tracepoint | kprobe + LSM |
| 可见上下文 | syscall 号 + args | 完整内核对象 | syscall + 丰富化 | 完整内核对象 |
| 阻断能力 | ✅ KILL/ERRNO | ✅ 返回错误码 | ❌ 仅告警 | ✅ SIGKILL |
| 进程血缘 | ❌ | 需自行实现 | 部分(用户态) | ✅ 完整 |
| 容器感知 | ❌ | 需自行实现 | ✅ | ✅ 原生 |
| K8s 集成 | securityContext | 无 | Helm | CRD + Helm |
| 学习曲线 | 低 | 高 | 中 | 中 |
| 适用场景 | 基础防护 | 精确控制 | 检测 + 合规 | 检测 + 阻断 |
不同场景的推荐选择
场景 1:基础容器安全加固 → Seccomp-BPF(必选)+ Falco(推荐)
Seccomp 做最低成本的基础防护,Falco 做运行时检测,覆盖 Seccomp 看不到的上下文。
场景 2:高安全性生产环境 → Seccomp-BPF + Tetragon
Seccomp 第一层白名单过滤 + Tetragon 第二层上下文感知检测和实时阻断。
场景 3:定制化安全需求(金融、政府) → Seccomp-BPF + 自定义 eBPF-LSM + Tetragon
eBPF-LSM 实现细粒度自定义策略,Tetragon 负责全局检测和告警。
场景 4:安全审计和合规 → Falco
Falco 规则库已覆盖 MITRE ATT&CK 大量 TTP,输出格式适合对接 SIEM。
分层防御模型
最佳实践不是二选一,而是分层防御:
┌─────────────────────────────────────────┐
│ 应用层安全(WAF、认证) │ ← 拦截已知攻击
├─────────────────────────────────────────┤
│ 网络策略(Cilium/Calico) │ ← 限制通信
├─────────────────────────────────────────┤
│ Seccomp-BPF(syscall 白名单) │ ← 禁止危险调用
├─────────────────────────────────────────┤
│ Tetragon/Falco(运行时行为检测) │ ← 检测异常
├─────────────────────────────────────────┤
│ eBPF-LSM(自定义安全策略) │ ← 精确控制
├─────────────────────────────────────────┤
│ Linux 内核(最小权限原则) │ ← Capabilities、NS
└─────────────────────────────────────────┘
安全是一个 纵深防御 问题。没有银弹,但 eBPF 让我们第一次能在不改内核的前提下,实现接近内核模块级别的安全监控能力。
参考资料: