Skip to main content

Command Palette

Search for a command to run...

OPA Gatekeeper 几条入门策略

Updated
3 min read

Gatekeeper 是基于 OPA(Open Policy Agent) 的一个 Kubernetes 策略解决方案。在之前的文章中说过,在 PSP/RBAC 等内置方案之外,在 Kubernetes 中还可以通过策略来实现一些额外的管理、安全方面的限制,本文将会从安装开始,介绍几条实用的小策略。

安装篇

安装可以通过 kubectl 来进行:

$ kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yaml

namespace/gatekeeper-system created
......
gatekeeper-validating-webhook-configuration created

或者也可以使用 Helm(目前只支持 Helm 2):

helm repo add gatekeeper https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/charts/gatekeeper
helm install gatekeeper/gatekeeper --devel

策略简介

Gatekeeper 的策略通常是由两个资源对象组成的:Template 和 Constraint。

Template:其定义分为两部分:crdtargetscrd 的确是一个 CRD 定义,也就是说生成一个 Template CR 对象,会随之生成一个 CRD;targets 则是一组 rego 为主体的代码包——个人表示很反对这种 YAML 中加代码的粗暴行径。

Contsraint:这个对象的定义来自于 Template 生成的 CRD,它负责为模板输出两种内容:其一是对 Kubernetes 资源对象的过滤,其二就是根据 CRD 定义,为 Template 提供输入参数。

只允许特定用户名操作特定命名空间

cluster-admin 成为缺省用户的情况下,我们希望限制特定用户在 Namespace 中的能力,例如下面的规则,会检查用户名前缀是否为命名空间名称:

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: ns-user
spec:
  crd:
    spec:
      names:
        kind: ns-user
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package nsuser
        violation[{"msg": msg}] {
          user_name = input.review.userInfo.username
          ns = input.review.object.metadata.namespace
          not startswith(user_name, ns)
          msg = sprintf("User %v is denied.", [user_name])
        }

上面的代码有几个需要注意的:

  1. metadata.name 要和 spec.crd.spec.names.kind 一致

  2. 规则顺序执行,使用 startswith 函数判断输入内容里面的用户名和命名空间是否为前缀关系

  3. 如果一致,则规则停止执行;如果不一致,则输出拒绝信息。

声明了 Template 之后,使用 kubectl apply -f 提交到集群。

然后创建一个 constraints

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: ns-user
metadata:
  name: ns-user
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["ServiceAccount"]

这里的 kind 字段使用的就是前面模板生成的 CRD(所以 template 和 contsrint 同时创建的话,后者的创建过程可能失败)。在 match 字段中,我们限制面向的是 ServiceAccount 对象,接下来测试一下:

$ kubectl create sa ab
Error from server ([denied by ns-user] User dustise@gmail.com is denied.): admission webhook "validation.gatekeeper.sh" denied the request: [denied by ns-user] User dustise@gmail.com is denied.

$ kubectl create sa sbac --kubeconfig=kubeconfig-defaultsa -n default
serviceaccount/sbac created

$ kubectl create deployment nginx --image=nginx
deployment.apps/nginx created

上面可以看到,策略成功发挥作用,使用缺省用户无法创建 sa,但是可以创建 deployment,换用名为 defaultsa 的用户,则能够创建成功。

这里如果多做一点测试,会发现 DELETE 操作是不受限制的,原因是 Gatekeeper 的 Webhook 配置去掉了对 DELETE 的反应,可以 kubectl edit ValidatingWebhookConfiguration gatekeeper-validating-webhook-configuration 进行编辑,在 operations 字段中加入 DELETE 元素。

只允许特定镜像前缀

如果在某集群中,我们要求仅适用内网仓库中的镜像,可以使用如下策略:

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: imagecheck
spec:
  crd:
    spec:
      names:
        kind: imagecheck
      validation:
        openAPIV3Schema:
          properties:
            prefix:
              type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package image
        violation[{"msg": msg}] {
          containers = input.review.object.spec.template.spec.containers
                some i
                image := containers[i].image
                not startswith(image, input.parameters.prefix)
                msg := sprintf("Image '%v' is not allowed.", [image])
        }

相对前面的模板,这个模板复杂了一些:

  1. spec.validation 字段中加入了一个字符串类型的属性,用这个属性作为参数,定义允许使用的容器前缀,使用 input.parameters.prefix 的方式来引用参数。

  2. 有一行奇怪的代码 some isome关键字声明了一个名为 i 的变量,规则会使用变量 i 对数组进行轮询,查找前缀不符合参数要求的镜像名称。

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: imagecheck
metadata:
  name: imagecheck
spec:
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds:
        - "Deployment"
        - "DaemonSet"
        - "StatefulSet"
  parameters:
    prefix: "dustise/"

Constraints 中注明,对 Deployment 等三种对象进行校验,要求其镜像前缀为 dustise/,下面我们进行一个测试:

$ kubectl create deployment sleep --image=nginx
Error from server ([denied by imagecheck] Image 'nginx' is not allowed.): admission webhook "validation.gatekeeper.sh" denied the request: [denied by imagecheck] Image 'nginx' is not allowed.

$ kubectl create deployment sleep --image=dustise/sleep
deployment.apps/sleep created

Nginx 镜像被禁止,而 dustise/sleep 镜像则成功创建。

Pod 必须具备资源限制

我们建议所有 Pod 都配置资源限制和请求,便于调度,也能预防系统资源滥用。下面的模板会遍历 Pod 定义,并对资源限制不完整的容器发出警告。

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: resource-limit
spec:
  crd:
    spec:
      names:
        kind: resource-limit
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package limit
        resources_defined(x) {
          x.resources; x.resources.limits; x.resources.requests
        }
        violation[{"msg": msg}] {
          ctr_list = input.review.object.spec.template.spec.containers
          some i
          ctr = ctr_list[i]
          not resources_defined(ctr)
          msg = sprintf("%v containers without 'resource' fields", [ctr.name])
        }

模板文件中,我们定义了一个函数,分号分割的三个判断构成了逻辑与的关系,缺乏任何一个字段都会导致返回 false

接下来创建类似的 Constraint 对象:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: resource-limit
metadata:
  name: resource-limit
spec:
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds:
        - "Deployment"
        - "DaemonSet"
        - "StatefulSet"

再次创建 Deployment,会看到新的拒绝信息:

$ kubectl create deployment sleep2 --image=dustise/sleep 
Error from server ([denied by resource-limit] sleep containers without 'resource' fields): admission webhook "validation.gatekeeper.sh" denied the request: [denied by resource-limit] sleep containers without 'resource' fields

如果创建下列代码所包含的 Deployment 对象,则会成功:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: sleep
  name: sleep
spec:
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      containers:
      - image: dustise/sleep
        imagePullPolicy: Always
        name: sleep
        resources:
          limits:
            cpu: 100m
          requests:
            cpu: 100m
      dnsPolicy: ClusterFirst

小结

Rego 语法还是有点烦人的,好在官方源码中提供了一些样例和基本用途的代码库可以参考。另外也可以用 Rego Playground 进行在线调试,来编写稍微复杂一点的策略。

我还是喜欢 Kyverno..

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts