翻译:小君君 技术校对:星空下的文仔、bot
在项目中, Kubernetes 集群会对 Kubernetes APIServer 的每个请求都进行身份验证和授权管理。在此过程中,授权管理通常由 RBAC 授权模块来实现,但开发者也可以选择其他组件,如 Open Policy Agent(OPA)。
本文从使用目的、设计方式以及示例演示阐述了如何利用 Webhook 授权模块使 OPA 实现高级授权策略。
使用动机
在一些项目中,我们希望为用户提供类似集群管理员的访问权限。但为了确保基线的安全性和稳定性,我们不希望授予用户完整的集群管理员权限。例如:
kube-system
之外的所有 namespace,因为我们的基础设施(例如监视和日志记录)部署在kube-system
中;root
的身份运行容器,也不允许用户直接挂载hostPath
卷。面对这些要求,一种解决方案是通过 Kubernetes RBAC 和一个自定义操作符实现授权。其基本思想是让所有必要的权限通过 RBAC RoleBindings 进行授予绑定。并且,除了kube-system
(通过 operator)之外,我们为每个 namespace 的客户提供了 ClusterRole admin
。每当我们发现某些东西不能像预期那样工作时,我们就会通过每个 namespace 角色或 ClusterRole 添加其他权限。
但是,这种方式会出现很多针对特定用例的单独规则。从长远发展角度来看,这些规则无法得到很好的维护。特别是在用户群不断增长的情况下,只要有人检测到与配置不匹配的边缘情况,调整角色不太可行。
综上所述,我们不能选择基于白名单的配置授权,而是需要切换到基于黑名单的模型。因为,我们真正想要的是为客户提供集群管理员访问权限,并限制某些特定权限。
基于白名单与黑名单的授权
关于授权,绝大多数要求可以通过 Roles 和 RoleBindings 简单地使用 RBAC 授权模块来实现 [1]。但 RBAC 在设计上仅限于白名单。即对于每个请求,它会选择检查其中的一个 Roles 和 RoleBindings 是否适用,然后批准请求。
请求只有在没有匹配项时才会被拒绝,虽然听起来限制不大,但一些特定用例需要更大的灵活性。例如:
kube-system
之外的所有 namespace 中创建/更新/删除 Pod 时,通过 RBAC 实现此目的的唯一方法是在每个 namespace 的基础上分配权限。例如,我们可以部署 ClusterRole 和每个 namespace 的 RoleBinding。如果 namespace 随时间而变化,则必须手动部署此 RoleBindings 或为它运行 operator;如果你有很多这样的用例,你就要通过运营商来实现很多自定义的逻辑,但是你需要知道这些用例可能不会被扩展。因为这样做就会有很多的运营商和随附的 RBAC 角色,我们将很难控制用户实际拥有的权利。下面,我们将展示如何通过 OPA 轻松实现上述两种情况。
Webhook 授权模块与 ValidatingWebhook & MutatingWebhook
除了本文探讨的情况,一些高级用例也可以通过 Dynamic Admission Control 实现,如 ValidatingWebhook 或 MutatingWebhook。这里为大家推荐两篇关于如何使用 OPA 的博客:Policy Enabled Kubernetes with Open Policy Agent [2]以及 Kubernetes Compliance with Open Policy Agent [3]。
动态准入控制具有以下限制:仅在 Kubernetes 资源上调用 Webhook 来创建、更新和删除事件。因此,它们不可能拒绝获取的请求。但与 Webhook 授权模块相比,它们也具有优势,因为它们可以根据 Kubernetes 资源的内容拒绝请求。这些是 Webhook 授权模块无法访问的信息。
作为参考,Webhook 授权模块由 SubjectAccessReviews 决定,而 ValidatingWebhook 和 MutatingWebhook 则由 AdmissionReviews 决定。在实践中,我们通过授权模块和 MutatingWebhook 来集成 OPA。
设计
本节概述了 Kubernetes 如何与 OPA 集成。由于 OPA 本身不能实现 Kubernetes 所需的 REST 接口,所以 Kubernetes Policy Controller 将 Kubernetes SubjectAccessReviews 和 AdmissionReviews 转换为 OPA 查询。
对于 Kubernetes API 服务器收到的每个请求,执行以下序列:
因此,根据我们想要拒绝的具体内容,我们可以实施授权或许可 OPA 策略。有关如何配置此方案的更多信息,请参见 open-policy-agent / kubernetes-policy-controller(授权方案[4])。
实现
本节将说明如何使用 OPA 实现上述用例。除 kube-system 之外,为每个 namespace 创建/更新/删除 Pod。
它的基本思想是通过 RBAC 在集群范围内授予 Pod 的创建/更新/删除权限,然后使用 OPA 策略拒绝访问 kube-system 中的 Pod。首先,我们授予user
组创建/更新/删除 Pod 的权限:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: pods
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create", "update", "delete"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: user-pods
subjects:
- kind: Group
name: user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: pods
apiGroup: rbac.authorization.k8s.io
现在,允许user
组中每个用户在集群范围内创建/更新/删除 Pod。如有需要通过 OPA 限制这些权限,请部署以下策略:
package authorization
import data.k8s.matches
deny[{
"id": "pods-kube-system",
"resource": {
"kind": kind,
"namespace": namespace,
"name": name,
}, "resolution":
{
"message": "Your're not allowed to create/update/delete pods in kube-system"
},
}]
{
matches[[kind, namespace, name, resource]]
not re_match("^(system:kube-controller-manager|system:kube-scheduler)$",
resource.spec.user)
resource.spec.resourceAttributes.namespace = "kube-system"
resource.spec.resourceAttributes.resource = "pods"
re_match("^(create|update|delete|deletecollections)$",
resource.spec.resourceAttributes.verb)
}
注意
排除system:kube-controller-manager
和system:kube-scheduler
,因为 Kubernetes 控制管理器和调度程序都必须能够访问 Pod。
resource.spec.resourceAttributes.resource ="pods" 如果删除,我们就可以限制对kube-system
中所有 namespace 资源的访问。
OPA 使我们为所有策略编写单元测试变得非常容易。有关更多信息,请参阅如何测试策略[5]。
在特定 StorageClass 上执行创建/更新/删除
在此示例中,我们要授予用户对除ceph
之外的所有 StorageClass 创建/更新/删除权限。与第一个示例一样,我们必须通过 RBAC 授予用户访问权限:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: storageclasses
rules:
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["create", "update", "delete"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: user-storageclasses
subjects:
- kind: Group
name: user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: storageclasses
apiGroup: rbac.authorization.k8s.io
现在我们需要拒绝通过 OPA 访问 StorageClass 的ceph
,部署以下策略:
package authorization
import data.k8s.matches
deny[{
"id": "storageclasses",
"resource": {
"kind": kind,
"namespace": namespace,
"name": name,
},
"resolution": {"message": "Your're not allowed to create/update/delete the StorageClass'ceph'"},
}] {
matches[[kind, namespace, name, resource]]
resource.spec.resourceAttributes.resource = "storageclasses"
resource.spec.resourceAttributes.name = "ceph"
re_match("^(create|update|delete|deletecollections)$",
resource.spec.resourceAttributes.verb)
}
你可以通过以下的方式进行策略的单元测试:
package authorization
test_deny_update_storageclass_ceph {
deny[{"id": id, "resource":
{
"kind": "storageclasses", "namespace": "", "name": "ceph"},
"resolution": resolution
}]
with data.kubernetes.storageclasses[""].ceph as
{ "kind": "SubjectAccessReview",
"apiVersion": "authorization.k8s.io/v1beta1",
"spec":
{ "resourceAttributes":
{ "verb": "update",
"version": "v1",
"resource": "storageclasses",
"name": "ceph",
},
"user": "alice",
"group": ["user"],
},
}
}
总结
总之,与内置 RBAC 授权相比,OPA 允许更灵活的策略,尤其是在不使用其他 operator 的情况下。在我看来,将 OPA 直接集成为授权模块和准入控制器会很好,但与此同时,Kubernetes Policy Controller 弥补了 Kubernetes 和 OPA 之间的差距。以下是我通过实践得到的一些启示:
calico
;cluster-admin
、admin
;kube-system
中的某些特定 Pod;以上就是本文的全部内容,你是如何看待 OPA 作为 Kubernetes 的策略引擎?如果感兴趣,不妨在读完本文后,亲自动手进行尝试!
1.https://kubernetes.io/docs/reference/access-authn-authz/rbac/
2.https://medium.com/capital-one-tech/policy-enabled-kubernetes-with-open-policy-agent-3b612b3f0203
3.https://itnext.io/kubernetes-compliance-with-open-policy-agent-3d282179b1e9
4.https://github.com/open-policy-agent/gatekeeper
5.https://www.openpolicyagent.org/docs/how-do-i-test-policies.html