一、eBPF:重新定义内核可编程性
扩展伯克利包过滤器(eBPF)最初是为网络包过滤设计的,但经过十年的演进,它已成为 Linux 内核中最具变革性的技术之一[^4]。eBPF 的核心价值可以用一句话概括:在不修改内核源码、不加载内核模块的前提下,安全地运行自定义内核代码。
这一特性对安全监控的意义是革命性的。传统的 Linux 安全监控依赖以下几种机制:
| 机制 | 粒度 | 性能开销 | 安全风险 | 灵活性 |
|---|---|---|---|---|
| Auditd | 系统调用 | 中 | 低 | 低(固定规则) |
| LSM (AppArmor/SELinux) | 强制访问控制 | 低 | 低 | 中(策略语言限制) |
| Kernel Module | 任意 | 低 | 高(可崩溃内核) | 高 |
| eBPF | 内核任意 hook 点 | 极低 | 极低(Verifier 保证) | 高 |
eBPF 通过内核验证器(Verifier)在加载前对程序进行静态分析,确保程序不会陷入无限循环、不会访问非法内存、不会泄露内核数据。这种"安全沙箱"特性使得 eBPF 程序可以在生产环境中放心部署,而传统内核模块的崩溃风险被彻底消除。
2026 年的 eBPF 生态已经极其丰富[^1]:Cilium 基于 eBPF 重构了 Kubernetes 的网络和安全策略,Falco 和 Tetragon 使用 eBPF 实现运行时安全监控,Pyroscope 和 Parca 利用 eBPF 进行无侵入的持续性能分析。对于安全工程师而言,理解 eBPF 已从"加分项"变为"必备技能"。
二、eBPF 安全监控的三种武器
2.1 系统调用追踪:kprobe/fentry 与 tracepoint
系统调用是用户空间与内核交互的唯一大门,也是监控进程行为的最强信号源。eBPF 可以通过 kprobe(内核函数探针)、fentry(函数入口探针)和 tracepoint(静态追踪点)三种方式挂钩系统调用。
tracepoint 是首选机制,因为它基于内核预定义的静态追踪点,接口稳定,不会因内核版本变化而失效。Linux 内核为每个系统调用都定义了 sys_enter_<syscall> 和 sys_exit_<syscall> tracepoint。
以下是一个使用 libbpf 和 CO-RE(Compile Once, Run Everywhere)的 eBPF 程序示例,监控 execve 系统调用:
// exec_monitor.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
char LICENSE[] SEC("license") = "GPL";
struct event {
u32 pid;
u32 ppid;
u64 timestamp;
char comm[16];
char filename[256];
};
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} rb SEC(".maps");
SEC("tp/syscalls/sys_enter_execve")
int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter *ctx)
{
struct event *e;
const char *filename = (const char *)ctx->args[0];
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
if (!e)
return 0;
e->pid = bpf_get_current_pid_tgid() >> 32;
e->ppid = bpf_get_current_task()->real_parent->tgid;
e->timestamp = bpf_ktime_get_ns();
bpf_get_current_comm(&e->comm, sizeof(e->comm));
bpf_probe_read_user_str(&e->filename, sizeof(e->filename), filename);
bpf_ringbuf_submit(e, 0);
return 0;
}这个程序使用 BPF ringbuf 将事件高效地传输到用户空间,避免了旧版 perfbuf 的内存拷贝开销。CO-RE 机制通过 BTF(BPF Type Format)信息,使得编译后的 BPF 程序可以在不同内核版本上运行,无需针对每个版本重新编译。
用户态程序使用 libbpf 加载并消费事件:
// exec_monitor.c
struct exec_monitor_bpf *skel;
struct ring_buffer *rb;
skel = exec_monitor_bpf__open_and_load();
exec_monitor_bpf__attach(skel);
rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL);
while (!exiting) {
ring_buffer__poll(rb, 100);
}2.2 文件系统监控:LSM BPF 与文件操作追踪
Linux 5.7 引入了 LSM BPF,允许 eBPF 程序挂载到 LSM(Linux Security Modules)的 hook 点上,如 security_file_open、security_socket_connect 等。这提供了比系统调用更高语义层级的安全事件。
以下是一个 LSM BPF 示例,监控敏感文件的访问:
SEC("lsm/file_open")
int BPF_PROG(restrict_file_open, struct file *file, int ret)
{
char buf[64];
struct dentry *dentry = file->f_path.dentry;
bpf_probe_read_kernel_str(buf, sizeof(buf),
BPF_CORE_READ(dentry, d_name.name));
// 监控对 /etc/shadow 的访问
if (bpf_strncmp(buf, 6, "shadow") == 0) {
u32 pid = bpf_get_current_pid_tgid() >> 32;
bpf_printk("PID %d accessed /etc/shadow", pid);
// 可在此处实现实时阻断:返回 -EPERM
// return -EPERM;
}
return 0;
}LSM BPF 的独特价值在于它可以在安全决策点直接返回错误码,实现内核级的实时阻断。例如,Tetragon 利用这一能力,在检测到恶意进程时直接调用 bpf_send_signal() 发送 SIGKILL,从内核层面终止威胁[^6]。
2.3 网络监控:XDP 和 tc BPF
eBPF 在网络层的应用包括 XDP(eXpress Data Path)和 tc(traffic control)BPF 程序。XDP 在网卡驱动层(甚至硬件层)处理数据包,延迟最低;tc BPF 在内核网络栈的更高层运行,灵活性更强。
安全场景中的典型应用:
- DDoS 缓解:XDP 程序在数据包进入内核协议栈之前就丢弃恶意流量
- 网络微分段:Cilium 使用 eBPF 替代 iptables,实现基于身份(而非 IP)的细粒度网络策略
- C2 检测:监控出站连接的域名和 IP,匹配威胁情报库
三、从工具到平台:构建 eBPF 安全监控体系
单点的 eBPF 工具(如 BCC 的 execsnoop、opensnoop)适合故障排查和临时分析,但生产环境的安全监控需要一个可管理、可扩展的平台。2026 年的主流选择是 Falco 和 Tetragon。
3.1 Falco:规则驱动的运行时检测
Falco 是 CNCF 孵化项目,使用 eBPF(或内核模块)监控系统调用,通过规则引擎匹配异常行为[^7]。
Falco 规则示例——检测容器内运行交互式 shell:
- rule: Terminal Shell in Container
desc: >
Detects an attempt to spawn a shell in a container.
This is a common pattern for attackers who gain
initial access and want to explore the environment.
condition: >
spawned_process
and container
and shell_procs
and not user_known_exec_shell_condition
output: >
Interactive shell spawned in container
(user=%user.name command=%proc.cmdline
container=%container.name id=%container.id)
priority: NOTICEFalco 的规则语言支持丰富的条件表达式,包括进程属性、用户属性、容器元数据、文件路径模式等。规则可以动态热加载,无需重启代理。
Falco 的架构优势在于与云原生生态的深度集成:
- 自动识别 Kubernetes Pod 和 Namespace 上下文
- 支持 gRPC 输出到 Falco Sidekick,触发 Webhook、Slack 告警、AWS Lambda 等
- 插件系统支持从 AWS CloudTrail、Okta、GitHub 等非内核来源摄取事件
Falco 的局限在于它的检测是"事后"的——事件发生后才匹配规则并告警。对于需要实时阻断的场景(如勒索软件文件加密),Falco 的响应存在延迟。
3.2 Tetragon:内核级实时执行
Tetragon 由 Cilium 团队开发,定位是"eBPF 驱动的安全可观测性和运行时执行"[^6]。与 Falco 相比,Tetragon 的核心差异化是内核级实时阻断能力。
Tetragon 的 TracingPolicy 示例——检测并阻断容器逃逸尝试:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: "container-escape-block"
spec:
kprobes:
- call: "__x64_sys_setns"
syscall: true
args:
- index: 0
type: "int"
selectors:
- matchNamespaces:
- namespace: pid
operator: NotIn
values:
- "host"
matchActions:
- action: Sigkill这个策略监控 setns 系统调用(用于改变进程的命名空间),当检测到容器内进程尝试切换到宿主机的 PID namespace 时,立即发送 SIGKILL 终止进程。
Tetragon 的执行能力来源于 eBPF 的 bpf_send_signal() 辅助函数,该函数允许 BPF 程序向当前进程发送信号。由于 BPF 程序运行在内核上下文,信号发送是即时生效的,无需用户态介入。
3.3 选型建议:Falco vs Tetragon
| 维度 | Falco | Tetragon |
|---|---|---|
| 核心定位 | 检测与告警 | 检测+实时阻断 |
| 规则语言 | Falco 规则 YAML | TracingPolicy CRD |
| 社区生态 | CNCF 孵化,社区更大 | Cilium 生态,K8s 原生 |
| 实时阻断 | 不支持(需外部响应) | 原生支持(Sigkill/Sigstop/Overwrite) |
| 云原生集成 | 强 | 极强(Cilium 网络策略联动) |
| 学习曲线 | 中等 | 较陡(需理解内核概念) |
建议的部署策略是两者互补:Falco 覆盖广泛的检测场景和合规需求,Tetragon 负责高风险的实时阻断(如容器逃逸、特权提升、敏感文件访问)。
四、行为基线:从规则到异常
规则驱动的检测(如 Falco 的"检测容器内 shell")在已知威胁场景下有效,但面对未知威胁和 APT 攻击时存在盲区。行为基线(Behavioral Baseline)检测通过建立"正常行为"的统计模型,识别偏离基线的异常活动。
4.1 基线构建方法
基线构建需要回答:"在这个环境中,正常的行为是什么样的?"
进程执行基线:
- 每个容器/主机在正常情况下会启动哪些进程?
- 每个进程的执行频率和参数模式是什么?
- 进程树的父子关系模式(如 nginx 派生 worker 进程)
文件访问基线:
- 哪些进程会访问哪些文件路径?
- 文件访问的时间分布(工作时段 vs 非工作时段)
- 文件修改的频率和大小分布
网络连接基线:
- 每个工作负载会与哪些外部 IP/域名通信?
- 通信的协议和端口分布
- 出站流量的时间模式和字节量分布
4.2 eBPF 基线采集实现
使用 eBPF 可以高效地采集基线数据。以下是一个使用 BPF hash map 统计进程执行频率的简化示例:
struct proc_key {
u32 pid_ns;
char comm[16];
char filename[64];
};
struct proc_stat {
u64 count;
u64 first_seen;
u64 last_seen;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10000);
__type(key, struct proc_key);
__type(value, struct proc_stat);
} proc_stats SEC(".maps");
SEC("tp/syscalls/sys_enter_execve")
int trace_execve(struct trace_event_raw_sys_enter *ctx)
{
struct proc_key key = {};
struct proc_stat *stat, init = {};
key.pid_ns = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&key.comm, sizeof(key.comm));
bpf_probe_read_user_str(&key.filename, sizeof(key.filename),
(const char *)ctx->args[0]);
stat = bpf_map_lookup_elem(&proc_stats, &key);
if (!stat) {
init.count = 1;
init.first_seen = bpf_ktime_get_ns();
init.last_seen = init.first_seen;
bpf_map_update_elem(&proc_stats, &key, &init, BPF_ANY);
} else {
__sync_fetch_and_add(&stat->count, 1);
stat->last_seen = bpf_ktime_get_ns();
}
return 0;
}用户态程序定期读取 proc_stats map,构建每个工作负载的进程执行频率矩阵。当某个进程的执行频率超过基线的 3σ(三个标准差)时,触发异常告警。
4.3 异常检测规则示例
基于 eBPF 采集的行为数据,可以构建以下异常检测逻辑:
| 异常类型 | 检测逻辑 | eBPF 数据源 |
|---|---|---|
| 新进程异常 | 首次出现的进程名/路径 | execve tracepoint |
| 子 shell 异常 | 非 shell 进程派生 bash/sh | sched_process_fork |
| 敏感文件异常 | 非授权进程访问 /etc/shadow | LSM BPF file_open |
| 反弹 shell 异常 | 进程标准输入重定向到 socket | kprobe socket-related |
| 权限提升异常 | setuid/setcap 调用激增 | tracepoint sys_enter_setuid |
| 数据外泄异常 | 出站连接字节量突增 | kprobe tcp_sendmsg |
五、实战:检测 MITRE ATT&CK Linux 技术
MITRE ATT&CK 企业矩阵包含 100+ Linux 平台技术[^10]。以下是使用 eBPF 检测部分技术的映射:
5.1 检测凭证转储(T1003.008)
攻击者常通过读取 /etc/shadow 或内存转储获取 Linux 凭证。使用 eBPF LSM hook 监控敏感文件访问:
SEC("lsm/file_open")
int BPF_PROG(detect_credential_access, struct file *file, int ret)
{
struct dentry *dentry = file->f_path.dentry;
struct dentry *parent = BPF_CORE_READ(dentry, d_parent);
char name[32], parent_name[32];
bpf_probe_read_kernel_str(name, sizeof(name),
BPF_CORE_READ(dentry, d_name.name));
bpf_probe_read_kernel_str(parent_name, sizeof(parent_name),
BPF_CORE_READ(parent, d_name.name));
// 检测 /etc/shadow 或 /etc/passwd 访问
if (bpf_strncmp(parent_name, 4, "etc") == 0) {
if (bpf_strncmp(name, 6, "shadow") == 0 ||
bpf_strncmp(name, 6, "passwd") == 0) {
u32 pid = bpf_get_current_pid_tgid() >> 32;
char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));
bpf_printk("CRED_ACCESS: pid=%d comm=%s file=%s",
pid, comm, name);
// 可升级为实时阻断:return -EPERM;
}
}
return 0;
}5.2 检测反弹 Shell(T1059.004)
反弹 shell 的典型特征是进程的标准输入/输出被重定向到 socket。通过监控 dup2 系统调用可以检测这一行为:
SEC("tp/syscalls/sys_enter_dup2")
int trace_dup2(struct trace_event_raw_sys_enter *ctx)
{
int oldfd = (int)ctx->args[0];
int newfd = (int)ctx->args[1];
// 检测将 socket (stdin=0/stdout=1/stderr=2) 重定向
if (newfd <= 2 && oldfd > 2) {
struct file *file = bpf_fdget(oldfd);
if (file) {
// 检查 oldfd 是否为 socket
struct socket *sock = BPF_CORE_READ(file, private_data);
if (sock) {
u32 pid = bpf_get_current_pid_tgid() >> 32;
bpf_printk("REV_SHELL: pid=%d dup2(%d, %d)",
pid, oldfd, newfd);
}
}
}
return 0;
}六、性能与稳定性:生产部署的考量
eBPF 虽然安全,但并非零成本。生产部署需要关注以下指标:
6.1 性能开销
eBPF 程序的运行时开销主要取决于:
- 触发频率:高频事件(如
tcp_sendmsg)的 hook 比低频事件(如execve)开销更大 - 程序复杂度:Verifier 限制了循环和指令数,但复杂的 map 操作和字符串处理仍有成本
- 数据传输:ringbuf/perfbuf 的写入频率影响 CPU 和内存使用
经验法则:
- 进程执行监控(execve):< 1% CPU 开销
- 文件访问监控(openat):1-3% CPU 开销
- 网络包处理(XDP):视流量而定,通常 < 5%
6.2 内核版本兼容性
CO-RE 大大缓解了内核版本兼容性问题,但仍有以下限制:
- 内核需启用 CONFIG_DEBUG_INFO_BTF(Linux 5.2+ 支持)
- 部分 LSM hook 需要较新的内核版本(LSM BPF 需要 5.7+)
- 某些发行版(如 CentOS 7)的内核版本过旧,无法运行现代 eBPF 程序
建议企业在升级 eBPF 安全监控前,先审计目标环境的内核版本和 BTF 支持情况。
6.3 安全边界
eBPF 的 Verifier 提供了强大的安全保障,但仍有潜在的攻击面:
- Spectre 类侧信道:eBPF 程序可能利用推测执行泄露内核信息,内核通过 retpoline 和 BPF JIT 硬化缓解
- Verifier 绕过:历史上出现过 Verifier 验证逻辑缺陷,允许恶意 BPF 程序访问越界内存。保持内核更新是最佳防护
- DoS 攻击:虽然 Verifier 阻止了无限循环,但复杂的 BPF 程序仍可能消耗大量 CPU。资源限制(RLIMIT_MEMLOCK、BPF map 大小限制)是必要的防护措施
七、结论:内核可编程性的安全革命
eBPF 将 Linux 内核从一个"黑盒"转变为一个"可编程平台"。对于安全团队,这意味着:
- 前所未有的可见性:可以观测到从系统调用到网络包的每一个内核事件
- 实时响应能力:从内核层面直接阻断威胁,无需等待用户态决策
- 安全与性能的平衡:eBPF 的低开销特性使得全量监控在生产环境中成为可能
对于尚未采用 eBPF 安全监控的企业,建议从 BCC 工具集开始,先解决具体的可见性问题(如"谁在生产环境执行了 sudo"),然后逐步引入 Falco 或 Tetragon 建立系统化的运行时检测能力。
内核是操作系统的信任根。当安全监控下沉到内核层时,攻击者的绕过成本将指数级上升——这才是 eBPF 安全价值的本质。
本文创建日期:2026-06-26 | 状态:draft | 标签:eBPF, Linux内核, 安全监控, 运行时安全, 威胁检测
