本文发表已超过一年。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已不再正确。
Kubernetes 验证准入策略:一个实际示例
Admission control 是 Kubernetes 控制平面 的重要组成部分,多个内部功能依赖于在 API 对象提交到服务器时批准或更改其内容的能力。对于管理员来说,能够定义关于哪些对象可以被允许进入集群的业务逻辑或策略也很有用。为了更好地支持这一用例,Kubernetes 在 v1.7 中引入了外部 admission control。
除了无数的定制内部实现之外,许多开源项目和商业解决方案实现了带有用户指定策略的 admission controllers,包括 Kyverno 和 Open Policy Agent 的 Gatekeeper。
虽然用于策略的 admission controllers 已得到采用,但它们的广泛使用存在一些障碍。Webhook 基础设施必须作为生产服务进行维护,并承担随之而来的一切。admission control webhook 的失败场景必须要么是关闭的,从而降低集群的可用性;要么是开放的,从而抵消该功能在策略强制执行方面的作用。网络跳转和评估时间使得 admission control 在处理例如“无服务器”环境中为响应网络请求而启动的 Pod 时,成为延迟的一个显著组成部分。
Validating admission policies 和 Common Expression Language
Kubernetes 1.26 版本在 alpha 阶段引入了一种折衷解决方案。Validating admission policies 是一种声明式的、进程内的 admission webhooks 替代方案。它们使用 Common Expression Language (CEL) 来声明验证规则。
CEL 是 Google 基于 Firebase 实时数据库的经验教训开发的,用于安全和策略用例。其设计使其能够安全地嵌入到应用中并以微秒级执行,对计算和内存影响有限。CRD 的验证规则 在 v1.23 中将 CEL 引入了 Kubernetes 生态系统,当时就指出该语言适合 admission control 的更通用验证实现。
试用 CEL - 一个实际示例
Kubescape 是一个 CNCF 项目,已成为用户提高 Kubernetes 集群安全姿态和验证其合规性的最流行方式之一。它的 controls(针对 API 对象的测试组)是使用 Rego 构建的,Rego 是 Open Policy Agent 的策略语言。
Rego 以复杂著称,这主要归因于它是一种声明式查询语言(类似于 SQL)。它 曾被考虑 用于 Kubernetes,但它不提供与 CEL 相同的沙箱约束。
该项目的一个常见特性请求是能够根据 Kubescape 的发现和输出来实现策略。例如,在扫描 Pod 是否包含 已知的云凭据文件路径 后,用户希望能够强制执行这些 Pod 不应被允许的策略。Kubescape 团队认为这将是尝试将我们现有的 controls 移植到 CEL 并将其作为 admission policies 应用的绝佳机会。
策略展示
我们很快就转换了许多 controls,并构建了一个 validating admission policies 库。让我们看一个示例。
Kubescape 的 control 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 组提供了匹配约束:用于 Pods 的 core/v1
组,apps/v1
工作负载控制器,以及 batch/v1
作业控制器。
注意
matchConstraints
将为你把 API 对象转换为匹配的版本。例如,如果 API 请求是针对 apps/v1beta1
的,并且你在 matchConstraints 中匹配了 apps/v1
,则 API 请求将从 apps/v1beta1
转换为 apps/v1
然后进行验证。这具有一个有用的特性,即通过防止使用新引入的版本绕过验证规则,使得验证规则能够抵御新版本 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
feature gate 的 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 control 应用于带有标签 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 controls 需要配置:例如需要哪些标签,允许或拒绝哪些能力,允许容器从哪些注册表部署等等。这些 controls 的默认值在 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
总结
在大多数情况下,将我们的 controls 转换为 CEL 是简单的。我们无法移植整个 Kubescape 库,因为有些 controls 检查 Kubernetes 集群外部的事物,有些则需要 admission request 对象中不可用的数据。总的来说,我们很高兴能为 Kubernetes 社区贡献这个库,并将继续为 Kubescape 和 Kubernetes 用户开发它。我们希望它能变得有用,无论是供您自己使用,还是作为您编写自己策略的示例。
至于 validating admission policy 功能本身,我们非常高兴看到这个原生功能引入 Kubernetes。我们期待着看到它进入 Beta 阶段,然后是 GA,希望能在年底前实现。重要的是要注意,此功能目前处于 Alpha 阶段,这意味着这是在 Minikube 等环境中进行试用和体验的绝佳机会。但是,它尚未被视为生产就绪和稳定,并且在大多数托管 Kubernetes 环境中不会默认启用。在底层功能变得稳定之前,我们不会推荐 Kubescape 用户在生产环境中使用这些策略。请关注 KEP,当然也包括此博客,以获取最终的发布公告。