草庐IT

K8s 原生支持的准入策略管理

阳明 2023-03-28 原文

在 Kubernetes 1.26 发布的 changelog 中,发现了一个 alpha 版本的验证准入策略的更新,其实就是可以用一种特定的语言来进行准入控制,以前我们介绍过可以通过 OPA、kyverno​ 等方式来进行策略管理,但是这些方式并非官方默认的方式,而现在官方提供了一种自带的方式,在验证准入策略时可以使用一种通用的表达式语言(CEL)来提供声明的、进程内的替代方法来验证 validating admission webhook。

CEL 最开始被引入到 Kubernetes 中来是用于 CustomResourceDefinitions 的验证规则,此次改进则大大扩展了 CEL 在 Kubernetes 中的使用,可以支持更广泛的准入用例场景。

什么是 CEL

CEL 是一种非图灵的完整表达式语言,被设计为快速、可移植和安全执行,CEL 可以单独使用,也可以嵌入到一个更大的产品中。

CEL 被设计成一种可以安全执行用户代码的语言,虽然在用户的 python 代码上盲目地调用 eval() 是危险的,但你可以安全地执行用户的 CEL 代码。因为 CEL 防止了会使其性能降低的行为,它可以在纳秒到微秒的时间内安全地进行评估;它是性能关键型应用的理想选择。CEL 评估表达式,这类似于单行函数或 lambda 表达式。虽然 CEL 通常用于布尔决策,但它也可用于构建更复杂的对象,如 JSON 或 protobuf 消息。

CEL 的类 C 语法看起来与 C++、Go、Java 和 TypeScript 中的等价表达式几乎是相同的。

resource.name.startWith("/groups/" + auth.claims.group) // 检测resource.name是否以group开头
关于 CEL 语言的完整语法可以参考官网 https://github.com/google/cel-spec。

准入策略

我们知道准入控制器的开发和操作是非常繁琐的,除了要开发 Webhook 程序之外,还需要维护 Webhook 二进制文件来处理准入请求,admission webhook 的操作也都很复杂,每个 webhook 都必须部署、监控,并要有一个明确的升级和回滚计划,如果你的 webhook 超时或不可用了,那么 Kubernetes 控制平面可能会变得不可用,影响面非常大。现在我们可以通过将 CEL 表达式嵌入到 Kubernetes 资源中,而不是调用远程 webhook 程序来实现准入策略,这样就大大降低了 admission webhook 的复杂性。

策略管理通常由三个资源组成:

  • ValidatingAdmissionPolicy 描述了策略的抽象逻辑。
  • ValidatingAdmissionPolicyBinding 将上述资源链接在一起并提供范围界定。
  • 参数资源向ValidatingAdmissionPolicy 提供信息以使其成为具体声明。ConfigMap 或 CRD 等类型定义了参数资源的 schema,ValidatingAdmissionPolicy 对象指定他们期望参数资源的种类。
至少必须定义一个 ValidatingAdmissionPolicy 和一个相应的 ValidatingAdmissionPolicyBinding 才能使策略生效。如果不需要通过参数配置 ValidatingAdmissionPolicy,只不设置 ValidatingAdmissionPolicy 中的 spec.paramKind 即可。

一个很简单例子,例如我们要设置 Deployment 可以拥有的副本数量限制,那么可以定义如下所示的验证策略资源对象:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "demo-policy.example.com" # 策略对象
spec:
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.spec.replicas <= 5"
该对象中的 expression 字段的值就是用于验证准入请求的 CEL 表达式,我们这里配置的 object.spec.replicas <= 5,就表示要验证对象的 spec.replicas 属性值是否大于 5,而 matchConstraints 属性则声明了该 ValidatingAdmissionPolicy 对象可以验证哪些类型的请求,我们这里是针对 Deployment 资源对象。

接下来我们可以将该策略绑定到合适的资源:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "demo-binding-test.example.com"
spec:
policy: "demo-policy.example.com"
matchResources:
namespaceSelector:
- key: environment,
operator: In,
values: ["test"]
此 ValidatingAdmissionPolicyBinding 资源会将上面声明的 demo-policy.example.com 策略绑定到 environment 标签设置为 test 的命名空间,一旦创建该绑定对象后,kube-apiserver 将开始执行这个准入策略。

我们可以简单对比下,如果是通过开发 admission webhook 来实现上面的功能,那么我们就需要开发和维护一个程序来执行 <= 的检查,虽然是一个非常简单的功能,但是要做非常多的其他工作,而且在实际工作中,绝大多数也是执行一些相对简单的检查,这些我们都可以很容易使用 CEL 来进行表达。

此外验证准入策略是高度可配置的,我们可以根据需要定义策略,可以根据集群管理员的需求对资源进行参数化,例如我们可以修改上面的准入策略以使其具有可配置性:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "demo-policy.example.com"
spec:
paramKind:
apiVersion: rules.example.com/v1 # 需要一个 CRD
kind: ReplicaLimit
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.spec.replicas <= params.maxReplicas"
在该准入策略对象中,paramKind 属性定义了用于配置策略的资源,在 expression 属性中我们使用了 params 变量来访问参数资源。

然后我们可以定义多个绑定,每个绑定都可以有不同的配置。

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "demo-binding-production.example.com"
spec:
policy: "demo-policy.example.com"
paramsRef:
name: "demo-params-production.example.com"
matchResources:
namespaceSelector:
- key: environment,
operator: In,
values: ["production"]
---
apiVersion: rules.example.com/v1
kind: ReplicaLimit
metadata:
name: "demo-params-production.example.com"
maxReplicas: 1000
这里我们通过 paramsRef 属性关联了一个 CRD 对象,这样在策略对象中我们就可以通过 params.maxReplicas 获取到该对象的 maxReplicas 属性值了,这里我们将 environment 标签设置为 production 的命名空间的 Deployments 限制为最多 1000 个副本,当然我们还可以创建另外的绑定对象,为其他命名空间进行不同的限制。

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "replicalimit-binding-test.example.com"
spec:
policyName: "replicalimit-policy.example.com"
paramRef:
name: "replica-limit-test.example.com"
matchResources:
namespaceSelector:
matchLabels:
environment: test
---
apiVersion: rules.example.com/v1
kind: ReplicaLimit
metadata:
name: "replica-limit-test.example.com"
maxReplicas: 3
此策略参数资源将部署限制为测试环境中所有名称空间中最多 3 个副本,准入政策可能有多个绑定。要将所有其他环境绑定到 maxReplicas 限制为 100,则可以创建另一个 ValidatingAdmissionPolicyBinding 对象:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "replicalimit-binding-nontest"
spec:
policyName: "replicalimit-policy.example.com"
paramRef:
name: "replica-limit-clusterwide.example.com"
matchResources:
namespaceSelector:
matchExpressions:
- key: environment,
operator: NotIn,
values: ["test"]
---
apiVersion: rules.example.com/v1
kind: ReplicaLimit
metadata:
name: "replica-limit-clusterwide.example.com"
maxReplicas: 100
此外在策略对象中我们还可以通过 failurePolicy 来定义如何处理错误配置和 CEL 表达式从准入策略评估为错误,该属性允许的值包括 Ignore、Fail。

  • Ignore 表示忽略调用 ValidatingAdmissionPolicy 的错误,允许 API 请求继续。
  • Fail 表示调用 ValidatingAdmissionPolicy 出错导致准入失败,API 请求被拒绝。
需要注意的是 failurePolicy 是在 ValidatingAdmissionPolicy 对象中定义的,如下所示:

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
spec:
...
failurePolicy: Ignore # 默认值为 "Fail"
validations:
- expression: "object.spec.xyz == params.x"
通过前面的示例我们知道在策略对象中是通过 spec.validations[i].expression 来表示由 CEL 进行评估的表达式的,通过 CEL 表达式我们可以访问准入请求/响应的内容,可以组织成 CEL 变量以及一些其他变量:

  • object - 来自传入请求的对象,对于 DELETE 请求,该值为 null。
  • oldObject - 现有对象,对于 CREATE 请求,该值为 null。
  • request - 准入请求的属性。
  • params - 正在评估的策略绑定引用的参数资源,如果未设置 ParamKind,则值为 null。
apiVersion、kind、metadata.name 和 metadata.generateName 这些属性我们始终可以从对象的根进行访问,没有其他元数据属性可访问。

有关K8s 原生支持的准入策略管理的更多相关文章

  1. ruby - i18n Assets 管理/翻译 UI - 2

    我正在使用i18n从头开始​​构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在ruby​​onrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi

  2. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  3. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  4. ruby - (Ruby || Python) 窗口管理器 - 2

    我想用这两种语言中的任何一种(最好是ruby​​)制作一个窗口管理器。老实说,除了我需要加载某种X模块外,我不知道从哪里开始。因此,如果有人有线索,如果您能指出正确的方向,那就太好了。谢谢 最佳答案 XCB,X的下一代API使用XML格式定义X协议(protocol),并使用脚本生成特定语言绑定(bind)。它在概念上与SWIG类似,只是它描述的不是CAPI,而是X协议(protocol)。目前,C和Python存在绑定(bind)。理论上,Ruby端口只是编写一个从XML协议(protocol)定义语言到Ruby的翻译器的问题。生

  5. ruby-on-rails - 事件管理员和自定义方法 - 2

    这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什

  6. ruby-on-rails - (Ruby,Rails) 基于角色的身份验证和用户管理...? - 2

    我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源

  7. ruby - 如何使用 readline 支持重新安装 ruby​​? - 2

    我已经按照https://github.com/wayneeseguin/rvm#installation上的说明通过RVM安装了Ruby.有关信息,我有所有文件(readline-5.2.tar.gz、readline-6.2.tar.gz、ruby-1.9.3-p327.tar.bz2、rubygems-1.8.24.tgz、wayneeseguin-rvm-stable.tgz和yaml-0.1.4.tar.gz)在~/.rvm/archives目录中,我不想在任何目录中重新下载它们方式。当我这样做时:sudo/usr/bin/apt-getinstallbuild-essent

  8. ruby-on-rails - "undefined method ` stub_request '"访问 RSpec 支持文件中的方法时 - 2

    我的Ruby-on-Rails项目中有以下文件结构,用于规范:/spec/msd/serviceservice_spec.rb/support/my_modulerequests_stubs.rb我的request_stubs.rb有:moduleMyModule::RequestsStubsmodule_functiondeflist_clientsurl="dummysite.com/clients"stub_request(:get,url).to_return(status:200,body:"clientsbody")endend在我的service_spec.rb我有:re

  9. ruby - Ruby 是否支持逐字字符串? - 2

    Ruby是否支持(找不到更好的词)非转义(逐字)字符串?就像在C#中一样:@"c:\ProgramFiles\"...或者在Tcl中:{c:\ProgramFiles\} 最佳答案 是的,您需要在字符串前加上%前缀,然后是描述其类型的单个字符。你想要的是%q{c:\programfiles\}。镐书很好地涵盖了这一点here,部分是通用分隔输入。 关于ruby-Ruby是否支持逐字字符串?,我们在StackOverflow上找到一个类似的问题: https:/

  10. ruby - 在 Ruby 1.8 中支持 Ruby 1.9 的哈希语法 - 2

    我正在编写一个Rubygem,在我的代码中使用{key:'value'}哈希语法。我的测试都在1.9.x中通过,但我(可以理解)在1.8.7中得到syntaxerror,unexpected':',expecting')'。是否有支持1.8.x的最佳实践?我是否需要使用我们的老friend=>重写代码,还是有更好的策略? 最佳答案 我认为你运气不好,如果你想支持1.8,那么你必须使用=>。像往常一样,我会提到在1.9的某些情况下您必须使用=>:如果键不是一个符号。请记住,任何对象(符号、字符串、类、float……)都可以是Ruby哈

随机推荐