阶段三 · eBPF:让内核可编程

运行时安全:Tetragon 与 Falco

一句话总结

CI 阶段的扫描(左移安全)只能保证"上线前这个镜像是干净的", 但管不了"运行时被攻破". 运行时安全负责在容器跑起来之后盯着它: Falco 和 Tetragon 借助内核视角(eBPF)实时发现"容器里冒出了不该有的进程, 读了不该读的文件, 连了不该连的地址", 并能告警甚至当场拦截.

前置回顾

第 04 篇说安全是 eBPF 的第二大主场, 本篇兑现它. 同时这一篇和 CI/CD 系列第 04 篇"质量门禁与安全扫描" 是一对--那篇讲的是"上线"的安全(左移), 本篇讲"运行"的安全. 两者合起来才是完整的容器安全, 缺一不可.

一个真实的安全缺口: 扫描全过了, 然后呢?

回顾 CI/CD 第 04 篇, 我们在流水线里装了一堆门禁: 测试, SAST, 依赖扫描, Trivy 镜像扫描, 签名... 镜像干干净净才放行上线. 听起来很安全了? 看这个场景:

镜像通过了所有扫描, 上线了. 然后应用有一个扫描没发现的 0day 漏洞, 被远程利用. 攻击者拿到容器里的 shell, 开始:

  • 执行 /bin/sh, 下载挖矿程序
  • 读取 /etc/shadow, 翻找挂载进来的密钥文件
  • 向一个陌生的外部 IP 发起连接(C2 回连)
  • 尝试提权, 横向移动到其他容器

CI 阶段的扫描对此完全无能为力--它的工作在上线那一刻就结束了. 镜像"出厂时干净", 不代表它"运行时不会被搞脏".

这就是左移安全的盲区: 它是静态的, 一次性的(检查制品本身), 而攻击是动态的, 持续的(发生在运行时). 要堵这个缺口, 需要有人在容器运行的全程盯着它的行为--这就是运行时安全(Runtime Security).

左移安全像机场安检: 登机前查有没有带违禁品(上线前扫镜像).
运行时安全像航班上的空警: 就算安检时是清白的, 飞行途中要是动手动脚(运行时异常行为), 空警当场制止.
安检再严, 也不能取代飞行途中的盯防--两者管的是不同阶段.

运行时威胁模型: 该盯什么

运行时安全要盯的, 是那些"正常容器不该做, 但被攻破后会做"的行为. 归纳起来几类高危信号:

可疑行为 为什么危险
容器内启动了意外的进程(如 shell, nc, 挖矿程序) 一个正常跑 Java 的容器, 突然 exec 出 /bin/bash, 八成被入侵了
读写敏感文件(/etc/shadow, 密钥, 挂载的 secret) 正常业务不会碰这些, 碰了就是在窃取凭据
发起异常网络连接(连陌生外部 IP, 非常规端口) 可能是 C2 回连, 数据外泄
提权行为(获取额外 capability, 写敏感系统路径) 攻击者在试图突破容器边界

运行时安全的底层逻辑是异常检测: 一个容器化的应用, 行为通常是高度可预测的(一个 nginx 容器永远在做 nginx 该做的事, 不会突然去读密码文件). 所以可以为它建立"正常行为基线", 一旦出现基线外的行为(冒出新进程, 碰敏感文件, 连陌生地址), 就高度可疑.

这种可预测性, 正是容器(相比传统多用途服务器)在安全上的一个红利--容器越是"单一职责, 不可变", 异常就越容易被识别.

怎么"看见"这些行为: 从系统调用入手

容器里发生的一切--执行进程, 读文件, 建连接--最终都要通过系统调用(syscall) 请求内核完成. 所以"监控容器行为"本质上就是"监控它发起了哪些系统调用". 这正好是 eBPF(第 04 篇)的主场: 它能挂在系统调用的钩子上, 实时看到每一次调用. 两个代表工具走了不同的路:

Falco: 运行时安全的"事实标准"

Falco(CNCF 项目, 运行时安全领域的先驱)的模型: 监控系统调用 → 用一套规则匹配 → 命中可疑规则就告警. 它的规则是声明式的, 人类可读的:

# Falco 规则示意: 容器里启动了 shell 就告警
- rule: Terminal shell in container
condition: >
spawned_process and container
and shell_procs # 启动的是 shell 类进程
output: "容器内检测到 shell (容器=%container.name 进程=%proc.cmdline)"
priority: WARNING
  • 强项: 成熟, 规则生态丰富(自带大量开箱即用的检测规则), 社区广泛
  • 定位: 主要是检测与告警(detection)--发现可疑行为, 发出警报, 让我们/系统去响应

Tetragon: eBPF 原生, 不止检测还能拦截

Tetragon(Cilium 团队出品, eBPF 原生)更进一步. 它同样基于 eBPF 监控系统调用/进程/网络等, 但有两个突出特点:

  • 深度 K8s 感知: 它天然理解 K8s 上下文--告诉我们的不是"某进程", 而是"哪个 namespace, 哪个 Pod, 哪个 Label 的容器"做了什么, 排查直接对上 K8s 资源.
  • 不止检测, 还能内核态拦截(enforcement): 这是关键区别. Falco 偏"发现并告警"; Tetragon 能利用 eBPF 在内核里直接阻断违规行为--比如"任何容器试图执行挖矿程序, 内核态当场杀掉该进程", 而不只是事后告警. 在内核态拦截, 速度快, 绕不过去.
Falco Tetragon
底层 系统调用监控(支持 eBPF 等驱动) eBPF 原生
核心能力 检测 + 告警(detection) 检测 + 内核态拦截(enforcement)
K8s 感知 有(关联容器/Pod) 很强(深度对应 K8s 身份)
生态 成熟, 规则丰富, 先驱 较新, 与 Cilium 生态一体

检测 vs 拦截: 一个重要的权衡

"能拦截"听起来当然比"只告警"强, 但拦截是双刃剑: 规则一旦误判, 拦截会直接打断正常业务(杀错进程, 阻断正常连接), 后果可能比漏报还严重.

所以实践中常常先用"检测/告警"模式跑一段时间, 观察规则会不会误报, 摸清业务的正常行为基线, 确认规则足够准了, 再对最确定的高危行为开启拦截. 这和可观测性系列"告警要先准再严", CI/CD"门禁不是越严越好"是同一种工程审慎--自动拦截的前提是低误报.

和左移安全是互补, 不是替代

回到开篇: 运行时安全不是要取代 CI 阶段的扫描, 而是补上它管不到的那一段. 把容器安全的完整时间线画出来:

├──────── 上线前(左移, CI/CD 第04篇)────────┤├──── 运行时(本篇)────┤
SAST · 依赖扫描 · 镜像漏洞扫描 · 签名 Falco/Tetragon 持续盯防
─────────────────────────────────────── ─────────────────────
保证"出厂时是干净的" 保证"运行时被搞脏了能发现/拦截"
静态, 一次性 动态, 持续
╲ ╱
╲ 二者缺一不可 ╱
╲ 左移防"带病上线" ╱
╲ 运行时防"上线后中招" ╱

纵深防御: 多道独立的防线

这体现了安全的核心原则--纵深防御(Defense in Depth): 不指望单一防线万无一失, 而是叠加多道独立的防线, 任何一道被突破, 还有下一道. 左移扫描, 运行时检测, 网络零信任(网络存储篇第 05 篇), 最小权限... 每一道都不完美, 但叠在一起, 攻击者要全部突破才能得手.

运行时安全是这套纵深防御里"最后, 也最贴近实战"的一道--因为它盯的是攻击真正发生的那一刻.

快速回顾

  • 左移安全的盲区: CI 扫描是静态, 一次性的(管制品), 管不了运行时被攻破; 镜像"出厂干净"不等于"运行时不被搞脏"
  • 运行时威胁模型: 盯"正常容器不该做"的行为--意外进程(shell/挖矿), 读敏感文件, 异常外连, 提权
  • 核心思路: 容器行为高度可预测, 建立正常基线, 偏离即可疑; 底层靠监控系统调用(eBPF 主场)
  • Falco: 运行时安全先驱, 系统调用+规则匹配, 偏检测告警, 规则生态成熟
  • Tetragon: eBPF 原生, 深度 K8s 感知, 能在内核态拦截(不只告警)--但拦截需先低误报, 通常先检测后拦截
  • 与左移互补: 左移防"带病上线", 运行时防"上线后中招", 同属纵深防御的多道防线

有了运行时安全, 是不是 CI 阶段的扫描就可以省了?

绝对不行, 这是危险的误解. 两者是不同阶段, 互补的: 左移扫描成本低, 能在问题进入生产之前就拦掉(修复成本最低, 见 CI/CD 第 04 篇"安全左移"), 还能挡掉大量已知漏洞; 运行时安全成本更高, 且是"亡羊补牢"性质(攻击已经发生才检测).

正确的策略是两者都做: 左移尽量在上线前消灭已知风险(减少运行时要应对的攻击面), 运行时兜住左移漏掉的, 和上线后才出现的(0day, 配置漂移, 内部威胁). 省掉任何一道, 纵深防御就塌一层.

运行时监控所有系统调用, 性能开销大吗?

正因为基于 eBPF(第 04 篇), 开销被控制得相当低--eBPF 在内核态高效运行, JIT 编译, 远比"把所有 syscall 拷到用户态再分析"的老办法轻. 但开销并非零: 监控的范围越广(盯多少种 syscall), 规则越多越复杂, 开销越大.

实践中要聚焦高价值的监控点(进程执行, 敏感文件, 网络连接这几类高危行为), 而不是无差别监控一切--这又呼应可观测性系列"采得准而非采得多". 合理配置下, 运行时安全的性能代价对绝大多数业务可接受, 远低于它防住一次入侵的价值.

动手练习

  1. 体验运行时检测: 在测试集群装 Falco, 然后在某个 Pod 里手动 kubectl exec 进去开个 shell, 观察 Falco 是否报出"容器内检测到 shell"--亲眼看到运行时检测.
  2. 敏感文件访问测试: 在容器里读一下 /etc/shadow 或访问一个挂载的 secret, 看检测工具是否捕获这类敏感文件访问.
  3. 互补性分析: 对照 CI/CD 第 04 篇, 列出"左移扫描能发现但运行时发现不了"的问题, 和"运行时能发现但左移发现不了"的问题各两个, 体会二者互补.
  4. Tetragon 策略实验(进阶): 装 Tetragon, 配一条策略: 检测容器内执行某类进程; 先用检测模式跑, 观察会不会误报, 再考虑是否开拦截--亲身体会"先准后严".
  5. 正常行为基线: 为自己负责的一个服务画出它的"正常行为基线"(会起哪些进程, 读哪些文件, 连哪些地址), 想想哪些行为一旦出现就该立刻告警.