kube-bench 节点与控制面 CIS 基准审计
一次托管 Kubernetes 集群的安全评审中,团队拿出 kube-bench 报告:worker node 的检查大多为 PASS,流水线退出码也是零。评审人追问 API Server、etcd 和控制面节点证据时,报告里没有失败,甚至没有对应 target。扫描 Pod 从来没有访问 provider 控制面的能力,“没有执行”却在汇总页里被看成了“没有问题”。
另一次升级把 kube-bench 二进制换成新版本,却继续挂载旧的 cfg/。同一批节点的总分突然上升,团队以为加固生效;实际变化来自 version mapping、controls 和路径定义不匹配,一部分 audit 没有按预期执行。只有二进制版本,没有 benchmark、target、controls 摘要和节点身份,漂亮的趋势图无法证明安全状态变好。
kube-bench 真正检查的对象
kube-bench 是一次运行到结束的审计程序。它读取随 release 提供的配置和 controls YAML,针对某个 benchmark 与 target 执行 audit 命令,从主机进程、文件、配置或 Kubernetes API 获得文本证据,再用 tests 判断为 PASS、FAIL、WARN 或 INFO。它不会拦截资源创建,不监控容器运行时行为,也不会自动修改节点。
因此最小审计对象不是“一个集群”,而是“固定工具 release + 同 release cfg + benchmark + target + 节点身份 + 原始输出”。CIS Kubernetes Benchmark 与 Kubernetes minor 并非一一同号,发行版还可能采用自己的 hardening guide。映射应以固定 release 的 cfg/config.yaml 为事实来源,而不是凭 Kubernetes 版本猜测。
kube-bench 官方仓库提供源码、release 与示例;安装方式见官方安装文档,命令与执行环境见运行文档。下面用 v0.15.6 组成可复核示例,团队采用其他版本时应把镜像、二进制、cfg/ 和后续命令一起替换并重新验收。
先完成无主机权限的版本冒烟
本机已有 Docker 时,可以先验证镜像身份,不需要挂载主机目录。拉取固定 tag 后记录镜像 digest,再执行 version 与帮助命令。这个步骤只证明镜像能启动且报告目标版本,不能证明它拥有目标节点所需的配置和权限,更不能产生集群合规结论。
docker pull docker.io/aquasec/kube-bench:v0.15.6
docker image inspect docker.io/aquasec/kube-bench:v0.15.6 \
--format '{{index .RepoDigests 0}}'
docker run --rm docker.io/aquasec/kube-bench:v0.15.6 version
docker run --rm docker.io/aquasec/kube-bench:v0.15.6 --help预期看到版本命令正常结束并输出 0.15.6 的版本身份,帮助中存在 run、--benchmark、--targets、--json 等入口。若镜像拉取失败,先检查 registry、代理、CA 与镜像地址;若版本与预期不一致,停止后续审计并核对 digest,不要用浮动 tag 继续。
直接安装到 Linux 节点时可使用官方 release 的 .deb、.rpm 或 tarball。tarball 方式必须同时放置同 release 的 cfg/,常见配置根目录是 /etc/kube-bench,全局映射文件位于 /etc/kube-bench/cfg/config.yaml;镜像内通常使用 /opt/kube-bench/cfg。安装用户可以拥有写入二进制与配置目录的管理员权限,日常执行账号只获得读取审计目标所需的权限,两者不应长期共用一个可写 root 会话。
kube-bench version
kube-bench --help
test -f /etc/kube-bench/cfg/config.yaml
find /etc/kube-bench -maxdepth 2 -type f | sort若只有二进制而没有 config 或 benchmark 目录,预期出现 config/benchmark not found 一类工具错误。它说明安装不完整,不是节点不合规。安装验收要把二进制摘要、配置目录摘要和 release URL 一并保存。
看懂 config、controls 与 tests 如何生成结果
全局 config.yaml 的关键作用是版本与 benchmark 映射、target 选择以及默认路径配置。固定 v0.15.6 的 cfg/config.yaml中,Kubernetes 1.32 至 1.34 映射到 cis-1.12,较早 minor 映射到相应配置目录。这个标识是 kube-bench 配置名称,报告中应原样保存,不能把它擅自改写成另一个出版物版本。
每个 target 的 controls YAML 由 group、check 和 test 组成。audit 定义要执行的命令,audit_config 或 audit_env 补充配置与环境证据,tests 解释输出;remediation 只是建议文字,scored 或 type 决定自动评分与人工判断语义。执行链中任一环缺失,结果的含义都会变化。
fixed release + cfg/config.yaml
-> Kubernetes/发行版映射到 benchmark
-> target 选择 controls YAML
-> audit 读取进程、文件、配置或 API
-> tests 解释原始输出
-> PASS | FAIL | WARN | INFO
-> remediation 供人工复核与变更设计PASS 表示自动测试实际执行且条件满足;FAIL 表示测试执行后不满足,或 scored test 无法完成;WARN 常用于人工检查,manual check 会保持 WARN;INFO 表示信息项或显式跳过。WARN 不是通过,缺文件也不能不经判断就当成真实 FAIL。报告系统要同时保存状态、原始 audit 证据、是否可执行和人工处理状态。
显式固定 benchmark 与 target
自动探测适合初次观察,但生产审计需要显式记录选择结果。--version 1.34让固定 cfg 根据 Kubernetes 版本映射 benchmark,--benchmark cis-1.12则直接选择配置;两者不能同时使用。自动探测受到托管发行版、组件路径和权限影响时,宁可停止并人工固定,也不要选择一个“能跑起来”的目录冒充正确映射。
标准自建集群常见 target 包括 master、controlplane、node、etcd 和 policies。master组织控制面总体章节,controlplane关注 API Server 等组件,node读取 kubelet 与 worker 配置,etcd处理 etcd 文件与参数,policies需要 API 或策略层证据;托管配置还可能有 managedservices。实际可用项由所选 benchmark 的 controls 决定。
kube-bench run \
--benchmark cis-1.12 \
--targets node \
--json \
--outputfile /results/node.json \
--exit-code 42预期在没有 FAIL 时按正常状态结束,存在一个或多个 FAIL 时返回 42,并生成 JSON。这个退出码不会把全部 WARN 变成失败,因此 CI 还要解析 JSON,确认 manual/WARN 项有 owner、证据和期限。命令产物旁应保存 node UID、OS image、kubelet version、镜像 digest、benchmark、targets 与 controls 摘要。
Kubernetes Job 为什么需要高敏主机访问
容器中的 kube-bench 若只看到自己的 PID namespace 和文件系统,就无法审计宿主机。官方 v0.15.6 通用 Job 示例启用 hostPID: true,并以只读 hostPath 挂载 kubelet、Kubernetes、etcd、systemd 和 CNI 等路径。只读并不等于低敏:其中可能存在 kubeconfig、证书路径、启动参数、节点拓扑和安全配置。
先把官方示例复制进受控仓库,固定镜像 tag 或 digest,并根据实际发行版审查挂载。为它创建专用 namespace 与 ServiceAccount,限制谁能创建 Job、读取 Pod/log 和下载报告;kube-bench Pod 本身没有理由获得 cluster-admin。某些 policies 或自动探测需要 Kubernetes API 时,再按具体资源增加只读 RBAC,不要把节点文件读取权限与 API 管理权限捆在一起。
Pod Security 或准入策略可能拒绝 hostPID 与 hostPath。例外应绑定专用 ServiceAccount、固定镜像、固定节点选择器、只读 mount、短生命周期和审计记录。给整个 namespace 永久 privileged 会让任何能创建 Pod 的主体获得同类主机入口,故障半径远大于一次审计任务。
调度还要区分 control-plane 与 worker。使用 nodeSelector、affinity、tolerations 或按节点生成 Job,使结果确实来自目标节点;不要让一个 worker 的成功报告代表整个节点池。短生命周期节点可能在扫描前被回收,需要在节点加入、发布窗口或周期任务中设置触发,并对“期望节点有报告、实际没有报告”单独告警。
正向实验:建立一份可复核的节点报告
在一次性自建 Linux 集群选择一个无业务负载的 worker node,把固定 Job 调度到该节点,保留 Pod YAML 与 node UID。确认所选 benchmark 包含 node controls,挂载均为只读,然后执行前述 JSON 命令。不要为了得到绿色结果修改节点;实验目的是验证证据链完整。
预期产物包含实际 check ID、target、状态和 audit 输出;命令退出码与 JSON 中 FAIL 语义一致;报告能反查到唯一节点、固定镜像与 controls;manual/WARN 项没有被丢弃。若 --include-test-output 用于排障,原始输出可能含路径、参数或身份信息,转存前要脱敏并限制访问。
接着选一个不会改变业务行为的测试 check,记录修复前原始证据;人工确认 remediation 是否适用于该发行版,在变更审批后修改一次性节点,重启必要组件并确认组件健康,再以同版本、同 benchmark、同 target 重跑。复核链应呈现 before evidence -> approved change -> component health -> after evidence -> rollback proof,而不是只留下前后分数。
反向实验:主动制造采集不完整
复制测试 Job,在另一个一次性节点上删除 /var/lib/kubelet 或 /etc/kubernetes 的必要挂载,其余版本、benchmark 与 target 保持相同。预期会出现缺路径、audit 无法执行、FAIL/WARN 或空证据。检查系统应把这些结果标成采集不完整,并阻止“节点安全”结论;不能因为报告文件生成成功就继续汇总。
还可以比较自动映射与显式映射。分别执行 --version 1.34 和 --benchmark cis-1.12,提取两份报告中的 controls 集合;在固定 v0.15.6 cfg 下,预期两者选择相同语义。若集合不同,优先检查 cfg 来源、版本探测、targets 和发行版覆盖。把两个参数同时传入作为反例时,预期命令报错且不应产生可发布的合规结论。
kube-bench run --version 1.34 \
--targets node --json --outputfile auto-from-version.json
kube-bench run --benchmark cis-1.12 \
--targets node --json --outputfile explicit.json
# 反例:参数互斥,预期失败,不消费其输出。
kube-bench run --version 1.34 --benchmark cis-1.12 --targets node反向实验结束后恢复原 Job,删除故障 Pod 与临时报告,并再次运行正向配置。这样能证明恢复的是采集能力,而不是把失败 check 写进 skip。
托管集群必须显式保留控制面缺口
EKS、GKE、AKS、ACK 等托管集群通常不向租户开放 master/control-plane nodes,因此通用 Pod 无法检查 API Server 进程参数、etcd 文件或控制面主机权限。worker node、policies 和某些 managedservices target 仍可能提供有价值的证据,但不能拼成自建集群的完整覆盖。
托管报告需要建立覆盖矩阵:每个 check ID 记录 target、automated/manual、evidence available、result、provider evidence 与 owner。master 没跑应标为 provider-owned、inaccessible 或 separate evidence required;再用云配置 API、组织策略、提供方认证材料或对应 managedservices checks 补证。worker 的 PASS 永远不能填充控制面单元格。
官方仓库中的 EKS、GKE、AKS Job 示例可能使用 latest 镜像并固定旧 benchmark/target 组合,不能直接作为可重复的生产基线。采用时复制到团队仓库,固定 v0.15.6 或 digest,核对目标集群发行线与 cfg 目录,再审查 host filesystem 布局。不可变 OS、Autopilot 或 virtual node 的缺路径,先分类为平台不可见、路径映射错误或真实不合规,不能批量跳过。
接进 CI 与安全平台时保留人工语义
CI Job 应输出 JSON、stdout、退出码和元数据清单,并把报告作为受控审计产物上传。门禁先判断工具是否成功、目标节点是否齐全、benchmark/target 是否符合计划,再处理 FAIL;最后检查 WARN/manual 是否有有效例外或人工证据。只写 exit 42 的脚本会放过所有 manual check,也会把安装失败与真实不合规混在一起。
安全平台导入时使用 cluster + node UID + benchmark + target + check ID + controls digest作为结果键。节点重建后即使名称相同,也应创建新资产版本;升级 controls 后保留旧结果,但不要直接比较总分。remediation 文本可以进入工单上下文,不能自动转成 shell 在生产执行,因为路径、参数和重启方式受发行版与托管责任边界影响。
项目模板还应包含节点覆盖任务。每轮计划先冻结期望节点清单,执行后计算有报告、失败、未调度和已消失节点;其他节点成功不能覆盖某节点失败。弹性节点池可用相对时间窗口判断新节点何时必须产生首次报告,生产阈值由伸缩速度与审计 SLO 决定。
用现象反推采集链的故障点
出现 unable to determine benchmark 或找不到 config,先核对二进制与 cfg/ 是否同 release、--config-dir 是否正确、自动探测是否有权限;不要修改 controls 让命令勉强运行。大量 check 同时提示文件不存在,先查看 Job 挂载和目标节点 OS 布局;只有确认路径可见且按发行版应存在,才判断为配置失败。
报告只有 node,先检查传入 targets、所选 benchmark 目录和调度节点角色;在托管集群继续记录控制面缺证,在自建集群则为 control plane/etcd 安排独立任务。Pod 被准入拒绝时检查 hostPID、hostPath、ServiceAccount 与策略例外,保持例外窄而短,不能把失败归因于“扫描器不兼容安全集群”。
升级后分数突变时先对比 check ID 集合、scored/manual 类型、audit 命令、路径和 remediation,再比较共同 checks 的结果。单个 WARN 长期存在时查它是否 manual、证据 owner 是否失效、例外是否过期;不要用修改 skip 或丢弃 WARN 来美化趋势。
敏感数据与最小权限要一起治理
节点配置和进程参数可能暴露 kubeconfig 路径、证书位置、云 provider 参数、内部地址和安全开关。--include-test-output 增加排障价值,也增加泄漏面。stdout、JSON、Pod log 和工单附件使用同一分类与脱敏规则,查看原始证据的权限应小于查看汇总结果的权限,保留期限也可以更短。
运行者权限拆成三层:发布任务的 CI 或平台账号只可在专用 namespace 创建和观察审计 Job;Pod 的 ServiceAccount 只获得必要 API 只读权限;节点访问通过固定 hostPID/hostPath 和调度约束提供。临时 kubeconfig、registry pull secret、报告上传凭证在 Job 结束后撤销,不能写入镜像、ConfigMap 或公开日志。
镜像供应链同样要固定。受控模板保存 release、digest 和来源,升级时验证签名或团队采用的制品信任策略。安全审计工具拥有高权限,并不意味着可以绕过镜像评审;恰恰因为它能观察主机,来源和变更需要更严格。
容量、调度与可用性看覆盖而不是副本数
kube-bench 没有常驻控制平面,也没有传统数据库 HA。它的可用性由任务能否按时覆盖所有目标节点、失败能否重试、结果能否可靠保存决定。一个 Job 同时扫描所有角色通常不如按 control-plane、etcd 与 worker 拆分,因为不同节点需要不同挂载、toleration、权限和故障处理。
容量预算至少包含节点数、每节点审计时长、并发 Job、CPU/内存请求、API 查询、日志与 JSON 体积、报告保留和弹性节点 churn。并发过高会争用节点与 API Server,过低则让报告在完成前已经陈旧。定时频率应与节点变更、版本升级和安全变更节奏关联,而不是把每日运行本身当作新鲜度证明。
成本主要来自高权限任务治理、调度覆盖、产物存储、安全平台摄入、人工 WARN 复核和整改变更。托管集群还要为 provider 控制面补证建立独立流程。若团队只需要开发侧 YAML 健康检查,kube-bench 的主机权限和人工运营成本并不划算,应选择静态或集群态势工具;若目标确实是节点与控制面基准,它的可解释 controls 和原始 audit 证据才有价值。
升级、回滚与退出以 controls 兼容为单位
升级不是只替换镜像。先在隔离环境同时替换二进制/镜像与同 release cfg/,比较 version mapping、benchmark 目录、audit 命令、路径、scored/manual 状态、remediation 以及新增、删除和 skip 的 check ID。用同一节点快照或等价测试节点双跑,先做 controls 集合 diff,再比较共同定义的 checks。
若新版本无法识别发行版、产生大面积不可执行检查或改变关键 check 语义,回滚镜像与 cfg/ 的完整组合,并恢复旧任务模板。升级期间的两套原始报告都要保留,不能用旧总分覆盖新结果。长期模板由安全平台 owner 维护,集群平台 owner 提供发行版与节点路径事实,整改 owner 对具体 check 负责。
退出 Job 方式时先停止 CronJob 或外部调度,转存需保留的报告,再删除 Job/Pod、专用 ServiceAccount、RBAC、namespace 和临时 Secret。节点本地安装还要删除二进制、cfg/、timer/cron 与临时报告目录。最后检查安全中心、日志平台和对象存储中的数据保留与删除,以及上传凭证是否撤销。
kubectl delete job kube-bench --ignore-not-found
kubectl get pod,job,sa,role,rolebinding -A -l app=kube-bench预期查询不到带该标签的运行对象;若模板创建过 ClusterRoleBinding、CronJob 或无标签资源,还要按受控清单逐项确认。清理命令不能泛化成删除整个安全 namespace,那里可能存在其他扫描器与共享证据。
选型结论落在证据来源
当风险问题是 kubelet 参数、主机文件权限、控制面进程或 etcd 配置时,kube-bench 能把固定 controls 与节点原始证据连接起来;当问题是仓库 YAML、live object、漏洞、RBAC 姿态或持续报告,应回到 基准、态势与集群扫描工具选择相应采集面。它可以成为 CIS 审计证据的一部分,却不能独立证明整个托管集群合规,也不能替代准入、镜像信任和运行时检测。
真正可运营的终点不是一份全绿 HTML,而是每个目标节点都有明确 benchmark 与 target,每个 manual 或缺证据项都有 owner,每次修复都保留前后原始证据,每次升级都能解释 controls 变化,并且高权限入口、报告与凭证在退出时可以完整核销。
