使用 OPA Gatekeeper 执行 Kubernetes 策略
共 8570字,需浏览 18分钟
·
2021-12-15 03:06
如果您所在的组织一直在使用 Kubernetes,您可能一直在寻找如何控制终端用户在集群上的行为,以及如何确保集群符合公司政策。这些策略可能需要满足管理和法律要求,或者符合最佳执行方法和组织惯例。使用 Kubernetes,如何在不牺牲开发敏捷性和运营独立性的前提下确保合规性?
例如,您可以执行以下策略:
所有镜像必须来自获得批准的存储库 所有入口主机名必须是全局唯一的 所有 Pod 必须有资源限制 所有命名空间都必须具有列出联系的标签
在接收请求被持久化为 Kubernetes 中的对象之前,Kubernetes 允许通过 admission controller webhooks 将策略决策与 API 服务器分离,从而拦截这些请求。Gatekeeper 创建的目的是使用户能够通过配置(而不是代码)自定义控制许可,并使用户了解群集的状态,而不仅仅是针对评估状态的单个对象,在这些对象准许加入的时候。Gatekeeper
是 Kubernetes 的一个可定制的许可 webhook ,它由 Open Policy Agent (OPA)
强制执行, OPA 是 Cloud Native 环境下的策略引擎,由 CNCF 主办。
Kubernetes 提供通过准入控制器(Admission Controller)Webhooks 扩展 API Server 功能的能力。在创建,更新或删除资源时,Kubernetes 就会调用这些 Webhooks。Gatekeeper 被作为验证 Webhook,并执行 Kubernetes CRD 中定义的策略。除了使用准入控制外,Gatekeeper 还提供了审核 Kubernetes 集群中现有资源并标记当前违反策略的功能。
安装
赋予集群管理员权限:
kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole cluster-admin \
--user admin
然后安装 opa-gatekeeper
,版本需要与k8s版本匹配,目前测试1.15版本使用3.4,1.19版本使用3.5,1.21使用3.6没有问题
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.5/deploy/gatekeeper.yaml
注意: 默认gatekeeper不约束对资源的删除,例如:opa中定义了某个用户无法创建和更新的资源,但是这个用户却能删除该资源,为了让其没有删除权限,需要修改gatekeeper.yaml文件:
operations:
- CREATE
- UPDATE
- DELETE //添加该行
示例
创建ConstraintTemplate模板:
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/demo/basic/templates/k8srequiredlabels_template.yaml
查看模板列表:
kubectl get ConstraintTemplate
查看模板是否有opa-rego语法错误:
kubectl describe ConstraintTemplate k8srequiredlabels
k8srequiredlabels_template.yaml
内容如下:
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels // 按需更改
spec:
crd:
spec:
names:
kind: K8sRequiredLabels // 按需更改
validation:
# Schema for the `parameters` field
openAPIV3Schema:
properties: // 需要满足的条件的参数
labels:
type: array
items: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg, "details": {"missing_labels": missing}}] {
provided := {label | input.review.object.metadata.labels[label]} // 获取到创建的对象的所有label
required := {label | label := input.parameters.labels[_]} // 获取到需要提供的label
missing := required - provided // rego语言支持集合相减,得到未满足的label
// 断言未满足的label数量>0,如果大于0,说明条件满足,
// violation为true,说明违反了约束,返回错误
count(missing) > 0
msg := sprintf("you must provide labels: %v", [missing])
}
创建约束 Constraints:
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/demo/basic/constraints/all_ns_must_have_gatekeeper.yaml
all_ns_must_have_gatekeeper.yaml
内容如下:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-gk
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"] // 表示这个约束会在创建命名空间的时候被应用
parameters:
labels: ["gatekeeper"] //传递给opa的参数,此处表示一个key为labels,value为一个列表的字典,与ConstraintTemplate里的properties要匹配上,此处表示要创建的对象需要含有gatekeeper的label
注意 match 字段,它定义了将应用给定约束的对象的范围。它支持以下匹配器:
kind
接受带有 apiGroups 和 kind 字段的对象列表,这些字段列出了约束将应用到的对象的组/种类。如果指定了多个组/种类对象,则资源在范围内只需要一个匹配项。scope
接受 *、Cluster 或 Namespaced 决定是否选择集群范围和/或命名空间范围的资源。(默认为 *)namespaces
是命名空间名称的列表。如果已定义,则约束仅适用于列出的命名空间中的资源。命名空间还支持基于前缀的 glob。例如,namespaces: [kube-*] 匹配 kube-system 和 kube-public。excludeNamespaces
是命名空间名称的列表。如果已定义,则约束仅适用于不在列出的命名空间中的资源。ExcludedNamespaces 还支持基于前缀的 glob。例如,excludedNamespaces: [kube-*] 匹配 kube-system 和 kube-public。labelSelector
是标准的 Kubernetes 标签选择器。namespaceSelector
是针对对象的包含名称空间或对象本身的标签选择器,如果对象是名称空间。name 是对象的名称。如果已定义,则匹配具有指定名称的对象。Name 还支持基于前缀的 glob。例如,名称:pod-* 匹配 pod-a 和 pod-b。
请注意,如果指定了多个匹配器,则资源必须满足每个顶级匹配器(种类、名称空间等)才能在范围内。每个顶级匹配器都有自己的语义来确定什么是匹配。空匹配器被认为是包含的(匹配所有内容)。还要了解命名空间、excludedNamespaces 和 namespaceSelector 将匹配未命名空间的集群范围资源。为避免这种情况,请将范围调整为 Namespaced。
示例
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPodLabels
metadata:
name: pod-must-have-gk
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
labelSelector:
matchExpressions:
- key: app
operator: In
values: [nginx1]
namespaces: ["ns-test"]
parameters:
labels: ["gatekeeper"]
查看Constraints:
kubectl get constraints
测试
创建如下的命名空间:
apiVersion: v1
kind: Namespace
metadata:
name: ns-test
labels:
a: b
#gatekeeper: "abc"
此时不给命名空间添加key为gatekeeper的label,会报错:
Error from server ([ns-must-have-gk] you must provide labels: {"gatekeeper"}): error when creating "ns-test.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [ns-must-have-gk] you must provide labels: {"gatekeeper"}
#gatekeeper: "abc"
这行的注释打开,则能成功创建。
获取input
如果需要创建自己的约束,但是不知道传入的参数即input是什么,可以在创建模板时violation只保留下面几行,此时创建对象时必定会失败,然后获取到输出的错误信息,里面即包含所有input信息,之后再通过rego语法去获取需要的数据即可
violation[{"msg": msg}] {
msg := sprintf("input: %v", [input])
}
案例1
要求:只能允许"user1", "user2", "kubernetes-admin"这三个用户拥有创建、更新、删除default命名空间中label为 app: nginx1
的pod。
创建约束模板:
[root@master1 opa]# cat k8srequiredusers_template.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8srequiredusers
spec:
crd:
spec:
names:
kind: K8sRequiredusers
validation:
# Schema for the `parameters` field
openAPIV3Schema:
properties:
users:
type: array
items: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredusers
violation[{"msg": msg}] {
// 把创建时的用户名放在一个集合里
user := { input.review.userInfo.username }
// 获取允许创建该pod的用户集合
permitUser := {user | user := input.parameters.users[_]}
// 集合-集合
allow := permitUser - user
// 如果减完之后集合还相等,说明这个用户没有在允许的集合中,不满足约束条件
allow == permitUser
msg := sprintf("user %v not allowd", [user])
}
创建约束:
[root@master1 opa]# cat requiredusers_constraind.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredusers
metadata:
name: required-users
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
labelSelector:
matchExpressions:
- key: app
operator: In
values: [nginx1]
namespace: ["default"]
parameters:
users: ["user1", "user2", "kubernetes-admin"]
创建pod,用kubernetes-admin用户创建这个pod,能正常创建,同时更新、删除也能成功:
[root@master1 opa]# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod2
labels:
app: nginx1
spec:
containers:
- name: nginx-pod
image: nginx:1.12
imagePullPolicy: Never
使用myuser用户创建、删除、更新均不成功,会提示约束报错信息。
案例2
ConfigMap、Secret、PersistentVolumeClaim必须包含security标签,并且标签必须为s1,s2,s3,s4中之一
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: resourcesecurityclassification
spec:
crd:
spec:
names:
kind: ResourceSecurityClassification
validation:
# Schema for the `parameters` field
openAPIV3Schema:
properties:
labelkey:
type: string
labelvalues:
type: array
items: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package resourcesecurityclassification
violation_label_and_value(provided, required) = allow {
rlt := provided - required
allow := (rlt != provided)
}
violation[{"msg": msg}] {
providedlabels := {label | label := input.review.object.metadata.labels[_]}
providedkey := {key | input.review.object.metadata.labels[key]}
requiredkey := input.parameters.labelkey
requiredlabels := {label | label := input.parameters.labelvalues[_]}
kind := input.review.object.kind
k := violation_label_and_value(providedkey, { requiredkey })
v := violation_label_and_value(requiredlabels, providedlabels)
{ k } & { v } != { true }
msg := sprintf("[%v] must have label: [%v] and must have one of the following values: %v", [kind, requiredkey, requiredlabels])
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: ResourceSecurityClassification
metadata:
name: resources-must-have-security-classification
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["ConfigMap","Secret","PersistentVolumeClaim"]
parameters:
labelkey: "security"
labelvalues: ["s1", "s2", "s3", "s4"]
官方地址:https://open-policy-agent.github.io/gatekeeper/website/docs/install
原文链接:https://blog.csdn.net/weixin_42758299/article/details/120978564