1. 项目概述:为什么Kubernetes集群必须进行CIS基准审计
如果你负责的Kubernetes集群已经平稳运行了一段时间,部署了各种应用,监控面板也一片祥和,是不是就觉得安全高枕无忧了?我见过太多团队在这个阶段放松警惕,直到某天收到安全告警,甚至被挖矿程序入侵,才手忙脚乱地开始排查。Kubernetes的复杂性决定了它的安全是一个“配置密集型”问题,默认安装的集群远未达到生产就绪的安全状态。这就是为什么我们需要像kube-bench这样的工具,它不是一个可选项,而是保障集群基线安全的“必做体检”。
简单来说,kube-bench是一个Go语言编写的开源工具,专门用于自动化检查Kubernetes集群是否符合CIS(互联网安全中心)Kubernetes基准。CIS基准是一套由安全专家社区制定的、公认的最佳实践安全配置标准。它就像一份极其详尽的检查清单,涵盖了从控制平面组件(如kube-apiserver、etcd)到工作节点(如kubelet、容器运行时)的数百个检查项。手动逐条核对这些项目不仅耗时,而且极易出错,kube-bench的价值就在于它能一键运行,给出清晰的通过/失败报告。
这个项目标题“Kubernetes 安全审计:kube-bench CIS 基准检查和安全加固配置”精准地概括了安全工作的两个核心阶段:审计与加固。首先,使用kube-bench进行自动化扫描,发现配置缺陷和潜在风险点,生成一份“体检报告”。然后,基于这份报告,结合集群的具体情况(如版本、部署方式),有针对性、有步骤地进行安全配置加固。整个过程是一个持续的闭环:审计 -> 加固 -> 再审计,确保集群的安全状态始终贴合最佳实践。
对于刚接触K8s安全的朋友,可能会觉得CIS基准过于严苛,有些检查项甚至会影响便利性。我的经验是,不要试图一次性100%通过所有检查。安全是风险与成本的平衡。我们应该优先关注那些风险等级高(CIS基准通常会标记)、且加固成本低的项目。例如,确保kube-apiserver启用加密审计日志、禁止匿名访问,这些是必须立即实施的“底线”。而一些涉及网络策略或Pod安全标准的项目,则可以纳入后续的迭代计划。接下来,我将带你从零开始,完成一次完整的kube-bench审计与加固实战。
2. 核心思路与工具选型:为什么是kube-bench?
面对Kubernetes安全,市面上有众多工具,从商业安全平台到开源扫描器,为什么我们首选kube-bench?这背后是基于几个核心的工程化考量。
2.1 CIS基准的权威性与普适性
CIS基准并非某一家公司的产品,而是由来自全球企业、政府机构和学术界的网络安全专家共同维护的共识性标准。这意味着它相对中立,且经过了广泛的实践检验。对于企业而言,遵循CIS基准往往也是满足行业合规要求(如等保2.0、PCI-DSS中相关部分)的一条有效路径。kube-bench直接对标这套权威标准,其检查结果具有很高的认可度和可比性。你可以在不同集群、不同时期运行kube-bench,用同一把尺子衡量安全水位的变化。
2.2 轻量级与无侵入性
kube-bench的运行模式非常“朴素”。它通常以一个Job或Pod的形式在目标集群中运行,读取集群内各组件的配置文件、进程参数、目录权限等,然后进行评估。它不需要在你的集群中安装常驻的Agent,也不需要将配置信息发送到外部服务器。这种一次性的、自包含的扫描方式,对生产环境的侵入性极小,运维团队接受度高,也避免了引入新的安全风险点。
2.3 版本适配与检查项透明化
Kubernetes版本迭代很快,安全配置项也会随之变化。kube-bbench项目紧跟CIS基准和K8s版本的更新。在运行工具时,你必须指定对应的Kubernetes版本(如1.24, 1.25)和基准类型(如CIS 1.6.0)。它会自动加载对应版本的检查清单(YAML文件)。这些检查清单文件是完全开源的,你可以在项目的cfg/目录下找到它们。这种透明化至关重要:你可以确切地知道每一条检查在查什么、判断逻辑是什么。如果某条检查不适用于你的特定环境(例如使用了特定的云托管服务),你可以基于对清单的理解,有理有据地将其标记为豁免,而不是盲目地追求通过率。
2.4 与其他工具的定位区分
这里需要厘清kube-bench与一些易混淆工具的区别:
- kube-hunter / kubesec:这些是主动攻击模拟或配置静态分析工具。kube-hunter会尝试攻击你的集群以发现漏洞,而kubesec主要针对单个Kubernetes资源清单(如Deployment YAML)进行安全评分。它们与kube-bench的基线配置审计是互补关系,而非替代。你应该先用kube-bench打好安全地基,再用其他工具发现更深层或更动态的风险。
- 商业容器安全平台:这些平台功能全面,通常包含镜像扫描、运行时防护、合规检查等。kube-bench的检查项往往是其合规模块的一个子集。对于预算有限或希望从核心做起的中小团队,kube-bench是一个绝佳的、零成本的起点。
注意:kube-bench检查的是配置,而不是运行时的安全状态。它无法发现正在发生的网络攻击或异常的进程行为。因此,它必须与网络策略(如Calico、Cilium)、Pod安全准入控制器(PSP或其替代品)、运行时安全监控等方案结合使用,才能构成纵深防御体系。
3. 实战部署与运行:多种场景下的kube-bench执行
理论说得再多,不如动手跑一遍。kube-bench提供了多种运行方式,以适应不同的环境和需求。我将以最常见的几种方式为例,并分享其中的操作细节和避坑点。
3.1 使用容器快速运行(推荐给初学者)
这是最快捷的方式,无需在本地安装Go环境或二进制文件。
# 下载最新的kube-bench容器镜像 docker pull aquasec/kube-bench:latest # 运行扫描,并将宿主机的根目录和docker.sock挂载进去,以便读取配置文件 docker run --rm -v /etc:/etc:ro -v /usr:/usr:ro \ -v /var/lib/kubelet:/var/lib/kubelet:ro \ -v /var/lib/etcd:/var/lib/etcd:ro \ -v $(which kubectl):/usr/local/mount-from-host/kubectl \ -v /var/run/docker.sock:/var/run/docker.sock \ --pid=host \ aquasec/kube-bench:latest run --targets=master,node命令解析与避坑:
-v /etc:/etc:ro等挂载:将宿主机上Kubernetes组件配置文件所在的目录以只读(ro)方式挂载到容器内,这是kube-bench能读取配置的关键。-v $(which kubectl):...:挂载kubectl二进制文件,因为有些检查项需要调用kubectl命令来查询集群状态。-v /var/run/docker.sock:/var/run/docker.sock:如果节点使用Docker作为容器运行时,需要挂载此socket以检查Docker配置。--pid=host:让容器共享宿主机的进程命名空间,以便检查kubelet等进程的运行参数。--targets=master,node:指定扫描目标。对于控制平面节点,使用master;对于工作节点,使用node。如果你在一个同时扮演两种角色的节点上运行,可以同时指定。
3.2 在Kubernetes集群内以Job方式运行(适合生产环境审计)
对于生产集群,更规范的做法是定义一个Kubernetes Job资源。这样便于集成到CI/CD流水线或定期调度任务(如CronJob)中。以下是示例的Job YAML文件:
apiVersion: batch/v1 kind: Job metadata: name: kube-bench namespace: default # 建议创建一个独立的命名空间,如kube-security spec: template: spec: hostPID: true # 共享主机PID命名空间,检查进程 nodeSelector: node-role.kubernetes.io/master: "" # 指定在master节点上运行,扫描控制平面 tolerations: - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule containers: - name: kube-bench image: aquasec/kube-bench:latest command: ["kube-bench", "run", "--targets=master"] volumeMounts: - name: etc-kubernetes mountPath: /etc/kubernetes readOnly: true - name: usr-bin mountPath: /usr/bin readOnly: true - name: var-lib-kubelet mountPath: /var/lib/kubelet readOnly: true - name: var-lib-etcd mountPath: /var/lib/etcd readOnly: true # 根据你的运行时挂载,例如Docker或containerd - name: docker-sock mountPath: /var/run/docker.sock readOnly: true # - name: containerd-sock # mountPath: /run/containerd/containerd.sock # readOnly: true restartPolicy: Never volumes: - name: etc-kubernetes hostPath: path: /etc/kubernetes - name: usr-bin hostPath: path: /usr/bin - name: var-lib-kubelet hostPath: path: /var/lib/kubelet - name: var-lib-etcd hostPath: path: /var/lib/etcd - name: docker-sock hostPath: path: /var/run/docker.sock # - name: containerd-sock # hostPath: # path: /run/containerd/containerd.sock操作心得:
- 节点选择:使用
nodeSelector和tolerations确保Job被调度到正确的节点上。扫描控制平面就调度到master节点,扫描工作节点就调度到worker节点,或者创建两个不同的Job。 - 运行时适配:注释清楚地展示了如何适配Docker或containerd。你必须根据集群实际情况挂载正确的运行时socket,否则相关检查会失败。
- 输出获取:Job运行完成后,Pod会处于Completed状态。你需要
kubectl logs <pod-name>来查看审计报告。为了持久化报告,可以考虑将日志输出到文件,并通过emptyDir或PVC卷挂载出来,或者直接集成日志收集系统。
3.3 解读审计报告:从海量信息中抓住重点
运行完成后,kube-bench会输出一份结构化的报告。报告按检查目标(Master Node, Etcd, Worker Node等)和检查章节(如1.1 Master Node Configuration Files, 1.2 API Server等)组织。每一项检查都会有以下状态:
- [PASS]:符合基准要求。
- [FAIL]:不符合基准要求。这是我们需要重点关注并着手修复的。
- [WARN]:该项检查可能不适用于当前环境,或者需要根据实际情况判断。
- [INFO]:仅提供信息,不构成通过或失败。
一份典型的FAIL项输出如下:
[FAIL] 1.2.7 Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)它会告诉你检查的编号(1.2.7)、标题、 remediation(修复建议),有时还会给出审计命令和期望结果。
我的策略是:不要被大量的FAIL吓倒。首先,将报告保存为文件(如kube-bench-report.json或txt)。然后,使用grep或文本编辑器筛选出所有的[FAIL]项。接着,按风险排序:优先处理与网络策略、认证授权、加密相关的FAIL项(通常对应CIS基准中的1.2.x API Server部分和4.x Pod Security Policies部分),因为它们可能直接导致未授权访问或数据泄露。
4. 关键安全项加固实战:从FAIL到PASS的配置调整
拿到审计报告后,真正的挑战开始了:如何安全、有效地修复这些FAIL项?Kubernetes组件的配置方式多样(静态Pod、systemd服务、托管服务等),且修改不当可能导致集群不可用。下面我挑选几个最常见且关键的安全加固项,详解修复步骤和背后的原理。
4.1 加固kube-apiserver配置
kube-apiserver是集群的网关,其安全配置至关重要。许多FAIL项都集中在这里。
示例FAIL项 1.2.6: Ensure that the --anonymous-auth argument is set to false
- 问题:允许匿名请求访问API Server,风险极高。
- 修复:修改kube-apiserver的静态Pod清单或systemd服务文件(通常位于
/etc/kubernetes/manifests/kube-apiserver.yaml或/etc/systemd/system/kube-apiserver.service)。 - 操作:在
spec.containers.command列表中找到kube-apiserver命令,确保包含--anonymous-auth=false参数。如果不存在,则添加。 - 注意:在托管Kubernetes服务(如EKS, AKS, GKE)上,此参数可能由云提供商管理且无法直接修改,你需要通过云平台提供的安全配置或咨询其文档。
示例FAIL项 1.2.19: Ensure that the --token-auth-file parameter is not set
- 问题:使用静态令牌文件进行认证,这是一种不安全且难以管理的认证方式。
- 修复:移除
--token-auth-file参数。现代K8s集群应使用更安全的认证方式,如X509客户端证书、ServiceAccount令牌(由kube-apiserver自动管理)或集成外部认证服务(如OIDC)。 - 操作:同样在apiserver的启动参数中,删除
--token-auth-file=<path>这一行。 - 原理:静态令牌文件中的令牌是永久的,一旦泄露无法轻易撤销,且缺乏用户标识的精细度。
修改后的配置片段示例(静态Pod):
apiVersion: v1 kind: Pod metadata: name: kube-apiserver namespace: kube-system spec: containers: - command: - kube-apiserver - --anonymous-auth=false # 确保没有 --token-auth-file 参数 - --client-ca-file=/etc/kubernetes/pki/ca.crt - --enable-admission-plugins=NodeRestriction,PodSecurityPolicy # 启用关键准入控制器 - --audit-log-path=/var/log/kubernetes/audit.log - --audit-log-maxage=30 - --audit-log-maxbackup=10 - --audit-log-maxsize=100 # ... 其他必要参数4.2 加固kubelet配置
kubelet是每个节点上的关键代理,其配置错误可能影响节点上所有Pod的安全。
- 示例FAIL项 4.2.6: Ensure that the --protect-kernel-defaults argument is set to true
- 问题:未保护内核默认参数,恶意Pod可能修改关键内核参数影响主机稳定性。
- 修复:在kubelet的配置文件(如
/var/lib/kubelet/config.yaml或systemd drop-in文件)中设置protectKernelDefaults: true。 - 操作:
- SSH登录到工作节点。
- 编辑kubelet配置文件:
sudo vi /var/lib/kubelet/config.yaml。 - 添加或修改行:
protectKernelDefaults: true。 - 重启kubelet服务:
sudo systemctl restart kubelet。
- 验证:重启后,运行
sudo systemctl status kubelet确保服务正常,并再次运行kube-bench检查该项是否通过。
4.3 配置网络策略(应对CIS 5.x系列检查)
CIS基准的5.x章节涉及网络策略,很多检查项会因为没有应用任何NetworkPolicy而失败。这提醒我们,默认的Kubernetes网络是“全通”的,Pod之间可以任意访问。
- 核心思路:采用“默认拒绝,按需允许”的白名单模式。
- 操作:
- 安装网络策略控制器:如果你的CNI插件(如Calico、Cilium、Weave Net)支持NetworkPolicy,请确保已启用。例如,对于Calico,通常部署时已包含。
- 创建默认拒绝所有入口流量的策略:在每个需要隔离的命名空间(如
default)中应用以下策略。这是一个基础的安全护栏。
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-ingress namespace: default # 应用到具体命名空间 spec: podSelector: {} # 选择所有Pod policyTypes: - Ingress # ingress: [] # 显式不指定任何规则,即拒绝所有入口流量3. **为需要通信的Pod创建允许策略**:在默认拒绝的基础上,为特定的应用Pod创建精确的允许规则。例如,允许前端Pod访问后端服务的特定端口。重要提示:修改kube-apiserver、kubelet等核心组件的配置后,务必观察集群状态。建议在非高峰时段操作,并准备好回滚方案(例如备份原始配置文件)。对于生产集群,强烈建议先在测试或预发环境中验证修改。
5. 进阶:将kube-bench集成至CI/CD与合规流程
单次运行kube-bench解决了“当前状态”的问题,但安全需要持续保障。将安全审计自动化、流程化,是提升整体安全水位的关键。
5.1 在CI/CD流水线中集成集群创建检查
如果你使用Terraform、Ansible或集群API(Cluster API)等工具自动化创建Kubernetes集群,可以在集群部署完成后立即加入一个kube-bench检查环节。
- 场景:在Terraform创建完EKS集群后,通过一个
null_resource或local-execprovisioner,在本地或一个临时Pod中运行kube-bench。 - 逻辑:设置一个可接受的最大FAIL数量阈值(例如,非关键项允许10个FAIL)。如果检查结果FAIL数超过阈值,则令CI/CD流水线失败,阻止后续应用部署到不安全的集群上,并将详细的审计报告作为流水线产物保存,供运维人员分析修复。
5.2 使用CronJob实现定期审计
在生产集群中部署一个CronJob,每周或每月自动运行kube-bench,并将报告发送到指定位置(如S3存储桶、安全信息与事件管理(SIEM)系统或Slack频道)。
apiVersion: batch/v1 kind: CronJob metadata: name: kube-bench-weekly namespace: kube-security spec: schedule: "0 2 * * 0" # 每周日凌晨2点运行 jobTemplate: spec: template: spec: # ... (复用前面Job的spec,注意挂载卷和节点选择) containers: - name: kube-bench image: aquasec/kube-bench:latest command: ["kube-bench", "run", "--targets=master,node", "--json"] volumeMounts: [...] volumes: [...] restartPolicy: Never5.3 生成JSON报告并与仪表板集成
kube-bench支持--json参数输出机器可读的JSON格式报告。这为自动化处理打开了大门。
- 处理流程:
- CronJob Pod将JSON报告输出到一个共享的
emptyDir卷。 - Pod内运行一个sidecar容器(如一个小型Python脚本),负责读取JSON报告,解析FAIL/WARN数量,并通过HTTP请求将结果推送到监控系统(如Prometheus Pushgateway)或安全运营中心(SOC)平台。
- 在Grafana等仪表板中,可以创建一个“K8s CIS合规率”看板,可视化展示各集群、各节点随时间推移的合规状态变化趋势。当FAIL项突然增多时,可以触发告警。
- CronJob Pod将JSON报告输出到一个共享的
5.4 豁免管理与风险接受
不是所有CIS检查项都适用于每个环境。例如,某些检查项针对的是自建etcd集群,而如果你使用的是云托管的、加密的etcd服务(如GKE的etcd),这些检查项就不适用。盲目地“修复”这些项可能毫无意义甚至引发问题。
- 最佳实践:建立正式的“风险接受”或“豁免”流程。
- 操作:在kube-bench的配置目录中,你可以创建自定义的配置文件,通过
--config参数指定,来跳过(skip)特定的检查项。但做这个决定必须有记录和审批。例如,在团队的知识库或CMDB中,记录下被豁免的检查项ID、豁免理由(如“使用AWS EKS托管控制平面,该项由AWS负责”)、豁免有效期和审批人。这既是安全审计的严谨性体现,也能在未来的合规检查中提供依据。
6. 常见问题排查与避坑指南
在实际操作中,你肯定会遇到各种预期之外的情况。下面是我和团队在多次审计中积累的一些典型问题及其解决方案。
6.1 运行kube-bench时出现权限错误或挂载问题
- 症状:日志中大量出现
Permission denied或open /etc/kubernetes/...: no such file or directory。 - 排查:
- 确认路径:首先确认你的Kubernetes组件配置文件的实际路径。使用
ps aux | grep kube-apiserver查看进程的--config参数指向哪里。不同安装方式(kubeadm, kops, 二进制)路径可能不同。 - 检查挂载:在Docker run命令或Job YAML中,确保所有必要的宿主机路径都已正确挂载,并且权限是
readOnly: true。 - 使用特权模式(谨慎):在极端情况下,如果容器内用户权限不足,可以尝试给容器添加
securityContext.privileged: true(在Job中)或Docker run时加--privileged。但这会显著扩大攻击面,仅应在受控的测试环境中临时使用,生产环境应通过精确的Capabilities和目录挂载来解决。
- 确认路径:首先确认你的Kubernetes组件配置文件的实际路径。使用
6.2 修复配置后组件无法启动
- 症状:修改kube-apiserver或kubelet配置后,
systemctl status kubelet显示失败,Pod无法调度或API无法访问。 - 黄金法则:永远先备份!在修改任何核心配置文件前,执行
sudo cp /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.backup。 - 排查步骤:
- 查看日志:使用
sudo journalctl -u kubelet -f或sudo docker logs <container-id>(如果组件以容器运行)查看详细的错误日志。错误信息通常会明确指出是哪个参数有问题。 - 参数格式:检查YAML格式是否正确,特别是缩进和布尔值(
true/false不要加引号)。检查参数名是否拼写错误。 - 参数冲突:某些参数不能同时使用。仔细阅读Kubernetes官方文档中对应组件的参数说明。
- 快速回滚:如果问题紧急,立即用备份文件覆盖修改后的文件,并重启服务。对于静态Pod,kubelet会自动检测到文件变化并重启容器。
- 查看日志:使用
6.3 某些FAIL项在托管服务上无法修复
- 典型项:如“确保etcd数据目录权限为700”或“确保API Server进程不由root用户运行”。
- 应对策略:
- 区分责任共担模型:在AWS EKS、GKE、AKS等托管服务中,云提供商负责控制平面(包括etcd和apiserver)的安全和管理。这些检查项的FAIL状态意味着作为用户的你无法直接修改,但不代表风险一定存在。云提供商可能在其底层实施了等效或更强的安全控制。
- 查阅官方文档:前往云提供商的官方安全合规文档,通常会有类似“CIS Kubernetes Benchmark on [云服务]”的白皮书,其中会明确列出哪些检查项由提供商负责,哪些需要客户负责。
- 标记豁免:在内部审计报告中,将这些项标记为“由云提供商管理”,并附上官方文档链接作为证据。你的加固重点应放在你可以控制的“客户责任”部分,如Worker Node配置、网络策略、RBAC、Pod安全标准等。
6.4 审计报告过于冗长,难以聚焦
- 解决方案:使用
grep和jq(用于JSON报告)进行过滤分析。
将常用的过滤和分析命令脚本化,可以极大提升效率。# 只查看FAIL项 kube-bench run --targets node --json | jq '.tests[].results[] | select(.status == "FAIL")' # 查看特定章节的检查结果(如1.2 API Server) kube-bench run | grep -A 5 \"1.2\" # 统计各类状态的数量 kube-bench run --json | jq '[.tests[].results[].status] | group_by(.) | map({status: .[0], count: length})'
7. 超越kube-bench:构建完整的K8s安全纵深防御体系
通过kube-bench实现CIS基准合规,是构建安全Kubernetes集群的坚实第一步,但绝不是最后一步。安全是一个多层次、持续的过程。以下是在此基础上,你应该考虑构建的纵深防御层:
7.1 Pod安全准入控制
CIS基准的很多检查项(特别是第5节)关于Pod安全。Kubernetes 1.22版本后,传统的PodSecurityPolicy (PSP)已被弃用,取而代之的是Pod Security Standards (PSS)和内置的Pod Security Admission控制器。
- 行动:在命名空间级别应用PSS标签。例如,要求
default命名空间下的Pod必须满足基线(baseline)标准,禁止特权模式。
这会在Pod创建时进行实时拦截或告警,比事后审计更主动。apiVersion: v1 kind: Namespace metadata: name: my-app labels: pod-security.kubernetes.io/enforce: baseline pod-security.kubernetes.io/enforce-version: latest pod-security.kubernetes.io/audit: restricted pod-security.kubernetes.io/warn: restricted
7.2 镜像安全扫描
不安全的镜像是最大的风险来源之一。需要在CI构建阶段和仓库推送阶段集成镜像漏洞扫描工具(如Trivy, Clair, Grype)。
- 集成点:
- CI流水线:在Docker构建完成后,立即使用Trivy扫描镜像,发现高危(CRITICAL/HIGH)漏洞则令构建失败。
- 仓库钩子:在Harbor等私有镜像仓库中配置扫描策略,阻止带有严重漏洞的镜像被拉取到生产环境。
7.3 运行时安全与行为监控
即使配置安全、镜像干净,运行时也可能出现异常行为(如挖矿、横向移动)。
- 工具:考虑部署Falco或Aqua Security、Sysdig等工具的Agent。它们基于规则或机器学习,可以检测容器内的异常进程、文件操作、网络连接等,并实时告警。
- 策略:与网络策略结合,实现零信任网络。使用Cilium或Calico不仅提供网络策略,还能提供基于DNS、API调用的更细粒度安全策略。
7.4 秘密信息管理
永远不要在镜像或环境变量中硬编码密码、密钥。使用Secrets对象,并考虑更强大的方案如HashiCorp Vault、AWS Secrets Manager或Azure Key Vault的集成,实现密钥的动态生成、轮转和细粒度访问控制。
将kube-bench审计作为你安全闭环的“检查”环节,与“预防”(镜像扫描、PSA)、“检测”(运行时安全)、“响应”(日志审计、告警联动)环节结合起来,才能真正为你的Kubernetes旅程保驾护航。安全没有终点,它是一场与复杂性和潜在威胁的持续对话,而扎实的基线配置,是这场对话最有力的开场白。