Kubernetes 合规、态势与运行时安全工具
一支平台团队在发布前同时看到了三盏绿灯:镜像漏洞扫描没有高危项,准入策略允许 Deployment 创建,Pod 也全部 Ready。几小时后,节点上出现了容器进程读取宿主敏感路径的行为。复盘发现,镜像扫描回答的是已知软件包漏洞,准入回答的是提交时的对象字段,Ready 回答的是工作负载健康;没有任何一项观察了容器启动后的系统调用,更没有把事件回指到 Pod UID、镜像摘要和发布版本。
另一个现场恰好相反。安全平台持续报告控制面基准失败、工作负载缺少安全上下文、镜像含有漏洞,值班人员却无法判断哪些发现仍然有效。托管集群看不到控制面文件,旧 ReplicaSet 已经被删除,漏洞库也已更新;报告里只剩资源名和一个总分。团队拥有大量结论,却缺少目标身份、采集时刻、规则版本、原始状态和复核动作,整改只能靠猜。
先把安全结论拆回事实
Kubernetes 安全工具产生的不是同一种“风险”。kube-bench 读取节点或控制面配置并按基准判断;Kubescape、Polaris 与 kube-score 观察资源配置或渲染清单;Trivy Operator 通过控制器和扫描 Job 生成集群内报告;Falco、Tetragon、Tracee 与 KubeArmor 从内核事件、eBPF hook 或 Linux Security Module 得到运行行为。输入、观察时点和失败语义不同,分数不能直接相加。
一条可复核记录至少包含稳定资产键、观察层、采集器版本、规则或漏洞库版本、采集时间、原始事实摘要、判断、动作和复核结果。Kubernetes 名称会复用,Pod 会重建,Tag 会漂移,因此工作负载要保留 cluster、namespace、kind、UID、容器名与镜像 digest;节点事实要保留 node UID、内核 build、运行时和发行版。只有名字的告警无法可靠关联一次发布。
asset identity
-> collector + version + capability state
-> observed fact + observed_at
-> rule/database revision
-> finding or runtime event
-> owner + action + exception expiry
-> rescan/replay + closure evidence六个观察层不能互相冒名
开发侧静态扫描最早、反馈最快,适合在仓库和 CI 检查渲染后的 YAML、依赖、文件系统与镜像。它看不到目标集群的准入默认值、节点配置和启动后行为。Trivy、Grype 与 Dependency-Check可以建立这类开发门禁;同一个镜像进入集群后,持续报告的调度、保留和多租户读取属于另一套运行对象。
准入层位于 API Server 持久化前,能拒绝不合规对象,却不会证明节点符合基准,也不会追踪进程启动后的文件、网络和执行行为。策略与执行证据可沿授权策略证据模型与工具选型继续学习。制品签名、Provenance、SBOM 和信任根可参照供应链证据模型与工具选型;签名可信不等于运行行为安全。
集群态势层观察 live object、RBAC、配置和漏洞报告,节点基准层观察 kubelet、控制面组件与主机配置,运行时检测层观察实际发生的内核事件,执行约束层尝试阻止某些行为。最后一层尤其要验证原子性:终止进程不一定能撤销已经发生的写入,缺少可用 LSM 也可能让“Block”退化为仅审计。
第一次盘点先做只读资产快照
进入集群前先确认 context、身份和目标 namespace,避免把实验命令发往错误环境。普通盘点身份应先获得 get/list/watch,不能因为扫描器需要部署 DaemonSet 就直接长期绑定 cluster-admin。下面的命令不安装安全产品,只建立后续工具共享的资产基线。
kubectl config current-context
kubectl auth can-i list nodes
kubectl auth can-i list pods --all-namespaces
kubectl get nodes -o custom-columns='NAME:.metadata.name,UID:.metadata.uid,KERNEL:.status.nodeInfo.kernelVersion,RUNTIME:.status.nodeInfo.containerRuntimeVersion'
kubectl get pods -A -o json > cluster-pods-snapshot.json预期证据不是“命令成功”四个字,而是当前 context、授权判断、节点 UID/内核/运行时,以及带 Pod UID、ownerReferences、容器状态和 imageID 的结构化快照。imageID 通常携带 digest,比声明中的可移动 Tag 更适合作为关联键。快照可能含环境变量来源、镜像地址、节点名和业务标签,应存入受控目录,设置保留期,禁止直接贴进公开工单。
如果 list nodes 返回 Forbidden,先保留响应中的 user、verb、resource 与状态码,再由平台角色执行节点盘点;不要用个人管理员凭证绕过。若托管服务隐藏控制面节点,则把 visibility=managed-control-plane-unavailable 写入覆盖记录。不可见是证据缺口,不是通过项。
用正反实验校验观察能力
正向实验选择隔离 namespace 和无敏感数据工作负载,记录一个满足团队安全配置的 Deployment。静态工具应对同一份渲染清单给出稳定结果,集群态势工具应关联 live object UID,运行时传感器应在执行受控命令时产生带 namespace、Pod、容器和进程字段的事件。各层结果通过统一 test_case_id 关联,但保留各自原始输出。
反向实验故意移除 runAsNonRoot 或增加危险 capability,让静态与态势层产生明确发现;随后在专用测试容器执行一个无破坏性的受控动作,验证运行时层是否观察到。判断标准是“预期层产生证据,其他层不冒领能力”:静态扫描没有内核事件很正常,运行时传感器没有评价 YAML 字段也很正常。
apiVersion: apps/v1
kind: Deployment
metadata:
name: evidence-lab
namespace: security-lab
labels:
security.example.com/test-case: posture-negative
spec:
replicas: 1
selector:
matchLabels: { app: evidence-lab }
template:
metadata:
labels: { app: evidence-lab }
spec:
containers:
- name: app
image: nginx:1.27-alpine
securityContext:
allowPrivilegeEscalation: true这是一份反例输入,不应进入共享生产模板。预期证据应明确指出命中的字段、规则 ID、目标 UID 或清单位置。实验结束后删除 namespace,并确认相关报告、扫描 Job、事件缓存和导出文件按预期消失或进入受控归档;若发现仍然存在,先判断它是历史证据还是孤儿对象,不能直接改成“已修复”。
从报告回到原始状态
发现项必须回答“工具看到了什么”。配置风险要保存对象字段或渲染片段摘要;节点基准要保存检查 ID、target、实际读取路径、权限错误和不可见项;漏洞报告要保存镜像 digest、包身份、数据库更新时间与扫描器版本;运行时事件要保存 hook 或事件类型、进程树、工作负载身份、策略 revision 和丢事件指标。
报告缺失不能直接解释为安全。它可能来自 selector 没命中、扫描 Job Pending、数据库下载失败、节点传感器未覆盖、BTF/LSM 不可用、事件被过滤、队列背压或 CRD 已过 TTL。诊断时先查采集器健康和覆盖分母,再看发现数量。零发现只有在“目标可见、采集成功、规则已加载、输出未丢失”同时成立时才有意义。
把项目接入变成版本化合同
项目仓库应维护安全资产声明:业务 owner、目标 namespace、关键工作负载、镜像摘要来源、允许的规则集、例外到期时间和复核命令。平台侧为每个采集器保存 Helm values、Chart 与镜像版本、RBAC diff、节点选择器、资源 requests/limits、报告 TTL 和导出字段。两者通过稳定的 cluster ID、namespace UID、workload UID 和 release revision 连接。
CI 可以保存渲染清单与开发侧扫描结果,但集群控制器生成的 CRD、节点检查和内核事件应从集群侧导出。导出器需要去重键与幂等写入,推荐使用 asset_uid + evidence_type + rule_id + rule_revision + observed_at_bucket;原始事件另存不可变 ID,避免聚合覆盖调查线索。项目看板展示状态,不能成为唯一事实库。
权限和敏感数据随观察深度增加
清单扫描通常只需仓库读权限;集群态势需要读取 workload、RBAC、镜像拉取信息和报告 CRD;节点基准会挂载宿主配置;运行时传感器可能使用 privileged、host PID/network、BPF、tracefs 或 securityfs。权限应按采集器拆分 ServiceAccount,并用 kubectl auth can-i --list --as=system:serviceaccount:<namespace>:<name> 保存授权证据。
安全报告和事件本身也是敏感数据。命令参数可能含令牌,文件路径可能暴露租户和挂载结构,漏洞报告会披露软件版本,RBAC 发现会暴露高权限主体。采集前做字段最小化,传输启用认证与加密,查询按 namespace/tenant 授权,导出前脱敏,日志与原始证据设置不同保留期。关闭一个 Dashboard 不等于数据已经删除。
容量从覆盖分母和事件洪峰计算
态势容量由工作负载数、容器数、报告类型、扫描频率、数据库下载、Job 并发、报告大小和 TTL 共同决定。报告保存为 CRD 时会消耗 API Server 与 etcd;保存到外部存储时又引入凭证、备份、查询延迟和费用。滚动发布会短时增加 ReplicaSet 与镜像数量,不能只按稳定 Pod 数估算。
运行时容量由每节点事件率、启用的事件类型、参数长度、规则复杂度、内核缓冲、用户态队列、导出吞吐和下游索引成本共同决定。基线应同时观察 DaemonSet 覆盖率、丢事件计数、队列年龄、规则加载失败、每事件字节数和下游确认延迟。生产阈值来自本集群压测与 SLO,不应照搬一个固定 EPS 数字。
高可用是分层故障合同
DaemonSet 的高可用首先是每个符合条件的节点都有健康传感器;单节点 agent 退出时,该节点会出现观察缺口。Operator 或 controller 的多副本通常依赖 leader election,增加副本主要提升接管能力,不必然线性增加扫描吞吐。事件 relay、消息队列和 SIEM 还要分别验证断线、背压、去重与重放,不能把 Deployment replicas 当成无损合同。
故障演练应分别停止单节点 agent、leader、扫描 Job 调度、漏洞数据库入口和事件下游,记录已有策略是否继续执行、新对象是否被扫描、事件是否丢失、恢复后是否补偿。检测与执行也要拆开:聚合服务不可用可能只影响集中可见性,节点侧已经加载的约束是否持续生效必须用受控动作确认。
升级和退出都要核销残留状态
升级前固定 Chart、镜像、规则、漏洞库和 CRD schema,先在一组节点或一个测试集群回放正反实验,再观察覆盖、丢事件、扫描队列和报告差异。Helm 回滚能恢复部分 Deployment 与 values,却不能自动保证 CRD、内核程序、LSM profile 和外部索引可逆。出现 schema 或规则语义变化时应并行导出新旧结果,分类差异后再切换事实源。
退出顺序从停止新增判断开始:先冻结规则与例外变更,切走消费方,关闭执行策略,验证业务恢复,再卸载传感器和控制器。随后检查 DaemonSet Pod、webhook、CRD/finalizer、ClusterRoleBinding、TLS Secret、hostPath 文件、BPF pin、LSM profile、外部 topic/index、对象存储和云账单。删除 namespace 只是中间动作,最终证据是所有节点不再执行旧策略、所有读取凭证已撤销、保留数据按计划迁移或销毁。
沿问题进入十九个学习单元
第一次选型先读安全证据分层与工具选型。节点与配置方向进入基准、态势与集群扫描工具,再分别学习 kube-bench、Kubescape、Polaris、静态清单体检、Trivy Operator 与整改闭环。运行行为方向进入运行时检测与执行约束工具,再按 Falco、Tetragon、Tracee、KubeArmor 的事件源和执行能力选择。最后由安全运营与生命周期治理收束发现归一、事件响应以及传感器容量、升级和退出。
遇到 Pod 启动、Event、日志、exec 或 EndpointSlice 证据缺失时,应回到Kubernetes 日志、事件、Exec 与端口转发补齐工作负载排障链。Kubernetes 官方的 审计日志、RBAC 和 Pod Security Standards提供平台基础事实;Falco、Tetragon、Tracee 与 KubeArmor文档则用于核对目标版本的内核、事件与执行能力。
