概述

你管着一个 K8s 集群,API Server 的 --anonymous-auth=true 没关,etcd 的证书权限是 644,kubelet 的 --read-only-port=10255 还开着——这些东西单独看每个都是"小问题",但攻击者拿到其中一个入口,就能一路横向打到整个集群。这不是假设,CNCF 2025 年度调查报告显示,超过 90% 的生产 K8s 集群存在至少一个 CIS Benchmark 级别的配置缺陷。

CIS(Center for Internet Security)Benchmark 是一套业界公认的安全配置基线,K8s 有对应的专门版本。kube-bench 就是用来跑这套基线检查的工具——你把它跑一遍,它告诉你哪些配置不符合安全标准、应该怎么改。

但这只是第一步。光跑出报告不够,你还得知道怎么修、怎么持续监控、怎么把扫描嵌进 CI/CD 流水线里。本文从实际操作出发,覆盖从安装 kube-bench、跑第一次扫描、解读报告、修复常见问题,到配合 Trivy 做镜像漏洞扫描、kube-hunter 做渗透模拟、把安全扫描自动化到 GitOps 流程中的完整路径。

CIS Kubernetes Benchmark 是什么

CIS Benchmark 说白了就是一份"安全配置检查清单"。CIS 这个组织拉了一批安全专家,把某个系统(操作系统、数据库、云平台、K8s 等)应该怎么做才算安全,整理成一份文档,每一条都有明确的检查方法和修复建议。

K8s 的 CIS Benchmark 把检查项分成四个层面:

层面检查对象典型检查项
控制平面API Server、Scheduler、Controller Manager、etcd是否禁用匿名访问、是否启用 RBAC、etcd 是否启用 TLS
工作节点kubelet、kube-proxy是否禁用只读端口、是否启用客户端证书认证
策略RBAC、PodSecurityPolicy/PSA是否使用最小权限、是否限制特权容器
托管服务EKS/GKE/AKS 等云平台特有的安全配置

每个检查项标注了严重级别:L1 是基本要求,任何环境都该满足;L2 是更严格的要求,适用于安全敏感度高的环境。

kube-bench 就是把这份检查清单做成了自动化工具。它直接读取 K8s 组件的配置文件、启动参数、证书权限,一条条对照 CIS Benchmark 规则,输出 PASS/FAIL/WARN 结果。

安装 kube-bench

方式一:直接在节点上跑(推荐初次使用)

# 下载最新版本(截至 2026 年 7 月,最新版本为 v0.10.0)
curl -L https://github.com/aquasecurity/kube-bench/releases/download/v0.10.0/kube-bench_0.10.0_linux_amd64.tar.gz -o kube-bench.tar.gz
tar -zxvf kube-bench.tar.gz
sudo mv kube-bench /usr/local/bin/

# 验证
kube-bench version

ARM64 环境(比如 Apple Silicon 或 AWS Graviton)把 linux_amd64 换成 linux_arm64

方式二:用 Job 在集群内跑

apiVersion: batch/v1
kind: Job
metadata:
  name: kube-bench
  namespace: kube-system
spec:
  template:
    spec:
      hostPID: true
      containers:
        - name: kube-bench
          image: aquasec/kube-bench:0.10.0
          command: ["kube-bench"]
          args: ["--benchmark", "cis-1.10"]
          volumeMounts:
            - name: var-lib-etcd
              mountPath: /var/lib/etcd
              readOnly: true
            - name: etc-kubernetes
              mountPath: /etc/kubernetes
              readOnly: true
            - name: usr-bin
              mountPath: /usr/local/mount-from-host/bin
              readOnly: true
            - name: var-lib-kubelet
              mountPath: /var/lib/kubelet
              readOnly: true
            - name: etc-systemd
              mountPath: /etc/systemd
              readOnly: true
      restartPolicy: Never
      volumes:
        - name: var-lib-etcd
          hostPath:
            path: /var/lib/etcd
        - name: etc-kubernetes
          hostPath:
            path: /etc/kubernetes
        - name: usr-bin
          hostPath:
            path: /usr/bin
        - name: var-lib-kubelet
          hostPath:
            path: /var/lib/kubelet
        - name: etc-systemd
          hostPath:
            path: /etc/systemd

这里有几个关键点要解释一下:

  • hostPID: true:kube-bench 需要读取宿主机进程信息(比如 kube-apiserver 的启动参数),不挂 PID namespace 它只能看静态配置文件,看不到运行时实际生效的参数。
  • 挂载多个 hostPath:因为 kube-bench 要读 etcd 数据目录权限、kubelet 配置、kubernetes manifest 文件等,这些都在宿主机不同路径下。
  • --benchmark cis-1.10:显式指定 benchmark 版本。kube-bench 支持自动识别 K8s 版本并匹配对应 benchmark,但在生产环境,我建议你显式指定版本。原因很简单——自动识别依赖 K8s minor version 推断,如果你的集群是 1.30.2,自动识别会判定为 1.30,但 CIS Benchmark 的版本映射可能还没覆盖到 1.30,导致用了旧版规则。显式指定版本号,避免漏检。

Benchmark 版本对照表

K8s 版本CIS Benchmark 版本
1.27 - 1.28cis-1.9
1.29 - 1.30cis-1.10
EKS 1.29+eks-1.8.0
GKE 1.29+gke-1.8.0
RKE2 1.29+rke2-cis-1.7

跑之前先确认你的 K8s 版本,再对照选 benchmark。别盲目用自动识别。

第一次扫描

# 在 master 节点上跑,检查控制平面
kube-bench --benchmark cis-1.10 --targets master,node

# 只检查某个组件
kube-bench --benchmark cis-1.10 --targets etcd

# 输出 JSON 格式报告(方便程序化处理)
kube-bench --benchmark cis-1.10 --targets master,node --output json | jq . > kube-bench-report.json

解读扫描报告

kube-bench 的输出长这样:

[FAIL] 1.2.6 Ensure that the --kubelet-certificate-authority argument is set as appropriate (Automated)
[FAIL] 1.2.7 Ensure that the --authorization-mode argument is not set to AlwaysAllow (Automated)
[PASS] 1.2.8 Ensure that the --authorization-mode argument includes Node (Automated)
[WARN] 1.2.9 Ensure that the --authorization-mode argument includes RBAC (Automated)
  • PASS:配置符合要求,不用动。
  • FAIL:配置不符合安全要求,需要修复。这是你要重点关注的部分。
  • WARN:kube-bench 无法自动判断,需要你手动确认。常见于需要根据业务场景决定是否适用的检查项。

每个 FAIL 后面会跟一段 remediation(修复建议),直接告诉你怎么改。比如:

Remediation:
Edit the API Server pod specification file /etc/kubernetes/manifests/kube-apiserver.yaml
on the master node and set the below parameter.
--authorization-mode=Node,RBAC

照着改就行。但别无脑全改——有些检查项在特定场景下不适用。比如托管 K8s(EKS/GKE/AKS),控制平面由云厂商管理,很多检查项的修复需要云厂商在平台层面做,你改不了。

实战:最常见的 10 个 FAIL 项及修复

我跑过几十个集群的 kube-bench 扫描,下面是出现频率最高的 10 个 FAIL 项,附上修复方法。

1. API Server 匿名访问未禁用

# 检查当前配置
ps -ef | grep kube-apiserver | grep anonymous-auth

# 修复:编辑 /etc/kubernetes/manifests/kube-apiserver.yaml
# 添加参数
- --anonymous-auth=false

这个参数控制未认证用户能否访问 API Server。开着的话,任何人都能访问 /api/v1/namespaces/default/pods 这类接口。在内网可能觉得无所谓,但如果你的 API Server 暴露了公网(别这么做),这就是直接送权限。

注意:禁用匿名访问会影响健康检查探针。如果你的 LB 健康检查走 /healthz 且不带认证,改成 --anonymous-auth=true 并限制只允许访问健康检查路径,或者用 --authorization-mode=Node,RBAC 配合 Node 认证。折中方案。

2. etcd 未启用客户端证书认证

# 检查
ps -ef | grep etcd | grep client-cert-auth

# 修复:编辑 etcd 配置
- --client-cert-auth=true
- --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
- --cert-file=/etc/kubernetes/pki/etcd/server.crt
- --key-file=/etc/kubernetes/pki/etcd/server.key

etcd 存了整个集群的状态——ConfigMap、Secret、ServiceAccount Token 全在里面。如果 etcd 不要客户端证书就能连,拿到网络访问权就能直接读走所有密钥。

3. kubelet 只读端口未关闭

# 检查
ps -ef | grep kubelet | grep read-only-port

# 修复
# 在 kubelet 配置文件 /var/lib/kubelet/config.yaml 中设置
readOnlyPort: 0

10255 端口(只读端口)允许未认证访问 kubelet 的信息接口,能拿到 Pod 列表、资源使用、环境变量等。这玩意在老版本 K8s 默认开着,好多人忘了关。

4. RBAC 未启用

# 检查
ps -ef | grep kube-apiserver | grep authorization-mode

# 修复:确保 authorization-mode 包含 RBAC
- --authorization-mode=Node,RBAC

不开 RBAC 等于所有人都有 cluster-admin 权限。这在任何生产环境都是不可接受的。注意 AlwaysAllow 不能出现在 authorization-mode 里,否则 RBAC 等于没开。

5. 审计日志未启用

# /etc/kubernetes/manifests/kube-apiserver.yaml
- --audit-log-path=/var/log/kubernetes/audit/audit.log
- --audit-log-maxage=30
- --audit-log-maxbackup=10
- --audit-log-maxsize=100
- --audit-policy-file=/etc/kubernetes/audit-policy.yaml

审计日志记录了"谁在什么时间对什么资源做了什么操作"。出了安全事件要溯源,没审计日志等于盲人摸象。写个审计策略文件:

# /etc/kubernetes/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  # 记录所有 Secret 操作
  - level: RequestResponse
    resources:
      - group: ""
        resources: ["secrets"]
  # 记录所有认证授权操作
  - level: Metadata
    resources:
      - group: ""
        resources: ["serviceaccounts", "rolebindings", "clusterrolebindings"]
  # 记录所有写操作
  - level: Request
    verbs: ["create", "update", "patch", "delete"]
  # 其余只记录元数据
  - level: Metadata

6. Pod 安全标准未配置

K8s 1.25+ 废弃了 PodSecurityPolicy(PSP),改用 Pod Security Admission(PSA)。PSA 通过 namespace 标签控制 Pod 安全级别:

# 给 namespace 打标签,强制 restricted 级别
kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/audit=restricted \
  pod-security.kubernetes.io/warn=restricted

三个级别从宽松到严格:privileged > baseline > restricted。生产环境我推荐至少 baseline,核心业务 namespace 用 restricted

restricted 级别禁止:特权容器、宿主机网络/PID/IPC、root 用户、不安全的能力(capabilities)等。刚切过去可能有些 Pod 起不来,先打 warn 标签看看告警,逐步收紧。

7. etcd 数据目录权限过宽

# 检查
ls -la /var/lib/etcd/

# 修复
chown -R etcd:etcd /var/lib/etcd/
chmod 700 /var/lib/etcd/

CIS 要求 etcd 数据目录权限为 700。权限过宽意味着任何系统用户都能读到 etcd 数据文件。

8. Kubernetes 证书文件权限

# 检查
ls -la /etc/kubernetes/pki/

# 修复:CA 证书 644,CA 私钥 600
chmod 644 /etc/kubernetes/pki/ca.crt
chmod 600 /etc/kubernetes/pki/ca.key
# 各组件证书 644,私钥 600
chmod 644 /etc/kubernetes/pki/apiserver.crt
chmod 600 /etc/kubernetes/pki/apiserver.key

9. kubelet 匿名认证未关闭

# /var/lib/kubelet/config.yaml
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true

跟 API Server 的匿名访问一个道理。kubelet 直接暴露在节点网络里,开了匿名认证就是裸奔。

10. 未配置 TLS 加密

# 确保 API Server 配置了 TLS
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key

2026 年了,不跑 HTTPS 的集群该被淘汰了。

Trivy:镜像层面的安全扫描

kube-bench 查的是"集群配置安不安全",Trivy 查的是"你跑的镜像安不安全"。两个维度,缺一不可。

安装

# 方式一:二进制
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin/

# 方式二:apt(Debian/Ubuntu)
sudo apt-get install trivy

# 方式三:Docker
docker run --rm aquasec/trivy:latest image nginx:latest

扫描镜像

# 扫描单个镜像
trivy image nginx:1.27

# 扫描结果按严重级别过滤
trivy image --severity HIGH,CRITICAL nginx:1.27

# 只输出存在漏洞的包
trivy image --severity CRITICAL --format json nginx:1.27 | jq '.Results[0].Vulnerabilities[] | {VulnerabilityID, PkgName, InstalledVersion, FixedVersion}'

# 扫描镜像并生成 HTML 报告
trivy image --format template --template "@html.tpl" -o report.html nginx:1.27

扫描 K8s 集群中所有镜像

# 列出集群中所有运行中的 Pod 使用的镜像
kubectl get pods --all-namespaces -o jsonpath="{range .items[*]}{range .spec.containers[*]}{.image}{'\n'}{end}{end}" | sort -u > images.txt

# 批量扫描
while read image; do
  echo "=== Scanning $image ==="
  trivy image --severity HIGH,CRITICAL "$image"
done < images.txt

这段脚本在实际使用中有个问题:集群镜像多的时候慢得要命。建议配合 --skip-db-update 跳过漏洞库更新(事先更新好),或者用 Trivy Server 模式共享缓存。

Trivy KBOM:K8s 物料清单

Trivy 在 2024 年推出了 KBOM(Kubernetes Bill of Materials)功能,能扫描整个集群的组件清单,类似于 SBOM 之于应用程序。这在做资产盘点和安全审计时很有用:

# 扫描集群 KBOM
trivy k8s cluster --format kubebom -o kbom.json

# 扫描集群配置错误(类似 kube-bench 但集成在 Trivy 里)
trivy k8s cluster --scanners misconfig

# 扫描集群中的漏洞
trivy k8s cluster --scanners vuln

KBOM 能帮你看清集群里到底装了什么:K8s 版本、各组件版本、运行时版本、安装的 Operator 及版本。出了漏洞,拿着这份清单对照 CVE 公告,很快能判断有没有中招。

kube-hunter:从攻击者视角看集群

kube-bench 是"防守方"视角——检查配置是否符合标准。kube-hunter 是"攻击方"视角——模拟攻击者能怎么打你的集群。

# 安装
pip install kube-hunter

# 在集群外扫描(模拟外部攻击者)
kube-hunter --remote <cluster-ip>

# 在节点上扫描(模拟拿到节点后的横向移动)
kube-hunter --pod

# 在集群内扫描
kubectl run kube-hunter --rm -it --image=aquasec/kube-hunter:latest -- --pod

kube-hunter 输出示例:

Vulnerability                | Severity    | Description
-----------------------------|-------------|--------------------------------------
CAP_NET_RAW enabled          | low         | Pods have CAP_NET_RAW capability
Dashboard exposed            | high        | Kubernetes Dashboard is exposed
Metrics Server insecure      | medium      | Metrics Server allows anonymous access

这些发现跟 kube-bench 的结果有重叠,但 kube-hunter 的优势在于它告诉你"如果你这个漏洞被利用了,影响有多大"。同样是 etcd 未授权访问,kube-bench 只标 FAIL,kube-hunter 会告诉你"攻击者可以读取所有 Secret 包括 ServiceAccount Token,进而拿到集群管理员权限"。

我在实际使用中的一点经验:kube-hunter 对误报控制不如 kube-bench 严格。它报的有些"漏洞"在你的网络架构下可能根本不可达。比如它报"API Server 10250 端口可未认证访问",但你的节点在 VPC 内网,外面根本访问不到。拿结果的时候自己判断可达性,别被吓到。

把安全扫描集成到 CI/CD 流水线

手动跑一次扫描谁都会,难的是持续化。安全扫描只有变成流水线的一部分,才能防止问题反复出现。

GitOps 模式:PR 合并时自动扫描镜像

# .github/workflows/security-scan.yml
name: Container Image Security Scan
on:
  pull_request:
    paths:
      - 'deploy/**'  # 只在部署配置变更时触发

jobs:
  trivy-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Extract images from manifests
        id: images
        run: |
          IMAGES=$(grep -ohP 'image:\s*\K.*' deploy/*.yaml | sort -u)
          echo "images<<EOF" >> $GITHUB_OUTPUT
          echo "$IMAGES" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT          

      - name: Run Trivy scan
        run: |
          for image in ${{ steps.images.outputs.images }}; do
            echo "=== Scanning $image ==="
            trivy image --severity CRITICAL --exit-code 1 "$image" || {
              echo "::error::Critical vulnerabilities found in $image"
              exit 1
            }
          done          

这个流水线在 PR 修改部署配置时,自动扫描变更的镜像,发现 CRITICAL 级别漏洞直接阻断合并。

定期扫描:CronJob 跑 kube-bench

apiVersion: batch/v1
kind: CronJob
metadata:
  name: kube-bench-scan
  namespace: kube-system
spec:
  schedule: "0 2 * * 1"  # 每周一凌晨 2 点
  jobTemplate:
    spec:
      template:
        spec:
          hostPID: true
          containers:
            - name: kube-bench
              image: aquasec/kube-bench:0.10.0
              command:
                - /bin/sh
                - -c
                - |
                  kube-bench --benchmark cis-1.10 --targets master,node \
                    --output json > /tmp/report.json
                  # 把报告推到监控系统或 Slack
                  curl -X POST "$SLACK_WEBHOOK" \
                    -H "Content-Type: application/json" \
                    -d "{\"text\":\"Weekly kube-bench scan completed. $(jq '[.Controls[].Checks[] | select(.State == \"FAIL\")] | length') FAIL items.\"}"                  
              env:
                - name: SLACK_WEBHOOK
                  valueFrom:
                    secretKeyRef:
                      name: slack-webhook
                      key: url
              volumeMounts:
                - name: etc-kubernetes
                  mountPath: /etc/kubernetes
                  readOnly: true
                - name: var-lib-kubelet
                  mountPath: /var/lib/kubelet
                  readOnly: true
          restartPolicy: OnFailure
          volumes:
            - name: etc-kubernetes
              hostPath:
                path: /etc/kubernetes
            - name: var-lib-kubelet
              hostPath:
                path: /var/lib/kubelet

集成到 Prometheus 监控

如果你想在 Grafana 上看到合规率指标,可以用 kube-bench 的 JSON 输出做自定义 Exporter:

#!/usr/bin/env python3
"""kube-bench results exporter for Prometheus"""
import json
import subprocess
from http.server import HTTPServer, BaseHTTPRequestHandler

class MetricsHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path != '/metrics':
            self.send_error(404)
            return

        result = subprocess.run(
            ['kube-bench', '--benchmark', 'cis-1.10',
             '--targets', 'master,node', '--output', 'json'],
            capture_output=True, text=True
        )
        data = json.loads(result.stdout)

        metrics = []
        total_pass = 0
        total_fail = 0
        total_warn = 0

        for control in data.get('Controls', []):
            for check in control.get('Checks', []):
                status = 1 if check['State'] == 'PASS' else 0
                metrics.append(
                    f'kube_bench_check{{id="{check["ID"]}",'
                    f'title="{check["Text"]}",'
                    f'state="{check["State"]}"}} {status}'
                )
                if check['State'] == 'PASS':
                    total_pass += 1
                elif check['State'] == 'FAIL':
                    total_fail += 1
                else:
                    total_warn += 1

        total = total_pass + total_fail + total_warn
        compliance = (total_pass / total * 100) if total > 0 else 0

        metrics.append(f'kube_bench_total_checks {total}')
        metrics.append(f'kube_bench_pass_total {total_pass}')
        metrics.append(f'kube_bench_fail_total {total_fail}')
        metrics.append(f'kube_bench_warn_total {total_warn}')
        metrics.append(f'kube_bench_compliance_percent {compliance}')

        self.send_response(200)
        self.send_header('Content-Type', 'text/plain')
        self.end_headers()
        self.wfile.write('\n'.join(metrics).encode())

if __name__ == '__main__':
    HTTPServer(('0.0.0.0', 9099), MetricsHandler).serve_forever()

跑起来后,Prometheus 抓取 /metrics,你在 Grafana 建一个面板展示合规率和 FAIL 趋势:

# 合规率
kube_bench_compliance_percent

# FAIL 项数量趋势
kube_bench_fail_total

# 按检查项查看状态
kube_bench_check{state="FAIL"}

安全加固决策矩阵

不是所有 FAIL 都要立刻修。安全加固也要排优先级——就像修 bug 一样,有 P0 也有 P3。

风险等级判断标准行动时间要求
P0 紧急远程可利用 + 可拿权限立即修复24 小时内
P1 高远程可利用 / 需要内网访问尽快修复一周内
P2 中需要节点访问权限计划修复一个月内
P3 低理论风险 / 需要物理访问评估后处理按计划推进

举个例子怎么用这个矩阵:

  • API Server 匿名访问开着 + 集群暴露公网 → P0,今晚就得改。
  • etcd 未启用客户端证书认证 + etcd 在独立网络段 → P1,这周搞定。
  • kubelet 只读端口未关 + 节点在内网 → P2,放进下一个迭代。
  • etcd 数据目录权限 755 → P3,改了不会出事,不改短期内也不会被利用。

别被一个满是 FAIL 的报告吓到。我见过一个 200 项的扫描结果里有 80 个 FAIL,但真正紧急的就 3-5 个。剩下的慢慢改。

托管 K8s(EKS/GKE/AKS)的特殊处理

如果你用的是云厂商的托管 K8s,控制平面你碰不到,kube-bench 大量检查项会显示 N/AWARN。这不是你的问题——云厂商负责控制平面安全。

但有几个检查维度是你必须自己管的:

检查维度工具你的责任
节点安全组云平台控制台限制 kubelet/kube-proxy 端口访问
Pod 安全PSA / OPA Gatekeeper限制特权 Pod
镜像安全Trivy扫描所有运行镜像
RBAC 配置kubectl + 审计日志定期检查过度授权的 RoleBinding
节点 OS 加固CIS OS Benchmark用云厂商提供的 CIS 合规镜像

EKS 有专门的 kube-bench benchmark(--benchmark eks-1.8.0),GKE 有 --benchmark gke-1.8.0。这些版本去掉了不适用于托管环境的检查项,补充了云平台特有的检查内容。

实际操作中,EKS 用户特别要注意:AWS 在 2026 年 7 月发布的安全公告指出,Ubuntu 22.04 节点映像的 Linux 内核 CVE 扫描发现数量激增,原因是 Canonical 重新分类了大量内核 CVE 的状态。很多报告的漏洞实际上上游还没修复,单纯升级节点版本解决不了。遇到这种情况别慌,先查 CVE 的实际可利用性,别被扫描器的报告牵着鼻子走。

开源安全工具对比

除了 kube-bench 和 Trivy,K8s 安全领域还有不少工具。我按用途做了一个对比:

工具用途优势局限
kube-benchCIS Benchmark 合规检查权威标准、检查项全面只查配置不查漏洞
Trivy镜像/文件系统/IaC 漏洞扫描多维度扫描、KBOM 支持需要联网更新漏洞库
kube-hunter渗透模拟攻击者视角、发现利用链误报较多
OPA Gatekeeper策略引擎准入控制、实时拦截学习成本高
Falco运行时安全监控实时检测异常行为规则编写复杂
Kyverno策略引擎原生 K8s 风格、易上手功能不如 OPA 灵活
KubeClarity应用安全扫描全生命周期覆盖社区较小

我的推荐组合:

  1. kube-bench:跑基线,每周一次
  2. Trivy:CI/CD 流水线镜像扫描 + 定期全集群扫描
  3. OPA Gatekeeper 或 Kyverno:准入控制,阻止不安全配置进入集群
  4. Falco:运行时监控,检测异常行为

这四层从配置安全、镜像安全、准入控制到运行时安全,基本覆盖了 K8s 安全的主要面。

OPA Gatekeeper:在准入层拦截不安全配置

光扫描不够。你今天修了一个 FAIL,明天有人提交一个 PR 又加回来。Gatekeeper 可以在 API Server 的准入控制层拦截不安全配置——不合规的 Pod/Deployment 直接创建不了。

# 禁止特权容器的 ConstraintTemplate
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sdisallowprivileged
spec:
  crd:
    spec:
      names:
        kind: K8sDisallowPrivileged
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sdisallowprivileged

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          container.securityContext.privileged == true
          msg := sprintf("Privileged container %v is not allowed", [container.name])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.initContainers[_]
          container.securityContext.privileged == true
          msg := sprintf("Privileged init container %v is not allowed", [container.name])
        }        
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDisallowPrivileged
metadata:
  name: no-privileged-containers
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    excludedNamespaces: ["kube-system"]

实际部署中,建议先开 warn 模式(只告警不拦截),观察一段时间,收集哪些 Pod 会被拦截。确认影响范围后再切到 deny 模式。一上来就 deny 很容易把正在运行的业务搞挂。

Falco:运行时安全监控

前面所有工具都是"静态"检查——查配置、查镜像、查准入。但攻击发生在运行时。有人通过漏洞拿到 Pod Shell 开始横向移动,静态扫描发现不了。Falco 就是用在这里的。

Falco 在内核层面(通过 eBPF 或内核模块)监控系统调用,根据规则匹配异常行为:

# Falco 规则示例:检测容器内 Shell 执行
- rule: Shell Spawned in Container
  desc: Detect shell spawned in container (possible attack)
  condition: >
    container.id != "" and
    proc.name in (bash, sh, zsh, ksh)    
  output: >
    Shell spawned in container
    (user=%user.name container_id=%container.id
    container_name=%container.name
    shell=%proc.name cmdline=%proc.cmdline)    
  priority: WARNING
  tags: [container, shell, mitre_execution]

Falco 规则覆盖了常见攻击模式:容器内 Shell 执行、文件系统异常读写、网络连接到可疑 IP、提权行为等。告警可以推到 Slack/PagerDuty/ELK。

部署 Falco 推荐 DaemonSet 模式,每个节点都跑:

helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
helm install falco falcosecurity/falco \
  --namespace falco \
  --create-namespace \
  --set driver.kind=ebpf \
  --set falcosidekick.enabled=true \
  --set falcosidekick.config.slack.webhookurl=$SLACK_WEBHOOK

eBPF 模式不需要内核模块,但要求内核版本 5.2+。低版本内核用 falcoctl 加载模块的方式。

一次完整的加固流程

把前面的内容串起来,实际操作中从零开始加固一个集群的标准流程:

步骤 1: kube-bench 扫描 → 获取 FAIL 清单
步骤 2: 按决策矩阵排优先级
步骤 3: 修复 P0/P1 项(API Server 参数、etcd 认证、RBAC、审计日志)
步骤 4: Trivy 扫描所有运行镜像 → 修复 CRITICAL 漏洞镜像
步骤 5: 部署 PSA 标签 → 限制 Pod 安全级别
步骤 6: 部署 Gatekeeper → 拦截不安全配置(先 warn 模式)
步骤 7: 部署 Falco → 运行时监控
步骤 8: 把 kube-bench + Trivy 扫描加到 CronJob + CI/CD
步骤 9: Grafana 建合规率大盘 → 持续监控

整个流程不是一天搞完的。中小型集群(50 节点以下),从步骤 1 到步骤 8,大概需要 1-2 周。大型集群花的时间更多,主要在步骤 5-6 的业务适配上——PSA 和 Gatekeeper 可能影响到现有工作负载,需要逐个 namespace 评估和迁移。

常见踩坑记录

坑 1:kube-bench 在 kubeadm 集群跑出大量 FAIL

kubeadm 默认部署的集群确实有不少 CIS Benchmark FAIL 项。别慌,kubeadm 的目标是易用性而非安全加固。大部分 FAIL 项按 remediation 改就行,影响面不大。但改之前一定先备份 /etc/kubernetes/manifests/ 下的 yaml 文件,改错了 API Server 起不来。

坑 2:修复参数后 API Server 起不来

API Server 的启动参数改错了(比如证书路径写错),Pod 会 CrashLoopBackOff,整个集群不可用。解决方案:在节点上直接编辑 /etc/kubernetes/manifests/kube-apiserver.yaml,kubelet 会自动检测变化并重启 Pod。改之前先备份,改之后看 journalctl -u kubelet -f 确认 Pod 正常启动。

坑 3:托管集群 kube-bench 跑不出结果

EKS/GKE/AKS 的控制平面不可达,kube-bench 挂载的那些 hostPath 在托管节点上路径不一样。用对应的 --benchmark eks-1.8.0--benchmark gke-1.8.0,并确保节点上 containerd/docker 配置文件路径正确。

坑 4:Trivy 离线环境扫描失败

Trivy 需要下载漏洞数据库(trivy-db)。离线环境会卡在下载步骤。解决方法:

# 在有网环境下载数据库
trivy --download-db-only

# 把数据库拷到离线环境
# 默认路径:~/.cache/trivy/db/

# 离线环境扫描时指定 --skip-db-update
trivy image --skip-db-update nginx:1.27

坑 5:Falco 规则误报太多

Falco 默认规则比较激进,特别是容器内进程执行和网络连接规则。建议先跑 notice 级别观察一周,收集基线,然后针对高误报规则加白名单。别一上来把所有规则设为 critical,告警风暴会让团队麻木。

总结

K8s 安全不是一个工具能解决的,它是一个分层防御体系:

  1. 配置层:kube-bench 跑 CIS Benchmark,确保基线合规
  2. 镜像层:Trivy 扫描镜像漏洞和配置错误
  3. 准入层:Gatekeeper/Kyverno 在 API Server 前拦截不安全配置
  4. 运行时:Falco 监控异常行为
  5. 持续化:把扫描自动化到 CI/CD 和定时任务

实操建议:第一次跑 kube-bench 别被一堆 FAIL 吓到,先按风险矩阵排优先级,从 P0 开始改。托管集群用户注意用对应版本 benchmark,别拿通用 cis benchmark 跑出大量 N/A 还以为出了问题。镜像扫描必须进流水线,手动扫描等于没扫——今天扫了明天构建的新镜像又带新漏洞了。

Gatekeeper 和 PSA 切到 deny 模式之前先跑 warn 模式观察。一上来就拦截,很可能把正在跑的业务 Pod 搞挂。安全加固要渐进推进,不是一刀切。

最后说句实在话:安全没有"做完"的一天。CVE 每天都在出,K8s 版本每三个月发一次,你今天 100% 合规的集群下个月可能就冒出新 FAIL。关键是建立持续扫描的机制,让问题在引入时就被发现,而不是等安全审计时才仓促补救。

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. kube-bench GitHub 仓库 — Aqua Security,CIS Kubernetes Benchmark 自动化检查工具及配置文档
  2. CIS Kubernetes Benchmark 官方文档 — Center for Internet Security,K8s 安全配置基线标准
  3. Trivy GitHub 仓库 — Aqua Security,容器镜像及 K8s 集群漏洞扫描工具,KBOM 功能参考
  4. kube-hunter GitHub 仓库 — Aqua Security,K8s 渗透测试工具
  5. Security and Hardening of Kubernetes in Public Clouds: A Comparative Study of EKS, AKS, and GKE — IEEE,kube-bench 在 EKS/AKS/GKE 上的对比分析研究
  6. Kubernetes 安全指南 2025 — 云安全联盟大中华区,基于 ATT&CK 模型的 K8s 攻防矩阵分析
  7. AKS 安全公告 — Microsoft Azure,Ubuntu 22.04 节点映像内核 CVE 分类变更信息
  8. Comparative Analysis of Lightweight Kubernetes Distributions for Edge Computing — Springer,kube-bench 在 k3s/k0s/KubeEdge/OpenYurt 上的安全合规对比