39

OPA Gatekeeper 策略入门

 4 years ago
source link: https://mp.weixin.qq.com/s/dqAi4vBy8xandVsyHnTtnQ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

OPA Gatekeeper 策略入门

原创 崔秀龙 伪架构师 5天前

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 [email protected] is denied.): admission webhook "validation.gatekeeper.sh" denied the request: [denied by ns-user] User [email protected] 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..


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK