这篇文章已经一年多了,较旧的文章可能包含过时的内容。请检查从发表以来,页面中的信息是否变得不正确。

Kubernetes 验证准入策略:一个真实示例

作者:Craig Box (ARMO), Ben Hirschberg (ARMO)

译者:Xiaoyang Zhang (Huawei)

准入控制是 Kubernetes 控制平面的重要组成部分,在向服务器提交请求时,可根据批准或更改 API 对象的能力来实现多项内部功能。 对于管理员来说,定义有关哪些对象可以进入集群的业务逻辑或策略是很有用的。为了更好地支持该场景, Kubernetes 在 v1.7 中引入了外部准入控制

除了众多的自定义内部实现外,许多开源项目和商业解决方案还使用用户指定的策略实现准入控制器,包括 Kyverno 和 Open Policy Agent 的 Gatekeep

虽然针对策略的准入控制器已经被人采用,但其广泛使用仍存在障碍。 Webhook 基础设施必须作为生产服务进行维护,且包含所有相关内容。 如果准入控制 Webhook 失败,则要么必须关闭,从而降低集群的可用性;要么打开,使该功能在策略执行中的使用失效。 例如,在 “serverless” 环境中,当 Pod 启动以响应网络请求时,网络跳跃和评估时间使准入控制成为处理延迟的重要组成部分。

验证准入策略和通用表达语言

Kubernetes 1.26 版本引入了一个折中的、Alpha 状态的解决方案。 验证准入策略是一种声明式的、 进程内的方案,用来替代验证准入 Webhook。使用通用表达语言 (Common Expression Language,CEL) 来声明策略的验证规则。

CEL 是由 Google 根据 Firebase 实时数据库的经验,针对安全和策略用例而开发的。 它的设计使得它可以安全地嵌入到应用程序中,执行时间在微秒量级,对计算和内存的影响很小。 v1.23 版本中,针对 CRD 的验证规则将 CEL 引入了 Kubernetes 生态系统,当时人们注意到该语言将适合实现通过准入控制进行更通用的验证。

让 CEL 发挥作用——一个实际例子

Kubescape 是一个 CNCF 项目, 已成为用户改善 Kubernetes 集群安全状况并验证其合规性的最流行方法之一。 它的控件——针对 API 对象的多组测试——是用 Open Policy Agent 的策略语言 Rego 构建的。

Rego 以复杂性著称,这主要是因为它是一种声明式查询语言(如 SQL)。 它被考虑在 Kubernetes 中使用,但它没有提供与 CEL 相同的沙箱约束。

该项目的一个常见功能要求是能够根据 Kubescape 的发现和输出来实现策略。例如,在扫描 Pod 是否存在云凭据文件的已知路径后, 用户希望能够执行完全不允许这些 Pod 进入的策略。 Kubescape 团队认为这是一个绝佳的机会,可以尝试将现有的控制措施移植到 CEL,并将其应用为准入策略。

策略展示

我们很快就转换了许多控件并建立了一个验证准入策略的库。让我们看一个例子。

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 组提供匹配约束:Pod 的 core/v1 组、负载控制器的 apps/v1 和作业控制器的 batch/v1

其中 validations 包括对象的 CEL 规则。有三种不同的表达方式,以满足 Pod spec 可以位于对象的根部(独立的 Pod)、 在 template 下(负载控制器或作业)或位于 jobTemplate(CronJob)下的情况。

如果任何一个 spec 没有将 readOnlyRootFilesystem 设为 true,则该对象将不会被接受。

在集群中使用 CEL 库

策略以 Kubernetes 对象的形式提供,并通过选择算符绑定到某些资源。

Minikube 是一种安装和配置 Kubernetes 集群以进行测试的快速简便的方法。 安装 Kubernetes v1.26 并启用特性门控 ValidatingAdmissionPolicy

minikube start --kubernetes-version=1.26.1 --extra-config=apiserver.runtime-config=admissionregistration.k8s.io/v1alpha1  --feature-gates='ValidatingAdmissionPolicy=true'

要在集群中安装策略:

# 安装配置 CRD
kubectl apply -f https://github.com/kubescape/cel-admission-library/releases/latest/download/policy-configuration-definition.yaml
# 安装基础配置
kubectl apply -f https://github.com/kubescape/cel-admission-library/releases/latest/download/basic-control-configuration.yaml
# 安装策略
kubectl apply -f https://github.com/kubescape/cel-admission-library/releases/latest/download/kubescape-validating-admission-policies.yaml

要将策略应用到对象,请创建一个 ValidatingAdmissionPolicyBinding 资源。 让我们把上述 Kubescape C-0017 控件应用到所有带有标签 policy=enforced 的命名空间:

# 创建绑定
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

# 创建用于运行示例的命名空间
kubectl create namespace policy-example
kubectl label namespace policy-example 'policy=enforced'

现在,如果尝试创建一个对象但不指定 readOnlyRootFilesystem,该对象将不会被创建。

# 如下命令会失败
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, 当然还有此博客,以获取最终的发布公告。