运行时安全:Falco/Tetragon 与 eBPF 检测实战

从运行时威胁模型讲起,深入 eBPF 的内核钩子与检测原理、Falco 规则语法与告警管道、Tetragon 的策略执行与进程谱系、系统调用行为基线、容器逃逸与提权检测、告警降噪与响应编排,以及性能开销与内核兼容性的生产实践。

镜像扫描只能回答「这个镜像里有没有已知漏洞」,回答不了「这个进程此刻为什么在读取宿主机文件」。运行时安全(Runtime Security)关注的是容器运行起来之后的行为:谁调用了 setns、谁打开了 /proc/*/mem、谁在监听一个非预期的端口。本文从威胁模型出发,讲清 eBPF 如何在内核层采集这些信号,Falco 与 Tetragon 的规则与执行能力,以及告警降噪、性能开销与内核兼容性这些落地时绕不开的问题。


目录


1. 运行时安全威胁模型

1.1 攻击链视角

容器攻击通常遵循一条链路:初始入口 → 执行 → 权限提升 → 逃逸 → 横向移动 → 持久化。镜像扫描与准入控制只能覆盖最前面的部分,后面几步全部发生在运行时。

入口:漏洞利用 / 供应链投毒 / 配置错误 / 凭据泄露
执行:下载并运行二进制、反弹 Shell、写入 WebShell
提权:利用 SUID、内核漏洞、错误挂载的 socket
逃逸:privileged 容器、hostPath 挂载、docker.sock 暴露
横向与持久:读取 SA Token、访问 API Server、写入 CronJob

1.2 运行时安全的可观测面

观测面数据源能回答什么
系统调用eBPF / auditd谁在做什么内核操作
进程谱系execve 钩子进程从哪来、由谁派生
网络连接socket 钩子连到哪个外部地址
文件访问openat 钩子读写哪些敏感路径
容器生命周期CRI / kubelet容器何时创建与销毁

1.3 与准入控制的边界

准入控制是事前拦截(对象还没落库),运行时安全是事中发现与阻断(进程已经在跑)。两者互补:准入能拦住特权容器,但拦不住「合法容器内的合法二进制被滥用」。不要把运行时安全当作准入的替代品,也不要用准入去解决运行时问题。


2. eBPF 检测原理与内核钩子

2.1 为什么是 eBPF

传统方案要么改内核模块(风险高、维护难),要么依赖 auditd(性能差、粒度粗)。eBPF 在内核中安全地运行沙箱程序,通过验证器保证不会崩溃内核,通过 map 与用户态通信,具备三个优势:

低开销:JIT 编译后接近原生性能,可编程过滤减少数据拷贝
可观测:挂载点覆盖系统调用、内核函数、网络栈
可移植:一次编写,内核版本 4.x+ 普遍支持(需 BTF/CO-RE)

2.2 常见挂载点

类型挂载点用途
kprobe任意内核函数跟踪 openat、execve 等
tracepoint稳定内核事件点语义稳定,推荐优先
raw_tracepoint系统调用入口低开销 syscall 采集
LSM安全钩子可实现阻断
cgroup容器维度按容器过滤

2.3 容器上下文关联

eBPF 看到的是「某个 PID 调用了 openat」,必须关联到「哪个容器、哪个 Pod」。关联链路:

pid -> /proc/<pid>/cgroup -> cgroup id -> container id -> Pod (via CRI)

这就是为什么运行时安全 Agent 通常以 DaemonSet + hostPID + hostNetwork 运行:它需要看到宿主机的全部进程与 cgroup 层级,才能完成这层映射。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: runtime-agent
spec:
  template:
    spec:
      hostPID: true
      tolerations:
        - operator: Exists
      containers:
        - name: agent
          image: registry.example.com/runtime-agent:v0.9.0
          securityContext:
            privileged: true
          volumeMounts:
            - name: proc
              mountPath: /host/proc
              readOnly: true

Agent 自身是特权组件,必须单独做准入、镜像签名与 RBAC 最小化,否则它本身就是最大的攻击面。


3. Falco 规则与告警管道

3.1 规则结构

Falco 规则由「条件」与「输出」两部分组成,条件基于系统调用事件字段表达。

- rule: 容器内写入敏感目录
  desc: 检测向 /etc 或 /bin 写入的行为
  condition: >
    open_write and container
    and fd.name startswith /etc
    and not proc.name in (known_package_managers)
  output: >
    敏感目录写入 (user=%user.name container=%container.name
    file=%fd.name command=%proc.cmdline)
  priority: WARNING
  tags: [filesystem, mitre_persistence]

3.2 常用宏与列表

Falco 提供大量内置宏(container、spawned_process、outbound)与列表(shell_binaries、sensitive_mount)。复用内置宏比手写条件更可靠,因为内置宏已经处理了各种边界情况。

宏 / 列表含义
container事件发生在容器内(非宿主机)
spawned_process有新进程被创建
shell_binariessh、bash、zsh 等
sensitive_file_names/etc/shadow、/root/.ssh 等
package_mgmt_binariesapt、yum、apk 等

3.3 告警管道

eBPF 事件 -> Falco 引擎 -> 规则匹配 -> 输出通道
                                      ├── stdout(JSON)
                                      ├── gRPC(Falco Talon / 自定义消费者)
                                      ├── syslog
                                      └── webhook / Kafka / S3

推荐把告警以 JSON 输出到 stdout,由 Fluent Bit 采集后送 Kafka 或 SIEM:

falco:
  json_output: true
  json_include_output_property: true
  priority: notice
  buffered_outputs: false

3.4 规则调优三步

第一步:观察。先用默认规则集跑一周,统计各规则触发量。
第二步:收敛。把噪声最大的规则加条件收窄(加 container、加进程白名单)。
第三步:分级。按 priority 分级路由:CRITICAL 立即告警,WARNING 聚合日报。

4. Tetragon 与策略执行

4.1 Falco 与 Tetragon 的定位差异

维度FalcoTetragon
数据面eBPF + 规则引擎纯 eBPF(CO-RE)
表达方式Falco 规则 DSLTracingPolicy CRD
阻断能力需配合 Talon 等原生支持(SIGKILL / 拒绝)
进程谱系弱强(原生进程树)
可观测告警为主全量事件 + 策略

核心区别:Falco 以「告警」为中心,Tetragon 以「可观测 + 执行」为中心。

4.2 TracingPolicy 示例

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: block-write-etc
spec:
  kprobes:
    - call: "fd_install"
      syscall: false
      args:
        - index: 1
          type: "file"
      selectors:
        - matchActions:
            - action: Sigkill
          matchArgs:
            - index: 1
              operator: "Prefix"
              values: ["/etc/"]

matchActions 支持 Post(仅记录)、Sigkill(杀进程)、Override(改写返回值)、FollowFD 等。阻断能力是 Tetragon 相对 Falco 最大的差异点。

4.3 进程谱系的价值

Tetragon 会记录每个进程的父子关系与容器归属,因此可以回答「这个可疑进程是从哪个入口进程派生出来的」:

kubectl exec -n kube-system ds/tetragon -- \
  tetra getevents -o compact --processes nginx

排查反弹 Shell 时,这条谱系链比单条告警有用得多。


5. 系统调用与行为基线

5.1 为什么要基线

静态规则只能覆盖已知攻击模式,行为基线能发现「这个 Pod 第一次访问了它从未访问过的路径」这类异常。基线通常按「工作负载 + 进程」维度建立。

基线维度:namespace / workload / image / 进程名
基线内容:常见系统调用集合、文件路径集合、出网目标集合
异常判定:出现基线外的调用或路径,且频次超阈值

5.2 建立基线的流程

1. 学习期(1~2 周):只记录不告警,采集全量行为
2. 收敛期:剔除低频噪声(如健康检查、日志轮转)
3. 固化期:生成基线规则,进入告警模式
4. 维护期:镜像或版本变更后重新学习对应工作负载

5.3 用 seccomp 做强制基线

基线不仅能告警,还能变成强制约束。用运行时观测到的系统调用集合生成 seccomp profile,是「以观测驱动加固」的典型做法:先采集工作负载实际调用的系统调用集合,再生成 securityContext.seccompProfile(type: Localhost + localhostProfile)挂载到 Pod 上,把基线外的调用直接在内核层拒绝。


6. 容器逃逸与提权检测

6.1 逃逸路径与检测信号

逃逸路径检测信号
privileged 容器创建时检测 privileged 标志
hostPath 挂载宿主机根挂载路径含 /、/etc、/var/run
挂载 docker.sockopenat /var/run/docker.sock
CAP_SYS_ADMIN 滥用mount、pivot_root 系统调用
/proc 逃逸访问 /proc/1/root 或 /proc/*/mem
内核漏洞利用异常的内核函数调用序列

6.2 关键检测规则

- rule: 容器内挂载文件系统
  desc: 非特权容器不应调用 mount
  condition: >
    evt.type in (mount, umount2) and container
    and not proc.name in (allowed_mount_helpers)
  output: 容器内挂载 (container=%container.name cmd=%proc.cmdline)
  priority: CRITICAL

- rule: 访问宿主机进程内存
  desc: 读取 /proc/<pid>/mem 是典型的逃逸探测
  condition: open_read and container and fd.name glob "/proc/*/mem"
  output: 进程内存读取 (file=%fd.name cmd=%proc.cmdline)
  priority: CRITICAL

6.3 提权检测要点

SUID 二进制执行:proc.name in (sudo, su) 或发现 SUID 位文件被调用
Capability 提升:capset 系统调用携带非预期 capability
命名空间操作:setns、unshare 在业务容器中出现
内核模块加载:init_module / finit_module

注意:这些规则在 CI/CD 构建机、调试容器中会大量误报,必须用命名空间或镜像白名单收敛。


7. 告警降噪与响应编排

7.1 噪声是运行时安全的最大杀手

一个未调优的 Falco 在中等规模集群每天可产生数十万条告警,最终结果是「没人看」。降噪手段按优先级:

手段效果
白名单(进程 + 镜像 + 命名空间)消除已知良性行为
条件收窄(加 container、加路径前缀)减少宽泛匹配
聚合去重(相同规则 + 相同容器)把风暴压成一条
优先级过滤(只发 CRITICAL)控制告警量
基线比对(只报基线外行为)从源头减少

7.2 响应编排

CRITICAL -> 立即通知(PagerDuty / 企业微信)+ 自动取证(保存进程树、网络连接、文件哈希)
WARNING  -> 聚合到日报 + 关联分析
NOTICE   -> 仅入库,供事后审计

自动取证比自动阻断更值得优先建设:阻断容易误伤,而取证数据一旦丢失就无法追溯。Tetragon 的 Sigkill 只应在验证充分的规则上启用。

7.3 与响应平台联动

启用 Falco 的 gRPC 输出(grpc.enabled: true,监听本地 unix socket),由响应服务消费事件流,按规则决定:隔离 Pod(打 taint)、吊销 Token、拉取现场快照、通知值班。


8. 性能开销与内核兼容性

8.1 开销来源与量级

组件典型 CPU 开销说明
eBPF 采集(过滤前)1%~3% / 节点与系统调用频率相关
规则引擎匹配1%~5%规则数量与复杂度相关
用户态事件处理1%~3%与告警量相关
全量事件导出10%+仅调试时开启

关键结论:开销主要来自「事件量 × 规则复杂度」,而非 eBPF 本身。在内核侧做过滤(按 cgroup、按系统调用类型)能把开销压到最低。

8.2 降低开销的做法

□ 只挂载需要的钩子,不采集全量系统调用
□ 用 cgroup 过滤,跳过 kube-system 等已知命名空间
□ 关闭调试级事件导出,只保留匹配规则的事件
□ 限制 ring buffer 大小,避免内存膨胀
□ 对高频事件(如 read/write)设置采样

8.3 内核兼容性

要求一:内核版本 >= 4.14(推荐 5.4+)
要求二:BTF 支持(/sys/kernel/btf/vmlinux 存在)以便 CO-RE
要求三:开启 CONFIG_BPF、CONFIG_BPF_SYSCALL、CONFIG_DEBUG_INFO_BTF
# 检查 BTF 是否可用
ls -l /sys/kernel/btf/vmlinux
# 检查内核版本
uname -r

无 BTF 的老内核需要为目标内核编译专用探针,跨节点内核版本不一致时优先选择支持 CO-RE 的发行版(如 Ubuntu 20.04+、RHEL 8.4+)。


9. 生产最佳实践

9.1 落地 Checklist

□ Agent 以 DaemonSet 部署,独立命名空间 + 最小 RBAC
□ 先观察后告警,学习期不少于一周
□ 规则按优先级分级路由,CRITICAL 立即通知
□ 建立进程 + 镜像 + 命名空间三层白名单
□ 采集进程谱系,为取证保留上下文
□ 基线驱动 seccomp profile 加固
□ 监控 Agent 自身资源占用与事件丢弃计数
□ 规则纳入 Git,变更走评审

9.2 常见坑与对策

坑现象对策
规则未调优每天数十万告警无人看白名单 + 聚合 + 分级
Agent 无特权采集不到宿主机进程hostPID + 必要 capability
内核无 BTFAgent 启动失败升级内核或用预编译探针
全量事件导出CPU 飙升只导出匹配事件
只告警不取证事后无法追溯事件触发时保存现场
阻断规则过激误杀生产进程先 Post 观察,再 Sigkill
忽略 Agent 自身安全Agent 成为攻击面镜像签名 + 最小权限

小结

运行时安全的本质是在攻击链的中后段建立可观测性与可执行性:eBPF 提供了低开销、覆盖内核全栈的采集能力,Falco 把它变成可读的规则与告警,Tetragon 进一步把它变成可执行策略与进程谱系。落地的最大挑战从来不是技术选型,而是噪声治理——一个未调优的规则集会在两周内被彻底忽略。因此建议:先以观察模式跑满一个学习周期,用白名单与聚合把告警压到值班可承受的量级,再对少数高置信度规则开启阻断。记住:没有基线的运行时安全只是更吵的日志。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「云原生」更多文章

  1. 调度均衡:Descheduler 与资源碎片整理
  2. Cluster API 与声明式集群生命周期管理
  3. 拓扑感知路由:topologySpread、本地流量与流量亲和