基准、态势与集群扫描工具
一家平台团队在合并请求里把 Deployment 扫描到零高危,镜像也没有阻断级漏洞;上线后的集群报告却持续提示容器可提权、ServiceAccount 权限过宽。排查后发现,仓库里的 YAML 经过 Helm 渲染、准入变更和运维热修后,已经不是 API Server 中保存的对象。CI 的绿色结论没有错,只是它证明的是提交时那份文件,而不是正在运行的资源。
另一支团队每周运行节点基准审计,worker node 的自动检查大多通过,于是把整个托管集群标成“符合 CIS”。真正接受请求的控制平面由云厂商管理,扫描 Pod 既看不到 API Server 启动参数,也读不到 etcd 文件;那些目标根本没有证据。安全总分看起来稳定,缺失的控制面却被错误地折算成了绿色。
先把“扫描通过”拆成可回答的问题
Kubernetes 安全扫描并不是一个产品按钮,而是几种证据采集方式。静态清单工具读取 YAML 或 Helm 渲染结果,适合在改动进入集群前发现明显配置风险;集群态势工具读取 API Server 中的 live object,把命名空间、工作负载、RBAC 和框架控制关联起来;节点基准工具进入具体主机,读取进程参数、文件权限和 kubelet 配置;Operator 类工具还会创建扫描 Job、CRD 或存储组件,持续更新漏洞与配置报告。
这些证据不能互相代替。静态文件无法证明准入后的对象,集群 API 对象无法证明主机文件权限,worker node 的检查无法证明托管控制面,镜像漏洞也无法说明容器此刻采用了什么权限运行。一个可信结论必须回答:扫描了哪个资产、使用哪组规则、观察哪个时间点、获得什么原始证据、哪些检查不可执行,以及整改后是否以同一语义重新验证。
用稳定资产键连接五种事实
证据链的第一步不是选择分数最高的工具,而是定义资产身份。工作负载至少使用 cluster / namespace / kind / name / uid,Pod 还要关联 owner reference 与实际镜像 digest;节点使用 cluster 与 node UID,并记录 OS image、kubelet version 和节点池;镜像发现项绑定 digest,不能只绑会漂移的 tag。规则身份应包含 tool、release、framework 或 benchmark、control/check ID 和规则内容摘要。
扫描时间也要拆开:observed_at 表示采集事实的时刻,artifact_revision 表示输入文件或镜像,resource_version 或 UID 表示 live object,rule_revision 表示解释事实的规则版本。没有这些字段,升级工具后分数变化时,团队无法判断是风险真的变化、规则新增、资产替换,还是采集面失效。
asset:
cluster: sandbox-a
namespace: checkout
kind: Deployment
name: api
uid: "<resource-uid>"
imageDigest: "sha256:<digest>"
evidence:
source: live-api
tool: "<scanner>"
toolVersion: "<fixed-version>"
ruleRevision: "<framework-or-controls-digest>"
observedAt: "T0"
result:
status: fail
controlId: "<stable-control-id>"
evidenceAvailable: true
exceptionId: null报告库可以保存这些字段的归一化副本,但原始 JSON、被扫描清单和渲染参数仍应作为不可变产物保留。归一化便于去重和分派,原始产物负责回答“工具当时究竟看到了什么”。
从仓库清单走到集群中的真实对象
开发者最容易跑通的是静态入口。先把 Helm Chart 用目标 values 渲染,或用 Kustomize 生成最终 YAML,再交给 kube-score、Polaris CLI 或 Kubescape CLI。扫描输入必须是渲染产物,而不是只有模板片段;同时保存源提交、依赖 Chart 版本、values 摘要和扫描器版本。这样失败项才能回到可评审的代码行。
部署后再读取 live object,并与渲染产物做字段级差异。默认值、mutating webhook、平台注入和人工热修都可能改变 securityContext、ServiceAccount、镜像、volume 或网络配置。仓库扫描通过而 live scan 失败时,先定位差异来源,不要立即把规则加入忽略列表。Kubescape 的 扫描文档说明了文件、目录、仓库与集群入口;Polaris 和 kube-score 更适合承担不同粒度的清单健康检查,不能因此被解释成节点审计器。
节点基准必须绑定节点、target 与配置集
kube-bench 这类工具不是扫描 Kubernetes YAML,而是根据固定 release 中的 cfg 和 controls,在特定节点执行 audit 命令。它需要观察主机进程并读取 kubelet、Kubernetes、etcd、systemd 或 CNI 配置,因此结果必须绑定 node UID、benchmark、target、controls 摘要和实际挂载。只保存 PASS/FAIL 汇总会丢掉最重要的采集上下文。
自建集群通常需要分别覆盖 control plane、etcd、worker node 和 policy 类检查。托管集群的 worker Job 看不到 provider 持有的控制面主机,此时正确状态是“缺少提供方证据”,不是通过,也不是自动失败。详细的 target 映射、只读 hostPath 和人工检查处理可继续进入 kube-bench 节点与控制面 CIS 基准审计。
集群漏洞报告有生命周期而不是永久结论
Trivy Operator 一类控制器会监听工作负载,创建扫描任务并把结果写成 Kubernetes 自定义资源。它解决的是“集群现在运行哪些制品、关联哪些漏洞或配置发现”,与开发侧一次性 Trivy CLI 的文件系统或镜像扫描不是同一运行面。报告会受到漏洞数据库更新时间、扫描 Job 成功率、工作负载变更、CR 保留策略和命名空间读取权限影响。
因此 VulnerabilityReport 或同类 CR 的存在不等于结果新鲜。接入时要观察报告 owner、artifact digest、scanner version、database update、creation timestamp 和最近一次扫描状态;工作负载已经换 digest 而报告仍指向旧制品时,应标为陈旧证据。大量工作负载同时变化会产生 Job 峰值、镜像拉取和 API 写入压力,扫描并发与报告保留都要进入集群容量预算。
一次正反实验先验证证据边界
在一次性测试集群准备一个最小 Deployment,仓库版本显式设置非 root、禁用 privilege escalation,并固定镜像 digest。先扫描渲染后的 YAML,再部署并读取 live object,最后运行适合该环境的集群扫描。正向判据不是“总分为某个数字”,而是三份产物能绑定同一工作负载,关键控制项的输入、规则版本和证据来源清楚,集群对象与渲染结果差异可解释。
反向实验可以通过一个测试用 mutating webhook 或测试环境热修,仅改变 live object 的安全字段而不改仓库文件。预期是静态扫描仍描述旧输入,集群扫描指出 live object 的新风险,差异产物明确显示字段来源。随后恢复对象并重扫,确认发现项关闭或转为新 revision;删除测试命名空间、临时 webhook 和报告产物,避免实验配置继续影响其他工作负载。
helm template demo ./chart -f values-sandbox.yaml > rendered.yaml
kubectl apply -f rendered.yaml
kubectl -n posture-lab get deploy demo -o yaml > live.yaml
# 用选定的静态扫描器扫描 rendered.yaml,再用集群入口检查 live object。
# 预期保存 source revision、live UID、规则版本、原始结果和字段 diff。
kubectl delete namespace posture-lab --ignore-not-found这个实验验证的是证据来源能否区分,不要求工具给出相同结论。若两边结果不同却无法定位输入差异,流水线还没有形成可审计的安全能力。
把扫描接进项目而不是堆进一个流水线步骤
仓库侧以渲染任务作为唯一输入生产者,随后并行执行清单检查、策略测试和镜像扫描;每个工具固定版本并输出机器可读产物。合并请求只对新增或恶化发现做阻断,历史基线进入有 owner 和期限的债务队列。部署侧用 revision、镜像 digest 与 live UID 回填发布记录,使同一个发布能找到代码扫描、集群态势、节点覆盖和漏洞报告。
团队需要一个发现项归一层,但不应把不同检查强行合并成一个风险。推荐去重键使用 asset identity、control identity 和 rule revision;工具间语义相近的发现可以建立关联组,由人确认等价关系。状态流至少保留 open -> acknowledged -> remediation planned -> fixed pending verification -> closed,例外则绑定批准人、理由、适用资产、到期时间和补偿控制。
失败先按采集、解释和覆盖三层分型
扫描器启动失败、下载规则失败、API 无权限、hostPath 缺失和扫描 Job OOM 都属于采集失败,不能产出安全结论。原始证据存在但规则解析、版本映射或字段类型不匹配属于解释失败。工具成功执行却没有覆盖某个节点、命名空间、target 或短生命周期工作负载,则是覆盖失败。三类故障在看板上要与真实 FAIL 分开计数。
当结果突然大幅变好,先检查资产数量、成功扫描数、规则数量、不可执行检查和报告年龄;当结果突然变差,先比较规则 revision、工具版本、渲染输入和 live object。只有输入与解释器都一致,前后趋势才有可比性。对 manual/WARN 项不能用退出码代替判断,必须附责任人、人工证据和复核时间。
高权限扫描器本身就是敏感工作负载
读取集群对象的控制器需要 Kubernetes API 权限,节点扫描器还可能需要 hostPID、root、capability 和只读 hostPath。最小化的单位不是“给安全命名空间 cluster-admin”,而是按组件拆分 ServiceAccount、ClusterRole 和资源读取范围;创建扫描 Job 的控制器不应自动获得读取全部 Secret 的能力,展示报告的用户也不应因此能读取节点 kubeconfig。
原始输出可能包含启动参数、文件路径、镜像地址、节点名、RBAC 主体和云账号信息。报告进入对象存储、日志平台或安全中心前要脱敏,并分别设置写入、查看、导出和删除权限。示例配置不得包含真实 kubeconfig、token、证书、私有仓库凭证或内部域名。短期实验使用专用 ServiceAccount 和临时凭证,清理时同时撤销 RoleBinding、Secret 与外部上传授权。
容量与成本要按采集面分别估算
静态 CLI 的成本主要是流水线 CPU、规则下载和制品保留;集群 Operator 增加 controller、扫描 Job、CRD、API 写放大和存储;节点 DaemonSet 或 Job 受节点数、调度覆盖和主机读取时间影响;漏洞扫描还受镜像大小、registry 带宽和数据库缓存影响。把这些组件合成“每集群一个扫描器”会掩盖真正瓶颈。
容量模型应观察每轮待扫描资产、扫描完成年龄、失败重试、并发 Job、CPU/内存峰值、API 限流、报告对象数、原始产物体积和日志索引量。成本不只有许可证,还包括节点资源、镜像流量、对象存储、SIEM 摄入、人工分诊和例外维护。生产阈值由集群规模、发布峰值和安全 SLO 测得,不能照搬演示环境数字。
高可用、升级与退出围绕证据连续性设计
CLI 没有常驻高可用问题,但流水线 runner 和规则源要能重试;控制器需要明确 leader election、扫描队列和存储故障时的行为;节点工具的关键指标是期望节点覆盖率,而不是某个 Pod 是否 Ready。控制器副本增加也不会自动提升扫描吞吐,底层 API、registry、Job 配额和存储仍可能是共享瓶颈。
升级时固定扫描器、Chart、规则库和数据库版本,先在影子环境对同一资产集双跑。比较新增、删除和语义变化的 control ID,再比较同定义发现;总分不可直接跨规则集画趋势。回滚必须恢复组件与规则的兼容组合,并保留升级期间的原始产物,避免旧控制器读取不了新 CRD 或新字段。
退出一种工具前先导出规则映射、资产键、开放发现、例外、原始证据、历史 owner 和保留策略,再由替代工具对同一资产建立覆盖。随后暂停新任务,删除 Job、DaemonSet、controller、Webhook、RBAC、CRD/PVC 和外部凭证,并确认日志、对象存储与安全中心中的数据按策略保留或删除。只有 Helm release 消失,不足以证明高权限入口和历史数据已经退出。
沿着证据缺口选择下一步
节点进程、文件权限、kubelet 与控制面参数需要进入 kube-bench;开发侧清单、live object、持续态势与 Operator 证据链适合进入 Kubescape;配置健康与渲染后清单可比较 Polaris 和 静态清单体检工具;集群中的漏洞 CR、扫描 Job 和报告生命周期进入 Trivy Operator。当多个工具已经运行却无法解释重复发现、例外和关闭条件时,问题转向 态势工具选型与整改闭环,而不是继续增加扫描器数量。
