本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
Kubernetes 验证性准入策略:一个实践示例
准入控制是 Kubernetes 控制平面的重要组成部分,一些内部功能依赖于在 API 对象提交到服务器时批准或更改该对象的能力。对于管理员来说,能够定义关于哪些对象可以被准入到集群中的业务逻辑或策略也很有用。为了更好地支持这一用例,Kubernetes 在 v1.7 中引入了外部准入控制。
除了无数的自定义内部实现外,许多开源项目和商业解决方案也使用用户指定的策略来实现准入控制器,包括 Kyverno 和 Open Policy Agent 的 Gatekeeper。
虽然用于策略的准入控制器已得到采用,但其广泛使用仍存在一些障碍。Webhook 基础设施必须作为生产服务来维护,并承担所有相关工作。准入控制 Webhook 的故障案例要么是关闭的,从而降低集群的可用性;要么是开放的,从而使该功能在策略执行方面的作用失效。在处理例如为响应“无服务器”环境中的网络请求而启动的 Pod 时,网络跳转和评估时间使得准入控制成为延迟的一个显著组成部分。
验证性准入策略和通用表达式语言
Kubernetes 的 1.26 版本引入了一个折衷的解决方案(处于 Alpha 阶段)。验证性准入策略是准入 Webhook 的一种声明式、进程内替代方案。它们使用通用表达式语言(CEL)来声明验证规则。
CEL 是由 Google 针对安全和策略用例开发的,其基础源于 Firebase 实时数据库的经验。其设计使其能够安全地嵌入到应用程序中,并在微秒级执行,计算和内存影响有限。CRD 的验证规则在 v1.23 中将 CEL 引入了 Kubernetes 生态系统,当时就有人指出,该语言将适用于更通用的准入控制验证实现。
试用 CEL - 一个实践示例
Kubescape 是一个 CNCF 项目,已成为用户提高 Kubernetes 集群安全状况和验证其合规性的最流行方式之一。它的控制项 — 针对 API 对象的测试组 — 是用 Rego 构建的,Rego 是 Open Policy Agent 的策略语言。
Rego 以其复杂性而闻名,这主要因为它是一种声明式查询语言(类似于 SQL)。它曾被考虑用于 Kubernetes,但它不提供与 CEL 相同的沙箱约束。
该项目的一个常见功能请求是能够根据 Kubescape 的发现和输出来实施策略。例如,在扫描 Pod 中是否存在已知的云凭证文件路径后,用户希望能够强制执行策略,完全禁止这些 Pod 被准入。Kubescape 团队认为这是一个绝佳的机会,可以尝试将我们现有的控制项移植到 CEL,并将其作为准入策略来应用。
展示策略
我们没花多长时间就转换了许多控制项,并构建了一个验证性准入策略库。让我们来看一个例子。
Kubescape 的控制项 C-0017 涵盖了容器需要具有不可变(只读)根文件系统的要求。根据 NSA Kubernetes 强化指南,这是一种最佳实践,但目前并不作为任何Pod 安全标准的一部分要求。
以下是我们如何在 CEL 中实现它
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "kubescape-c-0017-deny-resources-with-mutable-container-filesystem"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments","replicasets","daemonsets","statefulsets"]
- apiGroups: ["batch"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["jobs","cronjobs"]
validations:
- expression: "object.kind != 'Pod' || object.spec.containers.all(container, has(container.securityContext) && has(container.securityContext.readOnlyRootFilesystem) && container.securityContext.readOnlyRootFilesystem == true)"
message: "Pods having containers with mutable filesystem not allowed! (see more at https://hub.armosec.io/docs/c-0017)"
- expression: "['Deployment','ReplicaSet','DaemonSet','StatefulSet','Job'].all(kind, object.kind != kind) || object.spec.template.spec.containers.all(container, has(container.securityContext) && has(container.securityContext.readOnlyRootFilesystem) && container.securityContext.readOnlyRootFilesystem == true)"
message: "Workloads having containers with mutable filesystem not allowed! (see more at https://hub.armosec.io/docs/c-0017)"
- expression: "object.kind != 'CronJob' || object.spec.jobTemplate.spec.template.spec.containers.all(container, has(container.securityContext) && has(container.securityContext.readOnlyRootFilesystem) && container.securityContext.readOnlyRootFilesystem == true)"
message: "CronJob having containers with mutable filesystem not allowed! (see more at https://hub.armosec.io/docs/c-0017)"
为三种可能的 API 组提供了匹配约束:用于 Pod 的 core/v1
组,用于工作负载控制器的 apps/v1
组,以及用于作业控制器的 batch/v1
组。
说明
matchConstraints
会将 API 对象转换为你匹配的版本。例如,如果一个 API 请求是针对 apps/v1beta1
,而你在 matchConstraints 中匹配了 apps/v1
,那么该 API 请求将被从 apps/v1beta1
转换为 apps/v1
,然后进行验证。这有一个有用的特性,即可以使验证规则免受新版本 API 引入的影响,否则 API 请求可能会通过使用新引入的版本绕过验证规则。validations
包含了对象的 CEL 规则。这里有三种不同的表达式,以适应 Pod spec
可能位于对象根部(裸 Pod)、template
下(工作负载控制器或 Job)或 jobTemplate
下(CronJob)的情况。
如果任何 spec
的 readOnlyRootFilesystem
没有设置为 true,该对象将不会被准入。
说明
在我们的初始版本中,我们将这三个表达式组合到了同一个策略对象中。这意味着它们可以原子地启用和禁用,因此用户不会因为为一个 API 组启用策略而忘记为其他组启用,从而意外地留下合规性漏洞。将它们分成单独的策略将使我们能够利用针对 1.27 版本的目标改进,包括类型检查。我们正在与 SIG API Machinery 讨论如何在这些 API 达到v1
之前最好地解决这个问题。在您的集群中使用 CEL 库
策略以 Kubernetes 对象的形式提供,然后通过选择器绑定到某些资源。
Minikube 是安装和配置 Kubernetes 集群以进行测试的一种快速简便的方法。要安装启用了 ValidatingAdmissionPolicy
功能门控的 Kubernetes v1.26:
minikube start --kubernetes-version=1.26.1 --extra-config=apiserver.runtime-config=admissionregistration.k8s.io/v1alpha1 --feature-gates='ValidatingAdmissionPolicy=true'
要在您的集群中安装这些策略:
# Install configuration CRD
kubectl apply -f https://github.com/kubescape/cel-admission-library/releases/latest/download/policy-configuration-definition.yaml
# Install basic configuration
kubectl apply -f https://github.com/kubescape/cel-admission-library/releases/latest/download/basic-control-configuration.yaml
# Install policies
kubectl apply -f https://github.com/kubescape/cel-admission-library/releases/latest/download/kubescape-validating-admission-policies.yaml
要将策略应用于对象,请创建一个 ValidatingAdmissionPolicyBinding
资源。让我们将上述 Kubescape C-0017 控制项应用于任何带有标签 policy=enforced
的命名空间:
# Create a binding
kubectl apply -f - <<EOT
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: c0017-binding
spec:
policyName: kubescape-c-0017-deny-mutable-container-filesystem
matchResources:
namespaceSelector:
matchLabels:
policy: enforced
EOT
# Create a namespace for running the example
kubectl create namespace policy-example
kubectl label namespace policy-example 'policy=enforced'
现在,如果您尝试创建一个没有指定 readOnlyRootFilesystem
的对象,它将不会被创建。
# The next line should fail
kubectl -n policy-example run nginx --image=nginx --restart=Never
输出显示了我们的错误:
The pods "nginx" is invalid: : ValidatingAdmissionPolicy 'kubescape-c-0017-deny-mutable-container-filesystem' with binding 'c0017-binding' denied request: Pods having containers with mutable filesystem not allowed! (see more at https://hub.armosec.io/docs/c-0017)
配置
策略对象可以包含配置,这些配置在另一个对象中提供。许多 Kubescape 控制项都需要配置:需要哪些标签、允许或拒绝哪些能力、允许从哪些注册中心部署容器等。这些控制项的默认值在 ControlConfiguration 对象中定义。
要使用此配置对象,或您自己格式相同的对象,请将一个 paramRef.name
值添加到您的绑定对象中:
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: c0001-binding
spec:
policyName: kubescape-c-0001-deny-forbidden-container-registries
paramRef:
name: basic-control-configuration
matchResources:
namespaceSelector:
matchLabels:
policy: enforced
总结
在大多数情况下,将我们的控制项转换为 CEL 是很简单的。我们无法移植整个 Kubescape 库,因为一些控制项检查的是 Kubernetes 集群之外的东西,还有一些需要准入请求对象中不可用的数据。总的来说,我们很高兴能将这个库贡献给 Kubernetes 社区,并将继续为 Kubescape 和 Kubernetes 用户开发它。我们希望它能变得有用,无论是您自己使用,还是作为您编写自己策略的示例。
至于验证性准入策略功能本身,我们非常高兴看到这个原生功能被引入到 Kubernetes 中。我们期待着它进入 Beta 阶段,然后是 GA,希望能在年底前实现。需要注意的是,该功能目前处于 Alpha 阶段,这意味着现在是在 Minikube 等环境中试用并进行测试的绝佳机会。然而,它尚未被认为是生产就绪且稳定的,并且在大多数托管的 Kubernetes 环境中不会启用。在底层功能变得稳定之前,我们不会建议 Kubescape 用户在生产环境中使用这些策略。请关注KEP,当然还有本博客,以获取最终的发布公告。