Kubernetes准入控制:使用Gatekeeper或Kyverno防止高危漏洞镜像部署
在容器化和微服务盛行的今天,Kubernetes已成为部署和管理应用的事实标准。然而,随着应用规模的增长,容器镜像的安全问题也日益突出。部署带有已知高危漏洞的镜像,无疑会给整个集群带来巨大的安全隐患。为了解决这一问题,Kubernetes提供了准入控制器(Admission Controllers)机制,它允许我们在资源对象持久化到etcd之前对其进行拦截和校验,从而实现对容器镜像的准入控制。
本文将深入探讨如何在Kubernetes集群中利用Gatekeeper和Kyverno这两款流行的策略引擎,实现对容器镜像的准入控制,有效阻止包含高危漏洞的镜像被部署。
1. 为什么需要容器镜像准入控制?
传统的安全策略往往在镜像构建阶段或运行时进行检查。然而,仅仅依靠这些阶段是不够的:
- 构建时检测:镜像构建后可能又发现了新的漏洞,或者开发人员可能跳过扫描流程。
- 运行时检测:此时漏洞已经进入生产环境,为时已晚,修复成本高昂。
准入控制则在部署(Pod创建)的前端进行拦截,它能在资源对象写入etcd之前进行校验,确保只有符合安全策略的镜像才能被部署。这是一种预防性的安全措施,能有效降低风险。
2. 前置条件:镜像漏洞扫描
无论是Gatekeeper还是Kyverno,它们本身都不具备扫描镜像漏洞的能力。要实现基于漏洞信息的准入控制,你首先需要一个独立的镜像漏洞扫描工具(如Trivy, Clair, Anchore Syft/Grype等)以及一个管理扫描结果的机制。通常,扫描结果会被存储在:
- 镜像仓库(如Harbor)中,作为镜像的元数据。
- 一个外部的安全数据库或服务中。
- 作为Pod或Deployment的Annotation。
本文将假设你已经有了一种获取镜像漏洞信息的方式,我们主要关注如何根据这些信息进行准入判断。
3. 使用Gatekeeper实现准入控制
Gatekeeper是基于Open Policy Agent (OPA)实现的Kubernetes准入控制器。它允许你使用Rego语言定义策略,并在集群中强制执行。
3.1 Gatekeeper工作原理
Gatekeeper通过两个核心组件工作:
- ValidatingAdmissionWebhook:拦截Kubernetes API请求,将请求发送给OPA引擎进行评估。
- OPA Gatekeeper Controller:管理ConstraintTemplate和Constraint资源,并将它们编译成Rego策略,供Webhook使用。
3.2 部署Gatekeeper
你可以使用Helm或kubectl部署Gatekeeper:
# 使用Helm部署
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm install gatekeeper gatekeeper/gatekeeper --namespace gatekeeper-system --create-namespace
# 或者使用kubectl部署(从官方GitHub获取YAML)
# kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.11/deploy/gatekeeper.yaml
3.3 编写基于Rego的漏洞准入策略
要防止部署含有高危漏洞的镜像,Gatekeeper需要知道哪些镜像是“高危”的。这通常涉及到与外部镜像扫描服务(如Harbor)或自定义的Webhook集成。
示例:基于镜像Annotation的简单策略(概念性)
假设你的CI/CD流程或一个独立的扫描器会将镜像的“最高漏洞等级”作为Pod Spec中容器镜像的Annotation。例如:
apiVersion: v1
kind: Pod
metadata:
name: my-app
annotations:
security.example.com/highest-vulnerability-severity: "CRITICAL" # 假设这是扫描结果
spec:
containers:
- name: my-container
image: myregistry.com/my-app:v1.0
我们可以编写一个Rego策略,阻止security.example.com/highest-vulnerability-severity为CRITICAL或HIGH的Pod部署。
第一步:定义ConstraintTemplate
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8simagevulncheck
spec:
crd:
spec:
names:
kind: K8sImageVulnCheck
validation:
openAPIV3Schema:
type: object
properties:
severities:
type: array
description: "列表,包含不允许的最高漏洞等级,如 ['CRITICAL', 'HIGH']"
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8simagevulncheck
deny[{"msg": msg}] {
container := input.review.object.spec.containers[_]
image_vuln_severity_annotation := input.review.object.metadata.annotations["security.example.com/highest-vulnerability-severity"]
# 将传入的允许等级列表转换为set,方便查找
forbidden_severities := {s | s := input.parameters.severities[_]}
# 如果镜像漏洞等级在不允许的列表中
forbidden_severities[image_vuln_severity_annotation]
msg := sprintf("容器 '%s' 使用的镜像存在高危漏洞,最高漏洞等级为 '%s',不允许部署。", [container.name, image_vuln_severity_annotation])
}
第二步:创建Constraint
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sImageVulnCheck
metadata:
name: deny-high-critical-vuln-images
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
# 你也可以根据命名空间、标签等进行匹配
parameters:
severities: ["CRITICAL", "HIGH"]
思考与局限: 上述示例需要 Pod 自身带有漏洞信息。更高级的用法是Gatekeeper与一个外部Webhook服务集成。这个Webhook服务会在接收到准入请求时,实时查询镜像扫描结果(例如从Harbor),然后返回一个判断结果给Gatekeeper。这种方式更灵活,但需要额外的开发工作。
4. 使用Kyverno实现准入控制
Kyverno是专为Kubernetes设计的策略引擎,它使用标准的Kubernetes资源来定义、管理和验证策略。与Gatekeeper/OPA的Rego语言不同,Kyverno策略使用YAML格式,更接近Kubernetes原生体验。
4.1 Kyverno工作原理
Kyverno作为Kubernetes的动态准入控制器运行,它观察传入的API请求,并根据定义的ClusterPolicy或Policy资源进行验证、修改或生成操作。
Kyverno的优势在于其内置的镜像验证能力,可以直接查询镜像的SBOM(软件物料清单)或验证镜像的签名,也可以通过验证ImageRegistry来检查镜像来源。
4.2 部署Kyverno
你可以使用Helm或kubectl部署Kyverno:
# 使用Helm部署
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno --namespace kyverno --create-namespace
# 或者使用kubectl部署(从官方GitHub获取YAML)
# kubectl apply -f https://raw.githubusercontent.com/kyverno/kyverno/main/config/install.yaml
4.3 编写基于Kyverno的漏洞准入策略
Kyverno可以直接检查镜像的元数据或引用外部扫描结果。
示例1:基于镜像签名的准入控制
虽然不是直接针对漏洞,但确保镜像来自可信来源是安全的第一步。Kyverno可以验证Sigstore Cosign签名的镜像。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-image-signatures
spec:
validationFailureAction: Enforce
rules:
- name: check-image-signatures
match:
resources:
kinds:
- Pod
validate:
foreach:
element: request.object.spec.containers[]
validate:
attestors:
- entries:
- keys:
# 这里的公钥应替换为你的Cosign公钥
# 例如:-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----
publicKeys: |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+d2gXj...
-----END PUBLIC KEY-----
示例2:基于外部漏洞扫描结果的准入控制(概念性)
Kyverno可以通过image规则的validate.match或validate.deny条件与外部扫描结果集成。最常见的做法是:
- 扫描工具将漏洞信息注入到镜像的标签或Annotation中。
- Kyverno策略检查这些标签或Annotation。
假设我们有一个自定义的ClusterAdmissionControl资源或者一个外部服务,可以提供镜像的漏洞信息。我们可以在Kyverno策略中查询这些信息。
更直接的方式是,Kyverno可以通过Webhooks或CRD来查询外部扫描器的数据。例如,如果你有一个漏洞扫描结果CRD,Kyverno可以查询它。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: block-images-with-high-vulns
spec:
validationFailureAction: Enforce
rules:
- name: check-image-vulnerabilities
match:
resources:
kinds:
- Pod
validate:
message: "镜像存在高危漏洞,不允许部署。请使用更安全的镜像。"
foreach:
element: request.object.spec.containers[]
validate:
# 以下是一种概念性实现,实际可能需要结合Kyverno的Lookup功能或AdmissionReview.userInfo
# 来查询外部服务或CRD。
# 假设你有一个名为 'ImageVulnerabilityReport' 的CRD,存储了镜像的漏洞信息
# 并且我们可以在Kyverno策略中查询到这个信息。
# Kyverno的实际Lookup功能通常用于查询ConfigMap或Secrets,
# 对于外部API或自定义CRD,需要更复杂的配置或者通过MutatingWebhook预处理。
# 更实际的方案是:扫描器将漏洞等级注入到镜像的Annotation中(如Gatekeeper示例),
# 然后Kyverno通过`images`规则检查这些Annotation。
# 这里以一个简化的、类似Gatekeeper通过Annotation检查的方式作为Kyverno的演示
# Kyverno可以直接检查容器的 image 字段
# 如果你的扫描器可以在镜像本身打上包含漏洞等级的标签,或者在Pod定义中添加Annotation
# 那么Kyverno可以直接检查这些信息。
# 以下是Kyverno如何检查镜像名称或标签的示例,结合漏洞信息需要更高级的集成
# 例如,如果你的镜像标签包含漏洞等级
# image.registry ~ `^myregistry.com/.*` && !image.tag | contains('no-critical-vulns')
# 或者
pattern:
image: "!*vuln-critical*" # 假设不安全的镜像会带上“vuln-critical”标签或名称
# 这只是一个非常简化的示例,实际情况需要更精确的漏洞判断逻辑。
# 实际生产中,会通过与Harbor等镜像仓库集成,查询其API获取漏洞报告。
# 或者,通过一个Webhook Service在Kyverno策略执行前,将漏洞信息注入到AdmissionReview请求中。
Kyverno的image验证规则
Kyverno在validate规则中有一个强大的image字段,可以检查镜像的各种属性,包括Digest、Registry、Tag等。虽然它不直接扫描漏洞,但可以用于强制执行“只允许从经过安全扫描的、带有特定标签的镜像仓库中拉取镜像”的策略。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: check-image-source-and-digest
spec:
validationFailureAction: Enforce
rules:
- name: validate-images
match:
resources:
kinds:
- Pod
validate:
message: "所有镜像必须来自信任的仓库,并且使用固定的Digest。"
foreach:
element: request.object.spec.containers[]
validate:
# 检查镜像是否来自允许的仓库,并且必须使用Digest而非Tag
image:
registries:
- "mytrustedregistry.com"
# digest 是必需的,确保镜像的完整性和不可变性
requireDigest: true
# 也可以检查特定标签,例如 'no-high-vuln'
# tags:
# - "*-no-high-vuln" # 假设只有带有这种后缀的标签才允许
5. Gatekeeper vs. Kyverno 对比
| 特性 | Gatekeeper (OPA/Rego) | Kyverno |
|---|---|---|
| 策略语言 | Rego (声明式逻辑编程语言) | YAML (Kubernetes原生格式) |
| 学习曲线 | Rego语法学习成本较高,灵活性强 | YAML基于Kubernetes资源,学习曲线平缓,易于上手 |
| 功能范围 | 通用策略引擎,可用于任何JSON/YAML数据 | 专注于Kubernetes策略,提供更丰富的Kubernetes原生功能 |
| 镜像验证 | 需要自定义Rego逻辑,通常需要与外部服务集成 | 内置强大的image规则,可验证签名、来源、Digest等,更易与外部扫描器集成 |
| 漏洞集成 | 通常通过自定义Webhook查询外部服务或Pod Annotation | 可以通过lookup查询ConfigMap/Secret,或通过mutate注入信息,更直接的CRD/API查询需要额外配置或Webhook预处理 |
| 修改资源 | 支持Mutation功能,但需要编写更复杂的Rego | 内置Mutation功能,通过YAML易于实现修改资源 |
| 生成资源 | 不直接支持 | 支持生成新的Kubernetes资源 |
| 社区活跃度 | 非常活跃,作为CNCF项目被广泛采纳 | 活跃,专注于Kubernetes,获得CNCF项目地位 |
选择建议:
- 如果你已经在使用OPA/Rego,或者你的团队习惯于编写Rego策略,且需要高度灵活、通用的策略引擎,Gatekeeper是更好的选择。
- 如果你的团队更倾向于Kubernetes原生YAML配置,对策略引擎有更直接的Kubernetes相关需求(如镜像签名验证、自动注入Sidecar等),且希望降低学习成本,Kyverno可能更适合你。
- 对于直接的漏洞扫描结果集成,两者都需要一定程度的外部集成。Kyverno在处理镜像本身的元数据方面可能更直接,而Gatekeeper在与自定义外部Webhook服务集成方面更具通用性。
6. 实施最佳实践与注意事项
- 分阶段实施策略: 不要一开始就启用强制执行(
Enforce)。可以先以Audit模式运行,观察哪些请求会被策略拦截,进行调整和优化。 - 细化策略范围: 策略应该针对特定的命名空间、标签或服务账户生效,避免“一刀切”影响整个集群。
- 漏洞扫描与准入控制的结合: 准入控制是防线,但前端的漏洞扫描是数据源。确保你的漏洞扫描流程健全、及时,并且扫描结果能够被准入控制器有效获取。例如,扫描结果可以作为Image Manifest List的Annotation,或者在CI/CD中更新一个ConfigMap/CRD供准入控制器查询。
- 处理豁免: 某些特殊情况可能需要豁免策略。为关键的系统组件或紧急修复场景提供明确的豁免机制(例如,通过特定标签或命名空间)。
- 性能考量: 过于复杂的策略或频繁的外部API调用可能会增加API Server的延迟。监控准入控制器的性能,并优化策略。
- 集成CI/CD: 将漏洞扫描和准入策略的验证集成到CI/CD流水线中,在部署前就能发现并解决问题。
总结
在Kubernetes集群中实施容器镜像的准入控制,是提升集群安全性的关键一步。无论是Gatekeeper还是Kyverno,都提供了强大的能力来强制执行安全策略。关键在于选择适合你团队技术栈和需求的工具,并将其与镜像漏洞扫描流程紧密结合,构建一个从开发到部署的全生命周期安全防护体系。通过这些预防性措施,我们可以显著降低因部署含有高危漏洞镜像而带来的风险,确保生产环境的稳定与安全。