关于 container-audit
container-audit 是面向容器和 Kubernetes 环境的安全审计技能,供审计者检查镜像、构建文件及编排配置。它以核实攻击路径、记录证据和区分确认结果与待验证风险为重点。
主要功能
- 01审计镜像、Dockerfile 与 Kubernetes 配置
- 02检查权限、机密及运行时安全
- 03使用验证流程分类审计发现
- 04记录证据并校准严重程度
适用场景
- 01审查 Dockerfile 和基础镜像的安全配置
- 02检查 Kubernetes 清单中的特权及 RBAC 风险
- 03评估仓库中机密暴露或镜像风险
- 04根据集群证据验证配置问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ briiirussell/ cybersecurity-skills --skill container-audit安装后在 Claude Code 中输入/container-audit确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/findings-format.mdfindings.schema.jsonseverity-calibration.mdto-sarif.mjsvalidate-findings.mjsverification.md详细介绍
容器审计 — Docker 与 Kubernetes 安全审查
审查容器镜像、Dockerfile、Helm chart、Kustomize overlay 和 Kubernetes 清单,检查错误配置、权限过大、机密暴露和运行时安全缺口。此技能不同于 cloud-audit(云服务提供商的 IAM 和托管服务)和 dependency-audit(应用中的软件包 CVE)。它负责介于两者之间的容器/编排层。
授权
仅审计用户拥有或获准审查的镜像、镜像仓库和集群。trivy 和 kube-bench 只读取信息,不会更改集群。审计期间不要对工作负载运行 kubectl exec、应用清单或删除 Pod;也绝不要尝试在共享或生产集群上逃逸容器:描述逃逸路径,并让所有者在隔离环境中验证。
确定审计范围
- 梳理审计对象 — Dockerfile、基础镜像、镜像仓库、Helm chart、K8s 清单、Kustomize overlay、用于构建镜像的 CI 流水线
- 确定运行环境 — 原生 K8s、EKS、GKE、AKS、OpenShift、ECS Fargate、Cloud Run、Fly.io
- 确定网络模型 — 服务网格、入口控制器、默认拒绝还是默认允许
- 确定机密管理模型 — K8s Secrets(仅 base64 编码)、External Secrets Operator、sealed-secrets、Vault、Doppler
审计清单 — Dockerfile
基础镜像与供应链
- 使用摘要固定,而非标签 —
FROM node:20@sha256:abc...,而不是FROM node:20(后者可能发生变动) - 尽可能使用 Distroless/精简镜像 —
gcr.io/distroless/nodejs20、alpine(注意 musl 的特性)、chainguard/* - 不使用
:latest— 否则构建无法复现 - 多阶段构建会丢弃构建时工具 —
FROM build AS builder→ 最终阶段为FROM runtime - 搜索:
FROM .*:latest、FROM .*:[0-9]+$(有标签但无摘要)
构建时暴露风险
- 通过
--build-arg传入的机密会留在镜像层中,任何拉取镜像的人都能看到 — 改用 BuildKit secrets(--mount=type=secret)或运行时环境变量 COPY . .会将构建上下文中的所有内容打包进去 —.dockerignore应排除.git、.env、node_modules、*.pem、.aws/、.ssh/ADD <url>会跟随重定向并禁用校验和验证 — 建议改用RUN curl ... && sha256sum -c- 搜索:
ARG .*KEY、ARG .*TOKEN、ARG .*SECRET、ENV .*=.*[A-Za-z0-9]{32,}、ADD http
运行时安全态势
- 使用非 root 用户 — 在
CMD之前设置USER 1001(或任何非零 UID) - 最终镜像中不得包含
chmod 4755设置的 SUID 二进制文件 - 最终阶段不包含不必要的 shell/包管理器 — Distroless/FROM scratch 是最稳妥的默认选择
- 定义
HEALTHCHECK,以便编排器检测容器是否运行异常 - 运行时将根文件系统设为只读(通过 K8s 设置;确认镜像中没有任何内容会写入
/tmp或声明的卷以外的位置) - 搜索:
USER root(或未设置任何USER指令)、chmod 4755、apt-get install.*sudo
审计清单 — Kubernetes 清单
Pod 安全
- 设置
securityContext.runAsNonRoot: true,并将runAsUser设为非零 UID - 设置
securityContext.allowPrivilegeEscalation: false - 设置
securityContext.readOnlyRootFilesystem: true,并为应用需要写入的位置显式挂载emptyDir - 设置
securityContext.capabilities.drop: ["ALL"],然后仅添加必需的能力 - 应用工作负载中的
securityContext.privileged绝不能为true(Falco、kube-proxy 和某些 CSI 驱动程序是少数合理的例外) hostNetwork、hostPID、hostIPC均应为false— 将这些项设为true,意味着“容器可以查看节点/与节点通信”hostPath卷 — 每一个都存在节点逃逸风险;需逐一审查- 搜索:
privileged: true、runAsUser: 0、hostNetwork: true、hostPath:
Pod 安全标准(PSS)/准入
- 集群通过 PSS 准入强制执行
restricted配置文件,或通过 OPA Gatekeeper / Kyverno 实现等效策略 - Pod 安全策略(自 1.21 起弃用,已在 1.25 中移除)并未用于强制执行此策略——请确认当前使用了准入控制器
kube-system命名空间中没有运行应用代码的工作负载
网络
- 每个运行应用工作负载的命名空间都配置了
NetworkPolicy——默认拒绝入站和出站流量,然后允许特定 Pod 通信 - 缺少 NetworkPolicy = 每个 Pod 都可以通过任意端口与其他所有 Pod 通信,包括 kube-apiserver 和元数据服务
- 敏感命名空间中的服务网格(Istio、Linkerd)将 mTLS 设为 STRICT 模式,而非 PERMISSIVE
- Ingress 控制器正确终止 TLS;K8s Secrets 中的后端
tls.crt/tls.key会轮换
密钥
- K8s Secrets 使用 base64 编码,而非加密——默认情况下,它们在 etcd 中是明文数据
- 启用 etcd 静态加密——在 kube-apiserver 上配置
--encryption-provider-config - 工作负载通过投射卷使用密钥,而非环境变量(环境变量会通过
/proc/<pid>/environ、错误报告和崩溃转储泄露) - 使用 External Secrets Operator / Vault / sealed-secrets 桥接,确保 Git 仓库中从不包含明文
- 搜索:Git 中包含
data:字段的kind: Secret(已提交的 base64 编码值)
RBAC
- 除
cluster-admin外,不存在对*资源具有*动词的ClusterRole(审计绑定到该角色的对象) - 每个工作负载使用独立的
ServiceAccount,而非共享的“default” SA - 对不需要 API 访问权限的工作负载设置
automountServiceAccountToken: false - 绑定
system:authenticated组会让所有合法工作负载都能看到这些绑定——这几乎总是错误的 - 搜索:
verbs: ["*"]、resources: ["*"]、apiGroups: ["*"]、system:authenticated
资源限制
- 每个容器都设置了
resources.requests和resources.limits——缺少限制 = 邻居干扰 + DoS 攻击面(一个 Pod 就能耗尽节点资源) - 每个命名空间都配置
LimitRange作为后备保障 - 每个命名空间都配置
ResourceQuota,防止租户之间互相耗尽资源
镜像策略
- 对
:latest使用imagePullPolicy: Always(不应该使用 :latest,但如果使用了)——否则节点会缓存过时的镜像 - 集群级策略要求所有镜像都来自获准的仓库(自有仓库 + 少量允许列表);通过 Gatekeeper / Kyverno / image-policy-webhook 强制执行
- 验证镜像签名——使用 cosign + sigstore policy controller,或 Notary v2
审计清单 — 运行时
- 在 CI 中扫描镜像——
trivy image、grype、docker scout cves。每次构建都必须运行;应显示安全公告,但不应因此阻止构建 - 运行时检测——Falco / Tracee / Tetragon 可捕获“从未打开过 shell 的 Pod 中启动了 shell”这类行为模式
- 启用审计日志——kube-apiserver 审计日志会记录
exec、attach、port-forward事件,以便响应安全事件 - 跟踪
kubectl exec访问——并非任何集群管理员都能悄悄进入生产环境的 shell
实用单行命令
# All Dockerfiles in the repo + their first FROM line
git ls-files | grep -E '(^|/)Dockerfile(\.|$)' | xargs -I{} sh -c 'echo "==> {}"; grep ^FROM "{}"'
# Manifests missing securityContext
grep -rL "securityContext" --include="*.yaml" --include="*.yml" .
# Manifests with privileged containers
grep -rln "privileged: *true" --include="*.yaml" --include="*.yml" .
# Manifests with hostPath volumes
grep -rln "hostPath:" --include="*.yaml" --include="*.yml" .
# Secrets in Git (base64-encoded but readable)
grep -rln "kind: *Secret" --include="*.yaml" --include="*.yml" . | xargs grep -l "^data:"
# Image scan (Trivy)
trivy image --severity HIGH,CRITICAL --exit-code 0 <image>
# Manifest scan (Trivy)
trivy config --severity HIGH,CRITICAL .
# Cluster posture (kube-bench, run inside the cluster)
kube-bench run --targets master,node,policies
验证,然后记录
按照 references/findings-format.md 中的共享格式,为清单中的每个候选项给出一个结论。在将任何候选项标记为 confirmed 之前,先执行 references/verification.md 中的反证检查:从引用的清单或 Dockerfile 行开始,寻找能阻止问题发生的最有效控制措施(准入策略、命名空间 PSS 标签、覆盖默认值的 Helm 配置,或会丢弃该层的后续构建阶段)。
只有在能够明确说明谁获得了什么时,配置错误才算一项发现。根据 references/severity-calibration.md 中的标准记录:低信任主体(仓库读取者、任何能够拉取镜像的人、能够创建 Pod 的命名空间用户、在面向互联网的 Pod 中取得代码执行权限的攻击者)、跨越的边界(从容器到节点、从命名空间到集群、从仓库到生产密钥)以及造成的结果。
confirmed — 配置位于仓库中或已保存的集群输出中,且已追踪到结果的路径。示例:
k8s/database.yaml中的kind: Secret带有一个 base64data:值,解码后是生产数据库密码:任何拥有仓库读取权限的人都能访问数据库。根据权限范围评级,参照严重性文件中的密钥表。- 一个
ClusterRoleBinding将system:authenticated绑定到拥有集群范围内getsecrets权限的角色:任何 Pod 的服务账户令牌都能读取所有 Secret。 - 一个随附的 Helm chart,其
values.yaml默认设置privileged: true,并挂载hostPath: /。此问题通过system_configuration条件确认(“未覆盖securityContext.privileged的部署”),置信度为medium。
需要先在容器中执行代码的逃逸路径(privileged、hostPath、hostPID、CAP_SYS_ADMIN)应带有说明这一点的 environmental_dependency 条件。这会降低可能性,而不会降低到达节点后的影响。
needs_validation — 仓库显示存在风险,但决定性事实位于集群或镜像仓库中。示例:
- 一个没有
securityContext的 Deployment,其命名空间的 PSSenforce标签由平台团队在此仓库之外设置。阻碍因素:命名空间标签。计划:运行kubectl get ns payments -o jsonpath='{.metadata.labels}'和kubectl apply --dry-run=server -f k8s/api.yaml(会运行准入检查,但不会持久化任何内容)。 - 最终阶段使用了
ARG NPM_TOKEN。阻碍因素:谁能拉取registry.example.com/api。计划:查看镜像仓库的仓库策略,并对已推送的镜像运行docker history --no-trunc。
rejected — 指定位置的具名控制会阻止所有路径上的问题。示例:
charts/agent/templates/daemonset.yaml中的hostPath被处于Enforce模式的 KyvernoClusterPolicy排除(policies/disallow-host-path.yaml),其匹配范围涵盖该命名空间。ARG NPM_TOKEN仅在builder阶段使用;ruled_out_by.location指向最终阶段的FROM行,该阶段从干净的基础镜像开始,且只复制/app/dist。- 一个值为
REPLACE_ME占位符的kind: Secret,或一个仅含密文的SealedSecret。
加固说明,不作记录: 当没有已追踪的路径依赖某项措施时,记录缺失的单层防护。示例:使用标签而非摘要、未设置 readOnlyRootFilesystem、单租户集群中缺少资源限制、在没有主机挂载的非特权 Pod 中以 root 身份运行、没有 HEALTHCHECK。只有在指出其暴露的可访问目标(未经身份验证的内部服务、带有节点凭据的元数据端点)时,缺少 NetworkPolicy 才会成为一项发现。
镜像 CVE 遵循 dependency-audit 的规则:只有证明存在漏洞的软件包可从容器运行的内容中访问时,才将其列为发现。否则应标记为 needs_validation 或加固说明。
集群证据。 关于在线集群、镜像仓库或镜像(而非仓库中的文件)的发现,应指向已保存的输出。将只读命令的输出脱敏后,保存到 <audit dir>/evidence/<name>.json 或 .txt(例如 kubectl get clusterrolebindings -o json、kube-bench run --json、trivy image -f json),并将 location 和 evidence[] 指向该文件及行号。切勿保存 Secret 值:删除 .data 和 .stringData(kubectl get secret -o json | jq 'del(.items[].data, .items[].stringData)'),并对环境变量中的令牌脱敏。描述了但未运行的检查应标记为 needs_validation。
指纹: container-audit/<class>/<file or resource>/<name>,不含行号、镜像标签或摘要:
container-audit/privileged-container/charts/agent/values.yaml/securityContext.privilegedcontainer-audit/secret-in-git/k8s/database.yaml/db-credentialscontainer-audit/rbac-wildcard/cluster/prod-eks/ClusterRoleBinding/ci-deployercontainer-audit/CVE-2024-3094/image/registry.example.com/api/xz-utils编写镜像引用时不要带标签(registry.example.com/api,而不是api:1.4):验证器会拒绝包含:<digits>的片段。将标签和摘要写入证据描述中。
ext 键(可选,适用于任何记录):
| 键 | 值 | 示例 |
|---|---|---|
resource | Kubernetes 类型、命名空间和名称,或镜像仓库 | "Deployment/payments/api"、"registry.example.com/api" |
cis | CIS Docker 或 Kubernetes 基准控制项 | "CIS Kubernetes 5.1.1"、"CIS Docker 4.1" |
pss | 配置违反的 Pod Security Standards 级别 | "baseline"、"restricted" |
然后按指纹排序编写 findings.json,对其进行验证,并可选择导出 SARIF:
node references/validate-findings.mjs findings.json
node references/to-sarif.mjs findings.json -o findings.sarif
在运行时验证修复
runAsNonRoot: true— 验证 Pod 能够正常重启并保持 Running;如果镜像的ENTRYPOINT调用了chown,Pod 将陷入崩溃循环- NetworkPolicy 默认拒绝 — 验证合法流量仍能正常工作(在集群内运行
kubectl run -it --rm debug ... curl);默认拒绝策略推出后,常会发生无声的局部中断 readOnlyRootFilesystem: true— 验证应用不会在声明的emptyDir挂载点之外写入数据;日志写入、PID 文件和临时文件常会导致故障- 镜像策略强制执行 — 尝试部署未签名或不在允许列表中的镜像;验证准入控制会拒绝该镜像
输出格式
先编写 findings.json 并对其进行验证。根据该文件和你的加固备注列表生成报告。无论文字描述如何,都不得更改 JSON 中未包含的判定结果、严重程度或阻碍项。
# Container Security Audit
## Target: [cluster name / image registry / repo path] ## Date: [date]
## Scope: [full | partial — what was left out, e.g. "manifests only, no cluster access"]
## findings.json: [path] — validated: [yes]
### Summary
- Confirmed: [N] (Critical [n] | High [n] | Medium [n] | Low [n]) Needs validation: [N] Rejected: [N] Hardening notes: [N]
- Audited: [N] Dockerfiles, [N] manifests / charts, [N] images; cluster checks: [pass / fail counts, or "not run"]
### Confirmed findings
#### [SEVERITY] [Title] `fingerprint`
**Location:** `path/to/manifest.yaml:42` (or `audit/evidence/<name>.json:<line>`) **Confidence:** [medium | high (reproduced)]
**Category:** Dockerfile | Pod security | RBAC | NetworkPolicy | Secrets | Resource limits | Image policy | Runtime
**Principal → boundary → result:** [who, what they cross, what happens]
**Conditions:** [e.g. environmental_dependency: needs code execution in the pod]
**Vulnerable config:**
```yaml
[snippet]
```
**Remediation:**
```yaml
[fixed snippet]
```
**Verify the fix:** [observed behaviour that proves it holds, see Verify Fixes at Runtime]
### Needs validation
| Finding | Blockers | Check for the owner to run |
|---|---|---|
### Hardening notes (not findings)
- [location] — [gap] — [why it isn't a finding] — [effort]
### Rejected (do not re-raise)
| Fingerprint | Claim | Ruled out by | Location |
|---|---|---|---|
### Cluster posture
| Control | Status | Evidence |
|---|---|---|
| PSS / admission (Gatekeeper, Kyverno) | | |
| NetworkPolicy default-deny | | |
| etcd encryption at rest | | |
| API server audit logging | | |
| Image signature / registry policy | | |
| Runtime detection (Falco, Tetragon) | | |
### Prioritized remediation plan
1. [Critical / High confirmed findings]
2. [Medium / Low confirmed findings]
3. [Owner-run checks for needs-validation items]
4. [Hardening notes worth the effort]
已确认发现项的处置方式(稍后设置,通常由 finding-triage 设置):已修复 / 已延期 / 接受风险 / 已缓解(平台/配置)/ 需要重大迁移。误报应记录为 rejected 记录,而不是处置方式。
边界
- 仅审计用户提供或获准访问的集群和镜像仓库
- 审计期间绝不运行
kubectl delete或修改集群状态 — 仅允许只读操作(get、describe、auth can-i) - 收集运行时证据时,优先使用不会造成干扰的检查(
kubectl run -it --rm临时调试 Pod),而不是修改正在运行的工作负载 - 拒绝接管集群的场景 — 从发现弱点升级为全面渗透属于利用行为,而非审计
- 如果无法根据代码库或已保存的输出确定某项发现,应记录为
needs_validation,并注明阻碍项和检查方式,绝不能标记为已确认
参考资料
- CIS Docker Benchmark
- CIS Kubernetes Benchmark
- NSA/CISA Kubernetes Hardening Guide
- Pod Security Standards (PSS) — restricted、baseline、privileged
- OWASP Docker Security Cheat Sheet
- OWASP Kubernetes Security Cheat Sheet
- MITRE ATT&CK for Containers
- 共享参考资料(构建时复制到
references/):findings-format.md、severity-calibration.md、verification.md、validate-findings.mjs、to-sarif.mjs、findings.schema.json