Kubernetes 态势工具选型与整改闭环
一家平台团队同时运行 kube-score、kube-bench、Kubescape 和 Trivy Operator。四个页面分别给出 82 分、146 项通过、11 个风险和 37 个漏洞,管理者却无法回答“哪个工作负载必须先修”。同一个 runAsNonRoot 缺失在静态清单和集群报告中被重复建单,节点基准失败又被错误分配给应用团队。工具越多,工单越多,资产、规则、证据时间和责任边界反而越模糊。
另一家团队把高危发现全部接入工单系统,关闭率很快超过九成。复核时才发现,一部分工单因报告 TTL 到期自动关闭,一部分通过添加忽略注解消失,还有一部分修复了 Deployment 模板却没有完成滚动发布,旧 Pod 仍在运行。工单状态描述的是流程动作,不是风险事实;没有目标 UID、规则版本、原始证据和重新扫描,关闭率甚至会奖励“让发现项不可见”。
先用工程问题选择证据面
Kubernetes 安全工具的结果不能放在同一分数轴上。渲染前的模板、渲染后的清单、API Server 中的真实对象、节点与控制面配置、镜像软件包和运行时事件是不同事实。它们的采集时间、权限、失败方式和 owner 都不同。一份 Helm 模板通过静态检查,不能证明集群中没有人工漂移;节点 CIS 基准失败,也不能由修改业务 Deployment 解决。
最先决定的不是产品名,而是决策需要哪类证据。PR 阶段需要在对象进入集群前阻止明显错误,输入应是与发布一致的渲染产物。平台基线需要节点和控制面实际配置。持续态势需要观察运行中的对象变化、报告年龄和扫描失败。镜像漏洞需要绑定 digest 与数据库证据。运行时异常则依赖内核或审计事件,不能靠静态体检推断。
把一个工具部署到所有集群并不会自动补齐这些层次。真正可运营的组合通常由少量职责明确的采集器构成,每个采集器都回答“它看到了什么、没看到时如何判错、证据保存多久、谁能修复”。
建立统一发现项而不是统一总分
发现项需要一个稳定身份。最小资产键可以由 cluster / namespace / kind / name / uid / container / image_digest 组成,节点类发现改用集群、节点身份和基准 target。规则键由工具、规则 ID、规则版本或规则集 digest 组成。证据还要记录采集器版本、输入 digest、观察时间、原始结果引用与扫描状态。
下面的结构适合进入消息、数据仓库或工单适配层。它不是某个产品 CRD,而是团队自己的归一合同:
finding_id: sha256:<asset-key+rule-key>
asset:
cluster: dev-a
namespace: checkout
kind: Deployment
name: api
uid: <kubernetes-uid>
image_digest: sha256:<image-digest>
rule:
tool: trivy-operator
id: KSV012
ruleset_digest: sha256:<ruleset-digest>
evidence:
source_ref: configauditreport/<report-name>
observed_at: T0
collector_version: vX.Y.Z
state: observed
workflow:
owner: team-checkout
status: triaged
due_class: high
exception_ref: null同一资产和等价控制目标被多个工具发现时,可以聚合展示,但原始证据不能覆盖。静态检查的输入 digest 与集群报告的对象 UID 代表不同时间点;只有业务语义、资产和规则映射都一致时才能标记为重复。严重度也不应简单取平均,优先级还要结合生产暴露、可利用路径、补偿控制和修复爆炸半径。
静态清单工具把错误拦在合并之前
kube-score 和 Polaris CLI 适合读取渲染后的 Kubernetes 对象。它们无须长期控制器,就能在 CI 产生退出码、JSON、SARIF 或其他机器可读结果。关键动作是先执行与发布一致的 helm template 或 kustomize build,并把同一命名空间的 Deployment、Service、NetworkPolicy、PDB 等共同输入。只扫描单个模板文件会丢失跨对象上下文。
kube-score 的 --kubernetes-version 应显式设置为目标 API 版本;接受旧默认值会让检查与真实集群脱节。对象上的 ignore annotation 会改变结果,严格门禁可使用 --disable-ignore-checks-annotations 防止业务仓库自行绕过。Polaris 则区分 warning 与 danger,只有配合相应退出参数或 Webhook 模式才形成拒绝合同;一个页面出现 warning 并不等于流水线失败。
helm template checkout ./deploy/chart > rendered.yaml
kube-score score \
--kubernetes-version v1.36 \
--output-format sarif \
--disable-ignore-checks-annotations \
rendered.yaml > kube-score.sarif
polaris audit \
--audit-path rendered.yaml \
--format json \
--set-exit-code-on-danger > polaris.json预期正例在固定规则配置下退出为成功,并保存工具版本、输入 digest、目标 Kubernetes 版本和输出。反例删除资源请求、安全上下文或必要的配套对象,应产生稳定规则 ID 和非零退出;如果只有 warning,Polaris 仍可能成功退出,这正是需要明确写入流水线合同的行为。CI 侧一次性镜像与依赖扫描继续绑定固定制品,不能用集群态势工具的“稍后发现”取代合并前证据。
节点基准工具回答宿主和控制面事实
kube-bench 按 CIS benchmark 检查节点、etcd、控制面和 kubelet 配置。它需要把发行版、Kubernetes 版本与 target 映射到正确 benchmark 配置,并读取宿主进程参数和配置文件。自建集群可能获得完整控制面证据,托管 Kubernetes 通常看不到 provider 管理的控制面;缺失项必须标记为不可观察或由云厂商证明承接,不能算作自动通过。
Kubescape 可以同时覆盖框架扫描、集群对象、Operator 持续态势和可选节点采集,适合希望在同一生态里连接配置框架与持续发现的团队。它的 CLI、Operator、node-agent、存储与 provider 不是一个开关:启用哪些组件决定数据出站、凭证、节点权限、历史保留和容量。先用 CLI 对固定集群做基线,再决定是否承担长期控制器与存储成本,通常更容易识别真实需求。
节点工具的工单 owner 通常是平台或集群供应方。应用团队可以修复 Pod 安全上下文,却无法修改托管控制面的 API Server 参数。分责模型必须把“谁能改变目标事实”放在工具标签之前,否则中央安全队列会持续把不可执行任务抛给错误团队。
Trivy Operator 负责持续报告而不是唯一真相
Trivy Operator 监听 Kubernetes 对象,创建扫描 Job,并生成漏洞、SBOM、配置、暴露秘密、RBAC、基础设施与合规等报告 CRD。它适合回答运行对象是否发生变化、已知漏洞是否因数据库更新重新出现、集群中的配置风险是否持续存在。代价是 ClusterRole、Job 调度、私有镜像凭证、数据库供应、报告 TTL 与 etcd 容量。
一个可靠的接入必须同时采集有效报告、待扫描对象、失败 Job 和数据库年龄。报告为零只有在目标覆盖闭合、扫描完成、数据库可用且报告未过期时才有意义。Trivy Operator 与 CI 中的 Trivy CLI 通过镜像 digest 关联:CI 保存构建时证据,Operator 提供运行对象与持续重扫证据。前者不能观察绕过发布链路的对象,后者也不适合成为 PR 唯一门禁。
Trivy Operator CRD 文档列出了报告对象;配置入口用于核对 namespace selector、并发、TTL 和存储行为。选择它之前,应先证明团队愿意运营这些控制器状态,而不是只需要一次盘点。
Polaris 与 Popeye 适合当前态体检
Polaris 同时提供 CLI、Dashboard 和 Webhook。CLI 适合仓库或 CI,Dashboard 适合查看运行对象的配置、可靠性与效率建议;Webhook 会进入 API Server 准入路径,默认失败策略、TLS 与 endpoint 可用性都会影响业务创建资源。若需求只是持续发现,先采用只读 Dashboard 或定期 audit,更容易控制故障半径。自动 mutation 会改变对象字段所有权,还可能与 GitOps 控制器持续争用。
Polaris 例外可以来自配置或工作负载 annotation。允许业务自行写 annotation 会把安全决定下放给被检查者;收紧时可参考 官方 exemptions 文档,要求例外拥有 owner、原因、到期、补偿控制和复核证据。warning、danger、退出码与 Webhook 拒绝是四个不同概念,运营看板必须保留原始等级和执行模式。
Popeye 可读取运行中集群对象与部分指标,适合低成本巡检或 CronJob 补充。它不应成为强制门禁:维护节奏、宽读取权限和 --force-exit-zero 都会削弱失败合同。采用时固定版本、裁剪 ClusterRole、保存 JSON/HTML 原始输出,并区分“进程执行成功”与“发现 lint error”。其价值是补充当前态线索,不是成为新的中央分数。
用正反实验验证选型而不是比较演示页
选择工具前,准备同一组 fixture:一个满足资源、安全上下文、探针和跨对象要求的 Deployment;一个缺少这些字段的反例;一个实际集群漂移对象;一个节点或控制面基准问题;一个固定 digest 且包含已知测试发现的镜像。每个工具在它声称覆盖的输入上运行,保存版本、配置 digest、输入身份、退出码、原始输出和耗时。
正向实验要求合格 fixture 不产生目标规则失败,反例产生稳定规则 ID,修复后通过同一入口重扫并关闭原发现。反向实验还要破坏采集链:撤销 ClusterRole、让 registry 返回 429、让报告过期或给 CI 一个不完整对象集合。平台必须把这些情况标记为扫描失败、输入不完整或证据过期,而不是“零风险”。
工具对比的核心输出是一张能力与失败矩阵:每个证据面由谁采集,输入是什么,无法采集怎样暴露,原始数据保存在哪里,修复由谁执行。分数、界面美观和默认规则数量可以作为使用成本参考,不能代替这张矩阵。
让发现项经过可证明的状态机
发现项从 observed 进入 triaged,先确认资产仍存在、证据未过期、规则适用于目标环境。确认后进入 assigned,owner 必须是能修改对应事实的团队。修复提交后进入 fix_pending,等待部署和重扫;只有新对象、镜像或节点状态产生通过证据,才能进入 verified。资产下线则进入 retired,同时保存删除或替换证据。
observed -> triaged -> assigned -> fix_pending -> verified
| | |
| +-> exception_active -> exception_expired -> triaged
+-> false_positive_reviewed
+-> retired_with_asset_evidence状态转换需要不变量。verified 必须引用新的扫描证据,且资产键或其替代关系明确;exception_active 必须有未到期的审批;retired 必须证明对象确实退出,而不是报告因 TTL 消失。扫描器升级后规则语义变化,可以重新打开发现或建立新规则键,不能悄悄改写历史。
整改必须同时改变声明态和运行态
修复 Kubernetes 配置时,先改仓库中的 Helm values、Kustomize patch 或声明式清单,再通过 CI 生成渲染证据。部署完成后确认新 ReplicaSet、Pod template hash、对象 UID 与镜像 digest,最后由集群扫描器复核。只在 live object 上手工 patch 会被下一次 GitOps 同步覆盖;只改仓库不滚动 Pod,则旧运行实例仍保留风险。
漏洞整改通常通过更新基础镜像、依赖或删除不需要的软件包完成。复核必须绑定新 digest 和数据库证据。配置审计可能通过补充 runAsNonRoot、seccompProfile、capabilities、requests/limits 或 NetworkPolicy 修复,但字段是否有效还受镜像用户、CNI 能力和业务行为影响。静态规则通过后,最小业务请求、启动日志和运行状态仍要进入发布验证。
节点基准整改的爆炸半径更大。修改 kubelet、容器运行时或控制面参数前,需要逐节点或分批验证,保留回滚配置和健康判据。托管控制面不可修改时,记录 provider 责任与补偿控制,不能用一条永久忽略把未知状态变成通过。
例外不是消失按钮
例外记录至少包含资产键、规则键、风险原因、业务必要性、补偿控制、owner、审批人、到期条件和复核入口。annotation、工具配置和中央系统中的例外要有单一权威来源,再由自动化下发;否则同一规则可能在 CI 被忽略、集群报告仍告警,或集群沉默而仓库门禁继续失败。
例外到期必须重新进入 triaged,并触发重扫或重新渲染。资产变更、镜像 digest 改变、规则升级和 owner 变更也应使例外失效,因为原来的风险论证可能不再成立。高风险例外要测试补偿控制:例如允许特权 Pod 时,验证 namespace、ServiceAccount、节点池、网络出口与镜像来源是否真的受限,而不是只保存审批文字。
误报与风险接受需要分开。误报意味着规则与事实不符,应保存可重复的反证并推动规则修正;风险接受意味着事实成立但暂不修复,需要明确期限和责任。把两者都写成 ignore 会让后续规则质量和风险债务无法衡量。
敏感数据沿采集、报告和工单流动
集群扫描器可能读取工作负载清单、镜像元数据、imagePullSecret、节点配置和宿主目录。报告可能暴露软件包、漏洞、秘密位置、RBAC 路径、namespace 与内部镜像名。最小权限不仅控制谁能启动扫描,还要控制谁能读取报告、导出 Dashboard、访问对象存储和搜索工单附件。
原始证据进入中央平台前应删除 token、Secret 值、完整环境变量和不必要的内部地址。保留哈希、资源引用和定位所需字段即可。Webhook Header、registry 凭证、云 provider token 与数据库凭证通过 Secret 或工作负载身份注入,日志和错误页面不得回显。跨租户报表默认按 namespace 或租户授权,集群级节点与 RBAC 报告由专门角色读取。
数据保留期按用途区分:短期排障需要 Job 日志和事件,整改追踪需要发现项与复核证据,审计可能需要更长的不可变摘要。把所有 CRD 永久留在 etcd 成本高且影响控制面;把全部原始 YAML 长期复制到工单又会扩大敏感面。成熟方案在集群保留当前态,在受控证据仓库存必要历史,并记录来源与删除策略。
容量和成本来自扫描频率与证据基数
静态 CLI 的成本主要在 CI runner 时间、缓存和输出存储;节点基准受节点数、采集权限和维护窗口影响;持续 Operator 还承担控制器、Job、registry、数据库、API Server 与 etcd。容量模型应使用工作负载数、容器数、节点数、规则数、报告类型、平均报告大小、发布频率、TTL 和失败重试率,而不是仅按集群数量报价。
大规模滚动发布会让新 ReplicaSet 与新 digest 同时触发扫描。规则或数据库升级也可能使历史对象批量重评。通过 namespace 分批接入、限制并发、缓存数据库、错开重扫与发布窗口,可以降低突发;这些动作会延长证据收敛时间,必须由安全 SLO 定价。为了省资源而无限延长 TTL,会让新漏洞暴露变慢;为了追求新鲜度频繁全量扫描,又会挤压集群和仓库容量。
工具许可证、托管平台座席、长期日志索引和跨区域传输也是成本。选型时同时计算退出成本:规则能否导出,发现项能否迁移,历史是否使用开放格式,凭证和 Agent 是否能核销。免费安装不代表长期运营成本低。
高可用要按采集面分别设计
无状态 CLI 的可用性由 CI runner、制品存储和规则仓库保证;失败应阻止合并或明确标记检查未完成。集群 Controller 需要验证 leader election、故障域、队列恢复和重复任务幂等。Dashboard 多副本并不能保护扫描 Job,节点 DaemonSet 全部 Ready 也不能证明每个节点事件都已上传。
为每个采集面定义三条指标:目标覆盖率、有效证据年龄和采集失败率。Controller 故障恢复后,应看到积压下降、失败停止增长且目标最终收敛;只看到 Pod Ready 不足以解除事故。外部数据库、registry、对象存储和通知系统都是共享依赖,需要单独的容量、认证与恢复实验。
多集群方案还要决定中央控制面不可用时的行为。集群本地采集可以继续并缓冲,还是停止扫描;门禁是 fail closed 还是记录后放行;恢复后如何限速回放。这些取舍按证据面分别作答,不能由一个“平台 HA”标签笼统替代。
升级先比较规则结果再替换采集器
工具、Chart、规则包和数据库都可能改变发现结果。升级时用固定 fixture 和生产脱敏样本做双版本扫描,比较新增、消失、严重度变化、字段 schema、运行时间和资源占用。规则重命名要维护映射,避免旧工单全部关闭、新工单重新打开而制造虚假修复量。
控制器升级还要检查 CRD、RBAC、Webhook、leader election 和是否触发全量重扫。先在小集群或少量 namespace 灰度,观察 API 限流、Job 队列和报告写入,再扩大覆盖。回滚包必须包含旧镜像、旧 Chart values、规则包、CRD 兼容判断和凭证;若数据 schema 已前向迁移,单纯回滚 Deployment 不一定可行。
静态 CLI 升级更容易双跑。让旧版本继续决定门禁,新版本只生成比较报告;差异经过评审后再切换权威版本。这样既不把工具升级变成全仓库突发失败,也不会用永久忽略掩盖规则变化。
退出工具时归还每一类所有权
退出前先冻结新例外与新规则,把发现项、原始证据引用、规则映射、资产 owner 和未结风险导出为开放格式。为替代工具运行一段双轨期,用相同 fixture 和生产样本证明关键证据面已经接管。覆盖不是比较总数相近,而是逐类确认资产、规则、失败状态和复核能力。
随后停止外部通知和自动建单,暂停调度,卸载 Controller、CronJob、DaemonSet、Dashboard 与 Webhook。CRD、PVC、对象存储、ClusterRoleBinding、Secret、云身份和 registry token 分别核销;删除 CRD 前先确认历史证据已经迁移,因为 CRD 删除会级联删除对象。CI 中删除旧二进制、镜像、缓存和规则配置,并验证旧入口不能继续提交“成功”状态。
最后用反向实验调用旧 Webhook、旧 ServiceAccount、旧 token 与旧流水线 job,预期全部失败;再确认替代链路能从发现、分责、修复到重扫完成一次闭环。做到这一步,团队退出的才是一套工具及其隐含控制面,而不是仅仅关闭一个 Dashboard。
用决策信号收敛最终组合
渲染清单的 PR 门禁优先选择 kube-score 或 Polaris CLI,并固定目标 Kubernetes 版本、规则配置和退出码。节点与控制面 CIS 证据采用 kube-bench;需要框架、集群对象和持续态势一体化时评估 Kubescape,但要接受 Operator、存储与节点组件的运营责任。需要持续漏洞、SBOM、配置和 RBAC 报告时采用 Trivy Operator,同时把失败 Job、数据库年龄与报告 TTL 纳入 SLO。
Popeye 可以作为运行集群的轻量补充,不承担唯一门禁。已归档的 kubeaudit、KubeClarity 和 OpenClarity 不适合作为新平台基线;历史环境迁移时先导出规则、例外、SBOM、发现项和退出码合同,再寻找受维护替代品,不能假设产品名不同但证据语义一一对应。
最终组合应能回答四个问题:关键资产是否被某个采集面看见,采集失败是否比零发现更醒目,发现项是否能到达有修改权的 owner,修复后是否由新证据关闭。工具数量不是成熟度,证据闭环才是。
