mcpField
C
container-audit

container-audit

github.com/briiirussell安全478356 次安装1 天前更新
代码仓库

关于 container-audit

container-audit 是面向容器和 Kubernetes 环境的安全审计技能,供审计者检查镜像、构建文件及编排配置。它以核实攻击路径、记录证据和区分确认结果与待验证风险为重点。

主要功能

  1. 01审计镜像、Dockerfile 与 Kubernetes 配置
  2. 02检查权限、机密及运行时安全
  3. 03使用验证流程分类审计发现
  4. 04记录证据并校准严重程度

适用场景

  1. 01审查 Dockerfile 和基础镜像的安全配置
  2. 02检查 Kubernetes 清单中的特权及 RBAC 风险
  3. 03评估仓库中机密暴露或镜像风险
  4. 04根据集群证据验证配置问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/briiirussell/cybersecurity-skills --skill container-audit

安装后在 Claude Code 中输入/container-audit确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
findings-format.md
findings.schema.json
severity-calibration.md
to-sarif.mjs
validate-findings.mjs
verification.md

详细介绍

容器审计 — Docker 与 Kubernetes 安全审查

审查容器镜像、Dockerfile、Helm chart、Kustomize overlay 和 Kubernetes 清单,检查错误配置、权限过大、机密暴露和运行时安全缺口。此技能不同于 cloud-audit(云服务提供商的 IAM 和托管服务)和 dependency-audit(应用中的软件包 CVE)。它负责介于两者之间的容器/编排层。

授权

仅审计用户拥有或获准审查的镜像、镜像仓库和集群。trivy 和 kube-bench 只读取信息,不会更改集群。审计期间不要对工作负载运行 kubectl exec、应用清单或删除 Pod;也绝不要尝试在共享或生产集群上逃逸容器:描述逃逸路径,并让所有者在隔离环境中验证。

确定审计范围

  1. 梳理审计对象 — Dockerfile、基础镜像、镜像仓库、Helm chart、K8s 清单、Kustomize overlay、用于构建镜像的 CI 流水线
  2. 确定运行环境 — 原生 K8s、EKS、GKE、AKS、OpenShift、ECS Fargate、Cloud Run、Fly.io
  3. 确定网络模型 — 服务网格、入口控制器、默认拒绝还是默认允许
  4. 确定机密管理模型 — 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 带有一个 base64 data: 值,解码后是生产数据库密码:任何拥有仓库读取权限的人都能访问数据库。根据权限范围评级,参照严重性文件中的密钥表。
  • 一个 ClusterRoleBinding 将 system:authenticated 绑定到拥有集群范围内 get secrets 权限的角色:任何 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,其命名空间的 PSS enforce 标签由平台团队在此仓库之外设置。阻碍因素:命名空间标签。计划:运行 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 模式的 Kyverno ClusterPolicy 排除(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.privileged
  • container-audit/secret-in-git/k8s/database.yaml/db-credentials
  • container-audit/rbac-wildcard/cluster/prod-eks/ClusterRoleBinding/ci-deployer
  • container-audit/CVE-2024-3094/image/registry.example.com/api/xz-utils 编写镜像引用时不要带标签(registry.example.com/api,而不是 api:1.4):验证器会拒绝包含 :<digits> 的片段。将标签和摘要写入证据描述中。

ext 键(可选,适用于任何记录):

键值示例
resourceKubernetes 类型、命名空间和名称,或镜像仓库"Deployment/payments/api"、"registry.example.com/api"
cisCIS 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,并注明阻碍项和检查方式,绝不能标记为已确认

参考资料

评论

支持 Markdown