Kubernetes 主要通过 API Server 对外提供服务,对于这样的系统来说,如果不加以安全限制,那么可能导致请求被滥用,甚至导致整个集群崩塌。
Kubernetes 中提供了良好的多租户认证管理机制,RBAC正式其中重要的一个,今天我们来详细聊聊 K8s 中的 RBAC。
K8s 中的权限管理
Kubernetes 主要通过 API Server 对外提供服务,Kubernetes 对于访问 API 的用户提供了相应的安全控制:认证和授权。认证解决用户是谁的问题,授权解决用户能做什么的问题。只有通过合理的权限控制,才能够保证整个集群系统的安全可靠。
下图是 API 访问需要经过的三个步骤,它们分别是:认证、授权和准入。
我们核心关注在鉴权部分,Kubernetes 提供了以下 4 种鉴权模式:
-
Node:一种特殊的授权模块,基于 Node 上运行的 Pod 为 Kubelet 授权
-
ABAC:基于属性的访问控制
-
RBAC:基于角色的访问控制
-
Webhook:HTTP 请求回调,通过一个 WEB 应用鉴定是否有权限进行某项操作
我们现在深入讨论的就是RBAC模式。
Rbac 基础
RBAC就是一个权限控制模型,这个模型是经过时间沉淀之后,相当通用、成熟且被大众接受认可的一个模型。我的理解是RBAC和数学公式是一个道理,数学题可以套用数学公式,而权限系统也可以套用RBAC权限模型。
RBAC(Role-Based Access Control)权限模型的概念,即:基于角色的权限控制。通过角色关联用户,角色关联权限的方式间接赋予用户权限。
按照小白(我)的逻辑呢,权限嘛,只要给用户分配权限就好咯,何必多此一举,中间加一个角色,把权限倒个手。
其实之所以在中间加一层角色,是为了增加安全性和效率,而且后续扩展上也会提升不少。
打个比方,比如多个用户拥有相同的权限,在分配的时候就要分别为这几个用户指定相同的权限,修改时也要为这几个用户的权限进行一一修改。有了角色后,只需要为该角色制定好权限后,将相同权限的用户都指定为同一个角色即可,便于权限管理。对于批量的用户权限调整,只需调整用户关联的角色权限,无需对每一个用户都进行权限调整,既大幅提升权限调整的效率,又降低了漏调权限的概率。
k8s 中的RBAC
RBAC 鉴权机制使用 rbac.authorization.k8s.io API 组来驱动鉴权决定, 允许你通过 Kubernetes API 动态配置策略。
要启用 RBAC,在启动 API 服务器时将 --authorization-mode 参数设置为一个逗号分隔的列表并确保其中包含 RBAC。
核心元素
掌握K8s 中的RBAC,首先我们要了解会涉及到的基本概念和元素
-
Rule:规则,一组属于不同 API Group 的操作集合;
-
Role:角色,用于定义一组对 Kubernetes API 对象操作的一组规则,范围限定在 namespace;
-
ClusterRole:集群角色,该角色不受 namespace 的限制;
-
Subject:对象,也就是规则作用的对象;
-
RoleBinding:将角色和对象进行绑定,范围限定在 namespace;
-
ClusterRoleBinding:将集群角色和对象进行绑定,不受 namespace 限制;
Role 与 ClusterRole
在 RBAC API 中,一个角色包含了一套表示一组权限的规则。权限以纯粹的累加形式累积(没有” 否定” 的规则)。角色可以由命名空间(namespace)内的 Role 对象定义,而整个 Kubernetes 集群范围内有效的角色则通过 ClusterRole 对象实现。
一个 Role 对象只能用于授予对某一单一命名空间中资源的访问权限。以下示例描述了”default” 命名空间中的一个 Role 对象的定义,用于授予对 pod 的读访问权限:
kind: RoleapiVersion: rbac.authorization.k8s.io/v1beta1metadata:
namespace: default
name: pod-readerrules:- apiGroups: [""] # 空字符串"" 表明使用 core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
ClusterRole 对象可以授予与 Role 对象相同的权限,但由于它们属于集群范围对象, 也可以使用它们授予对以下几种资源的访问权限:
· 集群范围资源(例如节点,即 node)
· 非资源类型 endpoint(例如”/healthz”)
· 跨所有命名空间的命名空间范围资源(例如 pod,需要运行命令 kubectl get pods --all-namespaces 来查询集群中所有的 pod)
下面示例中的 ClusterRole 定义可用于授予用户对某一特定命名空间,或者所有命名空间中的 secret(取决于其 绑定 方式)的读访问权限:
kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1beta1metadata:
# 鉴于 ClusterRole 是集群范围对象,所以这里不需要定义 "namespace" 字段
name: secret-readerrules:- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]
RoleBinding 与 ClusterRoleBinding
角色绑定将一个角色中定义的各种权限授予一个或者一组用户。角色绑定包含了一组相关主体(即 subject, 包括用户 ——User、用户组 ——Group、或者服务账户 ——Service Account)以及对被授予角色的引用。在命名空间中可以通过 RoleBinding 对象授予权限,而集群范围的权限授予则通过 ClusterRoleBinding 对象完成。
RoleBinding 可以引用在同一命名空间内定义的 Role 对象。下面示例中定义的 RoleBinding 对象在”default” 命名空间中将”pod-reader” 角色授予用户”jane”。这一授权将允许用户”jane” 从”default” 命名空间中读取 pod。
以下角色绑定定义将允许用户 "jane" 从 "default" 命名空间中读取 pod。
kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1beta1metadata:
name: read-pods
namespace: defaultsubjects:- kind: User
name: jane
apiGroup: rbac.authorization.k8s.ioroleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
RoleBinding 对象也可以引用一个 ClusterRole 对象用于在 RoleBinding 所在的命名空间内授予用户对所引用的 ClusterRole 中 定义的命名空间资源的访问权限。这一点允许管理员在整个集群范围内首先定义一组通用的角色,然后再在不同的命名空间中复用这些角色。
例如,尽管下面示例中的 RoleBinding 引用的是一个 ClusterRole 对象,但是用户”dave”(即角色绑定主体)还是只能读取”development” 命名空间中的 secret(即 RoleBinding 所在的命名空间)。
# 以下角色绑定允许用户 "dave" 读取 "development" 命名空间中的 secret。kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1beta1metadata:
name: read-secrets
namespace: development # 这里表明仅授权读取 "development" 命名空间中的资源。subjects:- kind: User
name: dave
apiGroup: rbac.authorization.k8s.ioroleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
最后,可以使用ClusterRoleBinding在集群级别和所有命名空间中授予权限。下面示例中所定义的ClusterRoleBinding允许在用户组”manager” 中的任何用户都可以读取集群中任何命名空间中的 secret。
以下 ClusterRoleBinding 对象允许在用户组 "manager" 中的任何用户都可以读取集群中任何命名空间中的 secret。
kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1beta1metadata:
name: read-secrets-globalsubjects:- kind: Group
name: manager
apiGroup: rbac.authorization.k8s.ioroleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
与资源绑定
大多数资源由代表其名字的字符串表示,例如”pods”,就像它们出现在相关 API endpoint 的 URL 中一样。然而,有一些 Kubernetes API 还 包含了” 子资源”,比如 pod 的 logs。在 Kubernetes 中,pod logs endpoint 的 URL 格式为:
GET /api/v1/namespaces/{namespace}/pods/{name}/log
在这种情况下,”pods” 是命名空间资源,而”log” 是 pods 的子资源。为了在 RBAC 角色中表示出这一点,我们需要使用斜线来划分资源 与子资源。如果需要角色绑定主体读取 pods 以及 pod log,您需要定义以下角色:
kind: RoleapiVersion: rbac.authorization.k8s.io/v1beta1metadata:
namespace: default
name: pod-and-pod-logs-readerrules:- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list"]
通过resourceNames列表,角色可以针对不同种类的请求根据资源名引用资源实例。当指定了resourceNames列表时,不同动作 种类的请求的权限,如使用”get”、”delete”、”update” 以及”patch” 等动词的请求,将被限定到资源列表中所包含的资源实例上。例如,如果需要限定一个角色绑定主体只能”get” 或者”update” 一个 configmap 时,您可以定义以下角色:
kind: RoleapiVersion: rbac.authorization.k8s.io/v1beta1metadata:
namespace: default
name: configmap-updaterrules:- apiGroups: [""]
resources: ["configmap"]
resourceNames: ["my-configmap"]
verbs: ["update", "get"]
值得注意的是,如果设置了resourceNames,则请求所使用的动词不能是 list、watch、create 或者 deletecollection。由于资源名不会出现在 create、list、watch 和 deletecollection 等 API 请求的 URL 中,所以这些请求动词不会被设置了resourceNames的规则所允许,因为规则中的resourceNames 部分不会匹配这些请求。
对主体的引用
RoleBinding 或者 ClusterRoleBinding 可绑定角色到某 主体(Subject) 上。主体可以是组,用户或者服务账户。
Kubernetes 用字符串来表示用户名。用户名可以是普通的用户名,像 "alice";或者是邮件风格的名称,如 "bob@example.com", 或者是以字符串形式表达的数字 ID。你作为 Kubernetes 管理员负责配置身份认证模块, 以便后者能够生成你所期望的格式的用户名。
关于HummerRisk
HummerRisk 是开源的云原生安全平台,以非侵入的方式解决云原生的安全和治理问题,核心能力包括混合云的安全治理和K8S容器云安全检测。
Github 地址:https://github.com/HummerRisk/HummerRisk
Gitee 地址:https://gitee.com/hummercloud/HummerRisk