概述
你管着一个 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.28 | cis-1.9 |
| 1.29 - 1.30 | cis-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/A 或 WARN。这不是你的问题——云厂商负责控制平面安全。
但有几个检查维度是你必须自己管的:
| 检查维度 | 工具 | 你的责任 |
|---|---|---|
| 节点安全组 | 云平台控制台 | 限制 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-bench | CIS Benchmark 合规检查 | 权威标准、检查项全面 | 只查配置不查漏洞 |
| Trivy | 镜像/文件系统/IaC 漏洞扫描 | 多维度扫描、KBOM 支持 | 需要联网更新漏洞库 |
| kube-hunter | 渗透模拟 | 攻击者视角、发现利用链 | 误报较多 |
| OPA Gatekeeper | 策略引擎 | 准入控制、实时拦截 | 学习成本高 |
| Falco | 运行时安全监控 | 实时检测异常行为 | 规则编写复杂 |
| Kyverno | 策略引擎 | 原生 K8s 风格、易上手 | 功能不如 OPA 灵活 |
| KubeClarity | 应用安全扫描 | 全生命周期覆盖 | 社区较小 |
我的推荐组合:
- kube-bench:跑基线,每周一次
- Trivy:CI/CD 流水线镜像扫描 + 定期全集群扫描
- OPA Gatekeeper 或 Kyverno:准入控制,阻止不安全配置进入集群
- 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 安全不是一个工具能解决的,它是一个分层防御体系:
- 配置层:kube-bench 跑 CIS Benchmark,确保基线合规
- 镜像层:Trivy 扫描镜像漏洞和配置错误
- 准入层:Gatekeeper/Kyverno 在 API Server 前拦截不安全配置
- 运行时:Falco 监控异常行为
- 持续化:把扫描自动化到 CI/CD 和定时任务
实操建议:第一次跑 kube-bench 别被一堆 FAIL 吓到,先按风险矩阵排优先级,从 P0 开始改。托管集群用户注意用对应版本 benchmark,别拿通用 cis benchmark 跑出大量 N/A 还以为出了问题。镜像扫描必须进流水线,手动扫描等于没扫——今天扫了明天构建的新镜像又带新漏洞了。
Gatekeeper 和 PSA 切到 deny 模式之前先跑 warn 模式观察。一上来就拦截,很可能把正在跑的业务 Pod 搞挂。安全加固要渐进推进,不是一刀切。
最后说句实在话:安全没有"做完"的一天。CVE 每天都在出,K8s 版本每三个月发一次,你今天 100% 合规的集群下个月可能就冒出新 FAIL。关键是建立持续扫描的机制,让问题在引入时就被发现,而不是等安全审计时才仓促补救。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- kube-bench GitHub 仓库 — Aqua Security,CIS Kubernetes Benchmark 自动化检查工具及配置文档
- CIS Kubernetes Benchmark 官方文档 — Center for Internet Security,K8s 安全配置基线标准
- Trivy GitHub 仓库 — Aqua Security,容器镜像及 K8s 集群漏洞扫描工具,KBOM 功能参考
- kube-hunter GitHub 仓库 — Aqua Security,K8s 渗透测试工具
- Security and Hardening of Kubernetes in Public Clouds: A Comparative Study of EKS, AKS, and GKE — IEEE,kube-bench 在 EKS/AKS/GKE 上的对比分析研究
- Kubernetes 安全指南 2025 — 云安全联盟大中华区,基于 ATT&CK 模型的 K8s 攻防矩阵分析
- AKS 安全公告 — Microsoft Azure,Ubuntu 22.04 节点映像内核 CVE 分类变更信息
- Comparative Analysis of Lightweight Kubernetes Distributions for Edge Computing — Springer,kube-bench 在 k3s/k0s/KubeEdge/OpenYurt 上的安全合规对比