WEBKT

K8s 安全进阶:基于 OPA Gatekeeper 实现细粒度的镜像拉取控制

5 0 0 0

在企业级的 Kubernetes (K8s) 集群管理中,镜像安全是供应链安全的第一道防线。如果允许开发者随意从公共镜像仓库(如 Docker Hub)拉取镜像,可能会引入包含漏洞的包、恶意脚本,甚至因为镜像版本混乱导致生产事故。

本文将详细探讨如何利用 OPA (Open Policy Agent) Gatekeeper 这一工业级标准工具,通过“策略即代码”(Policy as Code)的方式,为 K8s 集群构建一套细粒度的镜像拉取控制体系。

一、 为什么选择 OPA Gatekeeper?

传统的 K8s 准入控制主要依赖内置的 ImagePolicyWebhook 或早期的 PodSecurityPolicy(已废弃)。而 OPA Gatekeeper 的优势在于:

  1. 灵活性:使用声明式语言 Rego,可以编写极其复杂的逻辑。
  2. 可扩展性:支持自定义资源定义 (CRD),策略逻辑与配置分离。
  3. 审计功能:不仅能拦截违规请求,还能定期审计存量资源。

二、 核心架构简述

Gatekeeper 作为 Kubernetes 的一个 Validating Admission Webhook 运行。当用户尝试创建 Pod 时,API Server 会将请求发送给 Gatekeeper,Gatekeeper 根据预定义的策略(Constraint)进行校验,决定是 Allow 还是 Deny

三、 实战:限制镜像来源与标签

我们要实现两个核心目标:

  1. 仓库限制:只允许从公司内部私有仓库(如 harbor.example.com)拉取镜像。
  2. 标签控制:严禁在生产环境使用 :latest 标签,强制要求具体版本号。

1. 安装 Gatekeeper

首先,在集群中部署 Gatekeeper 服务:

kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yaml

2. 定义策略模板 (ConstraintTemplate)

ConstraintTemplate 定义了 Rego 逻辑。它像是一个函数,定义了“如何校验”。

创建一个名为 k8s-image-policy-template.yaml 的文件:

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8simagepolicy
spec:
  crd:
    spec:
      names:
        kind: K8sImagePolicy
      validation:
        openAPIV3Schema:
          type: object
          properties:
            allowedPrefixes:
              type: array
              items: { type: string }
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8simagepolicy

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          satisfied := [good | prefix := input.parameters.allowedPrefixes; startswith(container.image, prefix)]
          not any(satisfied)
          msg := sprintf("镜像 <%v> 不在许可名单内,请使用内网仓库镜像", [container.image])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          endswith(container.image, ":latest")
          msg := sprintf("镜像 <%v> 禁止使用 :latest 标签", [container.image])
        }

逻辑解释

  • 第一个 violation 块利用 startswith 函数检查镜像前缀是否在 allowedPrefixes 列表中。
  • 第二个 violation 块利用 endswith 检查镜像名是否以 :latest 结尾。

3. 实例化约束 (Constraint)

有了模板后,我们需要传入具体的参数。例如,指定合法的仓库地址。

创建 k8s-image-constraint.yaml

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sImagePolicy
metadata:
  name: image-must-be-from-harbor
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
  parameters:
    allowedPrefixes:
      - "harbor.example.com/"
      - "k8s.gcr.io/" # 视情况保留系统镜像库

四、 策略测试与验证

场景 A:尝试拉取 Docker Hub 镜像

kubectl run test-nginx --image=nginx:1.21

结果:请求会被拒绝,报错:admission webhook "validation.gatekeeper.sh" denied the request: 镜像 <nginx:1.21> 不在许可名单内...

场景 B:使用私有仓库但带了 latest 标签

kubectl run test-harbor --image=harbor.example.com/base/nginx:latest

结果:请求被拒绝,报错:镜像 <...> 禁止使用 :latest 标签

场景 C:合规请求

kubectl run test-ok --image=harbor.example.com/base/nginx:v1.21

结果:Pod 正常创建。

五、 进阶建议:Dry Run 与平滑过渡

在生产环境直接开启拦截(Deny)具有较大风险,可能导致现有的自动化流水线中断。Gatekeeper 提供了 enforcementAction 参数:

  • dryrun:只在日志和 Constraint 的 Status 中记录违规行为,不进行实际拦截。建议在策略上线初期使用,观察是否有漏掉的合法镜像。
  • warn:返回警告信息给用户,但允许请求通过。
spec:
  enforcementAction: dryrun # 或者 warn

六、 总结

通过 OPA Gatekeeper,我们将镜像拉取策略从“口头约束”转变为“代码强制”。这种细粒度的控制不仅提升了 K8s 集群的安全水位,还规范了开发者的部署习惯。

在实际应用中,建议将 Rego 策略文件纳入 GitOps 工作流,通过 CI/CD 自动同步到集群,实现策略的版本化管理。此外,除了镜像控制,你还可以扩展出 CPU/Memory 限制、Ingress 域名检测、特权容器禁用等更多安全策略。

技术架构师老王 KubernetesOPA云原生安全

评论点评