本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

Kubernetes 1.30:多 Webhook 和模块化授权变得更加容易

在 Kubernetes 1.30 中,我们(SIG Auth)将结构化授权配置(Structured Authorization Configuration)升级到 Beta 阶段。

今天的文章是关于**授权**(Authorization):决定某人可以访问什么和不可以访问什么。可以查看昨天的一篇文章,了解 Kubernetes v1.30 在**身份验证**(Authentication,即确定谁在执行任务并检查他们是否是其声称的身份)方面的新功能。

引言

Kubernetes 持续演进以满足系统管理员和开发人员的复杂需求。确保集群安全性和完整性的一个关键方面是 API 服务器授权。直到最近,kube-apiserver 中的授权链配置还相当僵化,仅限于一组命令行标志,并且在授权链中只允许一个 Webhook。这种方法虽然功能可用,但限制了集群管理员定义复杂、细粒度授权策略所需的灵活性。最新的结构化授权配置功能(KEP-3221)旨在通过引入一种更结构化、更通用的方式来配置授权链,从而彻底改变这一方面,重点是启用多个 Webhook 并提供显式的控制机制。

改进的必要性

集群管理员长期以来一直希望能够在 API 服务器处理链中指定多个授权 Webhook,并能控制每个 Webhook 的详细行为,如超时和失败策略。这种需求源于创建分层安全策略的愿望,其中请求可以按特定顺序根据多个标准或规则集进行验证。以前的限制也使得动态配置授权链变得困难,无法有效地管理复杂的授权场景。

结构化授权配置功能通过引入一种配置文件格式来配置 Kubernetes API 服务器授权链,从而解决了这些限制。这种格式允许在授权链中指定多个 Webhook(所有其他授权类型最多指定一次)。每个 Webhook 授权器都有明确定义的参数,包括超时设置、失败策略以及使用 CEL 规则的调用条件,以便在请求被分派到 Webhook 之前进行预过滤,帮助你防止不必要的调用。该配置还支持自动重新加载,确保更改可以动态应用,而无需重新启动 kube-apiserver。此功能不仅解决了当前的限制,还为更有效地保护和管理 Kubernetes 集群开辟了新的可能性。

配置示例

以下是一个结构化授权配置示例,并附有所有字段、其默认值和可能值的说明。

apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthorizationConfiguration
authorizers:
  - type: Webhook
    # Name used to describe the authorizer
    # This is explicitly used in monitoring machinery for metrics
    # Note:
    #   - Validation for this field is similar to how K8s labels are validated today.
    # Required, with no default
    name: webhook
    webhook:
      # The duration to cache 'authorized' responses from the webhook
      # authorizer.
      # Same as setting `--authorization-webhook-cache-authorized-ttl` flag
      # Default: 5m0s
      authorizedTTL: 30s
      # The duration to cache 'unauthorized' responses from the webhook
      # authorizer.
      # Same as setting `--authorization-webhook-cache-unauthorized-ttl` flag
      # Default: 30s
      unauthorizedTTL: 30s
      # Timeout for the webhook request
      # Maximum allowed is 30s.
      # Required, with no default.
      timeout: 3s
      # The API version of the authorization.k8s.io SubjectAccessReview to
      # send to and expect from the webhook.
      # Same as setting `--authorization-webhook-version` flag
      # Required, with no default
      # Valid values: v1beta1, v1
      subjectAccessReviewVersion: v1
      # MatchConditionSubjectAccessReviewVersion specifies the SubjectAccessReview
      # version the CEL expressions are evaluated against
      # Valid values: v1
      # Required, no default value
      matchConditionSubjectAccessReviewVersion: v1
      # Controls the authorization decision when a webhook request fails to
      # complete or returns a malformed response or errors evaluating
      # matchConditions.
      # Valid values:
      #   - NoOpinion: continue to subsequent authorizers to see if one of
      #     them allows the request
      #   - Deny: reject the request without consulting subsequent authorizers
      # Required, with no default.
      failurePolicy: Deny
      connectionInfo:
        # Controls how the webhook should communicate with the server.
        # Valid values:
        # - KubeConfigFile: use the file specified in kubeConfigFile to locate the
        #   server.
        # - InClusterConfig: use the in-cluster configuration to call the
        #   SubjectAccessReview API hosted by kube-apiserver. This mode is not
        #   allowed for kube-apiserver.
        type: KubeConfigFile
        # Path to KubeConfigFile for connection info
        # Required, if connectionInfo.Type is KubeConfigFile
        kubeConfigFile: /kube-system-authz-webhook.yaml
        # matchConditions is a list of conditions that must be met for a request to be sent to this
        # webhook. An empty list of matchConditions matches all requests.
        # There are a maximum of 64 match conditions allowed.
        #
        # The exact matching logic is (in order):
        #   1. If at least one matchCondition evaluates to FALSE, then the webhook is skipped.
        #   2. If ALL matchConditions evaluate to TRUE, then the webhook is called.
        #   3. If at least one matchCondition evaluates to an error (but none are FALSE):
        #      - If failurePolicy=Deny, then the webhook rejects the request
        #      - If failurePolicy=NoOpinion, then the error is ignored and the webhook is skipped
      matchConditions:
      # expression represents the expression which will be evaluated by CEL. Must evaluate to bool.
      # CEL expressions have access to the contents of the SubjectAccessReview in v1 version.
      # If version specified by subjectAccessReviewVersion in the request variable is v1beta1,
      # the contents would be converted to the v1 version before evaluating the CEL expression.
      #
      # Documentation on CEL: https://kubernetes.ac.cn/docs/reference/using-api/cel/
      #
      # only send resource requests to the webhook
      - expression: has(request.resourceAttributes)
      # only intercept requests to kube-system
      - expression: request.resourceAttributes.namespace == 'kube-system'
      # don't intercept requests from kube-system service accounts
      - expression: "!('system:serviceaccounts:kube-system' in request.groups)"
  - type: Node
    name: node
  - type: RBAC
    name: rbac
  - type: Webhook
    name: in-cluster-authorizer
    webhook:
      authorizedTTL: 5m
      unauthorizedTTL: 30s
      timeout: 3s
      subjectAccessReviewVersion: v1
      failurePolicy: NoOpinion
      connectionInfo:
        type: InClusterConfig

以下配置示例说明了需要能够指定具有不同设置、优先顺序和失败模式的多个 Webhook 的真实场景。

保护已安装的 CRD

确保自定义资源定义(CRD)在集群启动时的可用性一直是一个关键需求。让控制器协调这些 CRD 的一个障碍是为它们提供一种保护机制,这可以通过多个授权 Webhook 来实现。这在以前是不可能的,因为在 Kubernetes API 服务器授权链中指定多个授权 Webhook 是不允许的。现在,借助结构化授权配置功能,管理员可以指定多个 Webhook,提供了一种在 RBAC 不足时(特别是在拒绝“非系统”用户对某些 CRD 的权限时)的解决方案。

在此场景中假设以下情况

  • 已安装“受保护的”CRD。
  • 它们只能由 `admin` 组中的用户修改。
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthorizationConfiguration
authorizers:
  - type: Webhook
    name: system-crd-protector
    webhook:
      unauthorizedTTL: 30s
      timeout: 3s
      subjectAccessReviewVersion: v1
      matchConditionSubjectAccessReviewVersion: v1
      failurePolicy: Deny
      connectionInfo:
        type: KubeConfigFile
        kubeConfigFile: /files/kube-system-authz-webhook.yaml
      matchConditions:
      # only send resource requests to the webhook
      - expression: has(request.resourceAttributes)
      # only intercept requests for CRDs
      - expression: request.resourceAttributes.resource.resource = "customresourcedefinitions"
      - expression: request.resourceAttributes.resource.group = ""
      # only intercept update, patch, delete, or deletecollection requests
      - expression: request.resourceAttributes.verb in ['update', 'patch', 'delete','deletecollection']
  - type: Node
  - type: RBAC

防止不必要的嵌套 Webhook

系统管理员希望在使用 Open Policy Agent 等框架将请求交给 Webhook 之前,对请求应用特定的验证。过去,这需要在添加到授权链的 Webhook 内部运行嵌套的 Webhook 才能实现所需的效果。结构化授权配置功能简化了此过程,提供了一个结构化的 API,可在需要时选择性地触发额外的 Webhook。它还使管理员能够为每个 Webhook 设置不同的失败策略,确保响应更加一致和可预测。

apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthorizationConfiguration
authorizers:
  - type: Webhook
    name: system-crd-protector
    webhook:
      unauthorizedTTL: 30s
      timeout: 3s
      subjectAccessReviewVersion: v1
      matchConditionSubjectAccessReviewVersion: v1
      failurePolicy: Deny
      connectionInfo:
        type: KubeConfigFile
        kubeConfigFile: /files/kube-system-authz-webhook.yaml
      matchConditions:
      # only send resource requests to the webhook
      - expression: has(request.resourceAttributes)
      # only intercept requests for CRDs
      - expression: request.resourceAttributes.resource.resource = "customresourcedefinitions"
      - expression: request.resourceAttributes.resource.group = ""
      # only intercept update, patch, delete, or deletecollection requests
      - expression: request.resourceAttributes.verb in ['update', 'patch', 'delete','deletecollection']
  - type: Node
  - type: RBAC
  - name: opa
    type: Webhook
    webhook:
      unauthorizedTTL: 30s
      timeout: 3s
      subjectAccessReviewVersion: v1
      matchConditionSubjectAccessReviewVersion: v1
      failurePolicy: Deny
      connectionInfo:
        type: KubeConfigFile
        kubeConfigFile: /files/opa-default-authz-webhook.yaml
      matchConditions:
      # only send resource requests to the webhook
      - expression: has(request.resourceAttributes)
      # only intercept requests to default namespace
      - expression: request.resourceAttributes.namespace == 'default'
      # don't intercept requests from default service accounts
      - expression: "!('system:serviceaccounts:default' in request.groups)"

接下来是什么?

从 Kubernetes 1.30 开始,该功能处于 Beta 阶段并默认启用。对于 Kubernetes v1.31,我们希望该功能在获得更多用户反馈的同时保持在 Beta 阶段。一旦准备好进入 GA(正式发布)阶段,该功能标志将被移除,配置文件版本将提升到 v1。

请在 Kubernetes 文档网站的结构化授权配置页面上了解有关此功能的更多信息。你还可以通过 KEP-3221 跟踪未来 Kubernetes 版本中的进展。

行动号召

在这篇文章中,我们介绍了 Kubernetes v1.30 中结构化授权配置功能的好处,以及一些针对真实场景的配置示例。要使用此功能,你必须使用 `--authorization-config` 命令行参数指定授权配置文件的路径。从 Kubernetes 1.30 开始,该功能处于 Beta 阶段并默认启用。如果你想继续使用命令行标志而不是配置文件,这些标志将继续按原样工作。同时指定 `--authorization-config` 和 `--authorization-modes`/`--authorization-webhook-*` 是行不通的。你需要从你的 kube-apiserver 命令中删除旧的标志。

以下 kind 集群配置在 API 服务器上设置该命令参数,以从文件(`authorization_config.yaml`)中加载 AuthorizationConfiguration,该文件位于 `files` 文件夹中。任何需要的 kubeconfig 和证书文件也可以放在 `files` 目录中。

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
  StructuredAuthorizationConfiguration: true  # enabled by default in v1.30
kubeadmConfigPatches:
  - |
    kind: ClusterConfiguration
    metadata:
      name: config
    apiServer:
      extraArgs:
        authorization-config: "/files/authorization_config.yaml"
      extraVolumes:
      - name: files
        hostPath: "/files"
        mountPath: "/files"
        readOnly: true    
nodes:
- role: control-plane
  extraMounts:
  - hostPath: files
    containerPath: /files

我们很乐意听到你对这个功能的反馈。特别是,我们希望从 Kubernetes 集群管理员和授权 Webhook 实现者那里获得反馈,因为他们正在构建与这个新 API 的集成。请在 Kubernetes Slack 上的 #sig-auth-authorizers-dev 频道上与我们联系。

如何参与

如果你有兴趣帮助开发此功能、分享反馈或参与任何其他正在进行的 SIG Auth 项目,请在 Kubernetes Slack 上的 #sig-auth 频道上与我们联系。

也欢迎你参加每两周一次的 SIG Auth 会议,会议在每隔一个星期三举行。

致谢

此功能由来自多家公司的贡献者共同推动。我们衷心感谢所有为实现这一目标而贡献时间和精力的每一个人。