WEBKT

DevSecOps 闭环:如何将镜像扫描结果强制引入 K8s 准入控制(Admission Control)

88 0 0 0

在 DevSecOps 的实践中,很多团队仅仅停留在“在 CI 流水线里跑一下扫描”的阶段。然而,如果扫描结果只是发一份邮件或者留在 Dashboard 里,而没有在集群入口处进行拦截,那么“左移安全”就只是一句空话。

要实现真正的安全闭环,必须通过 Kubernetes Admission Controller (准入控制器),根据镜像扫描的实时状态,决定是否允许该 Pod 运行。

一、 核心逻辑架构

实现镜像扫描与准入控制联动的标准流程通常分为三个环节:

  1. 扫描触发与元数据持久化:镜像推送到仓库(如 Harbor)后自动触发扫描,结果存入数据库或通过签名工具(如 Cosign)生成证明。
  2. 准入请求截获:当用户执行 kubectl apply 时,K8s API Server 将请求转发给 Validating Admission Webhook
  3. 决策判定:Webhook 查询镜像的安全分值、漏洞等级(CVE Severity)或签名状态,返回 allowdeny

二、 方案一:使用原生策略引擎(推荐方案)

与其从零开发一个 Webhook Server,目前工业界的最佳实践是使用 Gatekeeper (OPA)Kyverno

1. 基于 Kyverno 的实现

Kyverno 相比 OPA 的优势在于它直接使用声明式的 YAML,不需要学习 Rego 语言。

场景描述:只允许部署漏洞等级(Severity)低于 High 的镜像。

实现思路

  • Image Verification:利用 Kyverno 的 verifyImages 特性,配合 Cosign 检查镜像是否经过了流水线的安全认证签名。
  • 外部查询:Kyverno 可以配置查询 OCI 仓库中的符号链接(Attestations),如果镜像没有对应的“扫描通过证明”,则拒绝进入。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-image-vulnerabilities
spec:
  validationFailureAction: Enforce
  rules:
    - name: scan-check
      match:
        any:
        - resources:
            kinds:
            - Pod
      verifyImages:
      - imageReferences:
        - "my-registry.com/*"
        attestations:
        - predicateType: cosign.sigstore.dev/attestation/vuln/v1
          conditions:
          - all:
            - key: "{{ json_decode(content).metadata.scan_status }}"
              operator: Equals
              value: "passed"

2. 基于 OPA Gatekeeper 的实现

如果你需要更复杂的逻辑(例如:根据不同的 Namespace 设置不同的安全等级),OPA 的 Rego 语言更具表现力。

你需要编写一个 ConstraintTemplate,在其中通过 http.send 实时调用镜像仓库(如 Harbor API)获取漏洞报告,或者查询内存中缓存的扫描白名单。


三、 方案二:自建 Validating Webhook

如果企业内部有复杂的权限校验逻辑(例如需要对接内部自研的 SoC 安全平台),则需要自建 Webhook。

技术细节

  • 输入AdmissionReview 对象,包含 Pod 的所有 Container 镜像 Tag。
  • 逻辑
    1. 解析镜像地址。
    2. 调用后端 API(Trivy API 或 Harbor API)查询该 Tag 的 vulnerability_summary
    3. Fail-close vs Fail-open:如果扫描服务宕机,是允许还是禁止?(生产环境通常建议 Fail-open 并记录审计日志)。
  • 输出AdmissionResponse,并在 message 中明确告知开发者由于哪些 CVE 导致部署失败。

四、 关键实战挑战与避坑指南

1. 扫描延迟问题 (Race Condition)

现象:镜像刚推送,CI 还没扫完,CD 流程就开始部署,导致准入控制器查不到结果。
对策

  • 阻塞 CI:确保 CI 流水线在扫描返回结果前不结束。
  • 中间状态判定:如果状态为 Scanning,Webhook 返回禁止,并提示“镜像扫描中,请稍后重试”。

2. 避免 API 性能瓶颈

每启动一个 Pod 都要实时调接口查漏洞,会导致 API Server 延迟增加。
对策

  • 本地缓存:Webhook 内部维护一个短时间的缓存(如 Redis),存储镜像 Tag 与安全结论的映射。
  • 签名化(Attestation):这是最优雅的方案。CI 扫完后,将结果作为签名附加到镜像仓库中。Webhook 只校验签名是否存在且内容合法,无需发起跨网络请求。

3. 排除特定命名空间

不要对 kube-system 或监控组件进行镜像扫描拦截,否则可能导致集群基础组件故障时无法自动恢复。


五、 总结:DevSecOps 的分级防御架构

  1. 第一道防线 (IDE/Git):开发者提交代码时,SAST 静态扫描发现基础镜像漏洞。
  2. 第二道防线 (Registry):镜像推送到私有仓库时触发自动扫描。
  3. 第三道防线 (Admission Control):部署时执行“最后一公里”的拦截,确保只有通过审核的镜像能跑在集群内。

通过这种方式,安全人员不再是去“救火”,而是定义好准入规则,让整个安全治理过程自动化、透明化。

云原生安全老兵 KubernetesDevSecOps镜像安全

评论点评