-
万字长文 | 使用 RBAC 限制对 Kubernetes 资源的访问
云和安全管理服务专家 新钛云服 祝祥翻译
通过本文,我们将学习如何从头开始重新创建 Kubernetes RBAC 授权模型,并了解Roles、ClusterRoles、ServiceAccounts、RoleBindings 和 ClusterRoleBindings 之间的关系。
随着集群中应用程序和资源数量的增加,我们可能需要查看并限制他们相关的权限,从而避免一些危险操作。这些危险操作往往会严重影响生产环境,有时候,甚至会引起长时间的停机,比如过大的权限导致正常运行的服务被删除。
正常情况下,我们可能希望限制生产系统仅仅允许少部分人管理以及访问。
或者,我们可能希望向部署在集群中的维护人员授予一组有限的权限。
我们可以通过Kubernetes 中的基于角色的访问控制 (RBAC) 来实现上述的需求。
Kubernetes API
在讨论RBAC之前,让我们看看授权模型在图中的位置。
假设我们希望将以下 Pod 部署到 Kubernetes 集群:
apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: sise image: learnk8s/app:1.0.0 ports: - containerPort: 8080
我们可以使用以下命令将 Pod 部署到集群:
$ kubectl apply -f pod.yaml
当我们输入时kubectl apply,会触发以下的操作。
kubectl 会:
1、从KUBECONFIG读取配置
2、从 API 中发现 API 和对象
3、验证资源客户端(是否有明显的错误?)
4、将带有有效负载的请求发送到kube-apiserver
当kube-apiserver收到请求时,它不会立即将其存储在 etcd 中。
首先,它必须验证请求者是否合法。
换句话说,它必须对请求进行身份验证。(https://kubernetes.io/docs/reference/access-authn-authz/authentication/)
一旦通过身份验证,请求者是否有权创建资源?
身份和权限不是一回事。
仅仅因为我们可以访问集群并不意味着我们可以创建或读取所有资源。
授权通常使用基于角色的访问控制 (RBAC)(https://kubernetes.io/docs/reference/access-authn-authz/rbac/)来完成。
借助基于角色的访问控制 (RBAC),我们可以分配精细的权限并限制用户或应用程序可以执行的操作。
在更实际的情况下,API 服务器按顺序执行以下操作:
1、收到请求后,对用户进行身份验证。
a. 当验证失败时,通过返回来拒绝请求401 Unauthorized。
b, 否则,进入下一阶段。
2、用户已通过身份验证,但他们是否有权访问资源
a. 如果他们没有权限,请通过返回来拒绝请求403 Forbidden。
b. 否则,继续。
在本文中,我们将重点关注授权部分。
将用户和权限与 RBAC 角色解耦
RBAC(Role-Based Access Control)即:基于角色的权限控制。通过角色关联用户,角色关联权限的方式间接赋予用户权限。
如下图:
有人会问为什么不直接给用户分配权限,还多此一举的增加角色这一环节呢?
其实是可以直接给用户分配权限,只是直接给用户分配权限,少了一层关系,扩展性弱了许多,适合那些用户数量、角色类型少的平台。
对于通常的系统,比如:存在多个用户拥有相同的权限,在分配的时候就要分别为这几个用户指定相同的权限,修改时也要为这几个用户的权限进行一一修改。有了角色后,我们只需要为该角色制定好权限后,将相同权限的用户都指定为同一个角色即可,便于权限管理。
对于批量的用户权限调整,只需调整用户关联的角色权限,无需对每一个用户都进行权限调整,既大幅提升权限调整的效率,又降低了漏调权限的概率。
要了解它是如何工作的,让我们想象一下,我们必须从头开始设计一个授权系统。
我们该如何确保用户对特定资源具有写入权限?
一个简单的实现可能涉及编写一个如下所示的列表:
| User | Permission | Resource | | ----- | ---------- | -------- | | Bob | read+write | app1 | | Alice | read | app2 | | Mo | read | app2 |
在这个例子中:
· Bob 有读写权限,可以访问app1但无权访问app2
· Mo 和 Alice对app2有只读权限,但是无权访问app1
上表适用于少数用户和资源,但一旦开始扩展它就会显示一些限制。
让我们假设Mo和Alice在同一个Team中,并且他们被授予对app1的读取权限。
我们必须将以下条目添加到表中:
| User | Permission | Resource | | --------- | ---------- | -------- | | Bob | read+write | app1 | | Alice | read | app2 | | Mo | read | app2 | | Alice | read | app1 | | Mo | read | app1 |
这很好,但Alice和Mo是否有相同的访问权限并不明显,因为他们是同一Team的成员。
- 在典型的授权系统中我们有用户访问资源
- 我们可以直接向用户分配权限,并定义他们可以使用的资源
- 这些权限直接映射资源。注意它们是如何作用于用户的
- 如果我们决定让第二个用户具有相同的权限,则必须关联相同的权限
我们可以通过在表中添加“Team”列来解决这个问题,但更好的替代方法是分解关系:
1、我们可以为权限定义一个通用属性:角色。
2、我们可以为定义的组织“Team”进行授权,而不是直接将权限分配给用户。
3、最后,我们可以用户加入到定义组织”Team“。
让我们看看这有什么不同。
现在你有两个权限映射关系表:
· 在第一个表中,权限映射到角色
· 在第二个表中,角色与身份相关联
| Role | Permission | Resource | | -------- | ---------- | -------- | | admin1 | read+write | app1 | | reviewer | read | app2 | | User | Roles | | ----- | -------- | | Bob | admin1 | | Alice | reviewer | | Mo | reviewer |
当我们希望 Mo 成为 app1 的管理员时那又该如何做了?
我们可以像这样将角色添加到用户:
| User | Roles | | ----- | ------------------- | | Bob | admin1 | | Alice | reviewer | | M
我们已经可以想象,将用户与权限与角色分离可以促进拥有大量用户和权限的大型组织中的安全管理。
- 使用 RBAC 时,我们拥有用户、资源和角色
- 权限不会直接分配给用户。相反,他们被包括在这个角色中
- 用户通过绑定关联到角色
- 由于角色是通用的,当新用户需要访问相同资源时,您可以使用现有角色并将其新绑定关联
Kubernetes 中的 RBAC
Kubernetes 实现了一个 RBAC 模型(以及其他几个模型)(https://kubernetes.io/docs/reference/access-authn-authz/authorization/#authorization-modules) 来保护集群中的资源。
基于角色(Role)的访问控制(RBAC)是一种基于组织中用户的角色来调节控制对 计算机或网络资源的访问的方法。
RBAC 鉴权机制使用 rbac.authorization.k8s.io API 组 来驱动鉴权决定,允许你通过 Kubernetes API 动态配置策略。
要启用 RBAC,在启动 API 服务器 时将 –authorization-mode 参数设置为一个逗号分隔的列表并确保其中包含 RBAC。
kube-apiserver --authorization-mode=Example,RBAC --<其他选项> --<其他选项>
因此 Kubernetes 使用了前面解释过的相同的三个概念:身份、角色和绑定。
它只是用稍微不同的名字来称呼它们。
例如,让我们检查以下授予对 Pod、Service等资源的访问权限所需的 YAML 定义:
apiVersion: v1 kind: ServiceAccount metadata: name: serviceaccount:app1 namespace: demo-namespace --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: role:viewer namespace: demo-namespace rules: # Authorization rules for this role - apiGroups: # 1st API group - '' # An empty string designates the core API group. resources: - services - pods verbs: - get - list - apiGroups: # 2nd API group - apiextensions.k8s.io resources: - customresourcedefinitions verbs: - list - apiGroups: # 3rd API group - cilium.io resources: - ciliumnetworkpolicies - ciliumnetworkpolicies/status --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: rolebinding:app1-viewer namespace: demo-namespace roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: role:viewer subjects: - kind: ServiceAccount name: serviceaccount:app1 namespace: demo-namespace
该文件分为三个部分:
1、ServiceAccount——这是访问资源的用户的身份
2、Role——包含访问资源的权限
3、将身份(ServiceAccount)关联到权限(Role)的 RoleBinding
配置提交到集群后,允许使用服务帐户的应用程序向以下端点发出请求:
# 1. Kubernetes builtin resources /api/v1/namespaces/{namespace}/services /api/v1/namespaces/{namespace}/pods # 2. A specific API extention provided by cilium.io /apis/cilium.io/v2/namespaces/{namespace}/ciliumnetworkpolicies /apis/cilium.io/v2/namespaces/{namespace}/ciliumnetworkpolicies/status
结果很成功,但是我们忽略了很多细节。
我们究竟授予了哪些资源访问权限?
什么是服务帐户?身份不只是集群中的“用户”吗?
为什么 Role 包含 Kubernetes 对象列表?
为了理解它们是如何工作的,让我们抛开 Kubernetes RBAC 模型并尝试从头开始重建它。
我们将重点关注三个要素:
1、识别和分配身份
2、’授予权限
3、将身份与权限相关联
分配身份:用户,组,robot账户
假设我们的新同事希望登录 Kubernetes 界面。
在这种情况下,我们应该有一个“帐户”或“用户”的实体,每个实体都有一个唯一的名称或 ID(例如电子邮件地址)。
我们应该如何将用户存储在集群中?
Kubernetes 没有代表常规用户帐户的对象。
无法通过 API 调用将用户添加到集群中。
相反,任何提供由集群的证书颁发机构 (CA) 签名的有效证书的参与者都被视为已通过身份验证。(https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/#normal-user)
在这种情况下,Kubernetes 从证书的“subject”中的通用名称字段中分配用户名(例如,“/CN=bob”)。
创建一个临时用户信息对象并将其传递给授权 (RBAC) 模块。
深入研究代码会发现一个结构映射了从 Authentication 模块收集的所有详细信息。
type User struct { name string // unique for each user ... // other fields }
请注意,User用于集群外的人员或应用。
如果要识别集群中的应用,则应改用服务帐户。
该帐户与普通用户非常相似,但不同之处在于Kubernetes管理它。
服务帐户通常分配给 pod 以授予权限。
例如,我们可以让以下应用程序从集群内部访问资源:
· cilium-agent必须列出特定节点上的所有 pod 资源· ingress nginx控制器必须列出服务的所有后端端点
对于这些应用,我们可以定义一个 ServiceAccount (SA)。
由于服务帐户是在集群中管理的,因此我们可以使用 YAML 创建它们:
apiVersion: v1 kind: ServiceAccount metadata: name: sa:app1 # arbitrary but unique string namespace: demo-namespace
为了方便 Kubernetes 管理,我们还可以定义一组User 或ServiceAccount。
通过这样,我可以很方便的引用特定namespace中的所有 ServiceAccount。
现在我们已经定义了如何访问资源,是时候讨论权限了。
此时,我们有一种机制来确定谁可以访问资源。
它可能是一个人、一个robot账户或一群人。
但是他们在集群中访问什么资源呢?
对资源的访问进行建模
在Kubernetes中,我们感兴趣的是控制对资源的访问,比如Pod、Services、Endpoints等。
这些资源通常存储在数据库 (etcd) 中,并通过内置 API 访问,例如:
/api/v1/namespaces/{namespace}/pods/{name} /api/v1/namespaces/{namespace}/pods/{name}/log /api/v1/namespaces/{namespace}/serviceaccounts/{name}
限制对这些资源的访问的最佳方法是控制这些 API 端点的请求方式。
为此,我们将需要两件事:
1、资源的 API 端点
2、授予访问资源的权限类型(例如只读、读写等)
对于权限,我们将使用verbs,例如get, list, create, patch, delete 等。
想象一下,你想要get, list 以及 watchPods、logs和Services等资源。
我们可以将这些资源和权限组合在一个列表中,如下所示:
resources: - /api/v1/namespaces/{namespace}/pods/{name} - /api/v1/namespaces/{namespace}/pods/{name}/log - /api/v1/namespaces/{namespace}/serviceaccounts/{name} verbs: - get - list - watch
根据以下信息,我们可以更简化定义上述信息:
· 基本 URL/api/v1/namespaces/对所有人都是通用的。也许我们可以省略它
· 我们可以假设所有资源都在当前namespace中并删除{namespace}路径
最终我们可以简化为:
resources: - pods - pods/logs - serviceaccounts verbs: - get - list - watch
该定义更人性化,我们可以很清晰的了解相关权限信息。
不过,权限配置不仅仅只有上面的内容,还有更多的内容。
除了 pod、endpoint、service等内置对象的 API 外,Kubernetes 还支持 API 扩展。
例如,当使用安装 Cilium CNI 时,脚本会创建一个CiliumEndpoint自定义资源 (CR):
apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: ciliumendpoints.cilium.io spec: group: cilium.io names: kind: CiliumEndpoint scope: Namespaced # truncated...
这些对象存储在集群中,可通过 kubectl 获得:
$ kubectl get ciliumendpoints.cilium.io -n demo-namespace NAME ENDPOINT ID IDENTITY ENDPOINT STATE IPV4 IPV6 app1 2773 1628124 ready 10.6.7.54 app2 3568 1624494 ready 10.6.7.94 app3 3934 1575701 ready 10.6.4.24 $
可以通过 Kubernetes API 类似地访问自定义资源:
/apis/cilium.io/v2/namespaces/{namespace}/ciliumendpoints /apis/cilium.io/v2/namespaces/{namespace}/ciliumendpoints/{name}
如果要将它们映射到 YAML 文件中,可以编写以下内容:
resources: - ciliumnetworkpolicies - ciliumnetworkpolicies/status verbs: - get
但是,Kubernetes 怎么知道资源是自定义的呢?
它如何区分使用自定义资源和内置的 API?
不幸的是,从 API endpoint删除 URL 并不是一个好主意。
我们可以通过稍作更改来恢复它。
我们可以在顶部定义它并稍后使用它来扩展资源的 URL。
apiGroups: - cilium.io # APIGroup name resources: - ciliumnetworkpolicies - ciliumnetworkpolicies/status verbs: - get
对于没有namespace API的POD之类的资源呢?
Kubernetes “”空API组是一个特殊的组,它引用内置对象。因此,前面的定义应该扩展到:
apiGroups: - '' # Built-in objects resources: - pods - pods/logs - serviceaccounts verbs: - get - list - watch
Kubernetes 读取 API 组并自动将它们扩展为:
· 如果为空,则将“”转换为/api/v1/xxx
· 否则为/apis/{apigroup_name}/{apigroup_version}/xxx
既然我们知道了如何映射资源和权限,现在终于到了将对多个资源的访问组合在一起的时候了。在Kubernetes中,resources和verbs的集合称为rules,您可以将规则分组到列表中:
rules: - rule 1 - rule 2
每条规则都包含我们上述所提到的apiGroups,resources和verbs:
rules: # Authorization rules - apiGroups: # 1st API group - '' # An empty string designates the core API group. resources: - pods - pods/logs - serviceaccounts verbs: - get - list - watch - apiGroups: # another API group - cilium.io # Custom APIGroup resources: - ciliumnetworkpolicies - ciliumnetworkpolicies/status verbs: - get
规则的集合在 Kubernetes 中具有特定的名称,称为角色。
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: viewer rules: # Authorization rules - apiGroups: # 1st API group - '' # An empty string designates the core API group. resources: - pods - pods/logs - serviceaccounts verbs: - get - list - watch - apiGroups: # another API group - cilium.io # Custom APIGroup resources: - ciliumnetworkpolicies - ciliumnetworkpolicies/status verbs: - get
到目前为止,我们定义了:
· 具有用户、服务帐户和组的身份。
· 对具有角色的资源的权限。
最后,缺少的部分是将两者联系起来。
向用户授予权限
RoleBinding 将角色中定义的权限授予用户、服务帐户或组。
让我们看一个例子:
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: role-binding-for-app1 roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: viewer subjects: - kind: ServiceAccount name: sa-for-app1 namespace: kube-system
该定义有两个重要字段:
· 引用viewer角色的roleRef
· 关联到sa-for-app1服务帐户的subjects
将资源提交到集群后,使用服务帐户的应用程序或用户将有权访问角色中列出的资源。
如果我们删除绑定,应用程序或用户将失去对这些资源的访问权限(但该角色将随时准备被其他绑定使用)。
请注意该subjects字段是一个包含kind,namespace和name的列表。
该kind属性是从服务帐户和组中识别用户所必需的。
但是namespace了?
将集群拆分为namespace,并将对namespace资源的访问限制为特定帐户,这通常很有帮助。
在大多数情况下,Role和RoleBinding与namespace关联,并授予对特定namespace的访问权。
然而,这两种资源是可以混合使用的——稍后我们将描述该如何混合。
在我们总结理论并从实践开始之前,让我们看一下subjects的几个例子:
subjects: - kind: Group name: system:serviceaccounts apiGroup: rbac.authorization.k8s.io # when the namespace field is not specified, this targets all service accounts in all namespace
我们还可以将多个组、用户或服务帐户作为subjects:
subjects: - kind: Group name: system:authenticated # for all authenticated users apiGroup: rbac.authorization.k8s.io - kind: Group name: system:unauthenticated # for all unauthenticated users apiGroup: rbac.authorization.k8s.io
回顾一下到目前为止所学的内容,让我们看看如何授予应用程序访问某些自定义资源的权限。
首先,让我们介绍一下场景:你有一个应用程序需要访问Cilium暴露的资源。
- 想象一下,在集群中部署了一个需要通过 API 访问自定义资源的应用程序
- 如果不授予对这些API的访问权限,请求将失败,并显示403禁止的错误消息
如何授予访问这些资源的权限?
使用服务帐户、角色和角色绑定。
- 首先,我们应该为我们的工作负载创建一个身份。在 Kubernetes 中,这意味着创建一个服务帐户。
- 然后,我们需要定义权限并将其包含到角色中
- 最后,我们希望通过RoleBinding将身份(服务帐户)关联到权限(角色)
- 下次应用程序向Kubernetes API发出请求时,它将被授予访问Cilium资源的权限
Namespaces和cluster-wide的资源
当我们讨论资源时,您了解到endpoint的结构如下面一样:
/api/v1/namespaces/{namespace}/pods/{name} /api/v1/namespaces/{namespace}/pods/{name}/log /api/v1/namespaces/{namespace}/serviceaccounts/{name}
但是没有namespace的资源,比如持久卷和节点呢?
namespace资源只能在namespace内创建,并且该namespace的名称包含在 HTTP 路径中。
如果资源是全局的,比如节点,namespace名称就不会出现在 HTTP 路径中。
/api/v1/nodes/{name} /api/v1/persistentvolume/{name}
我们可以将它们添加到角色中吗?
是的,我们可以添加。
毕竟,在引入 Roles 和 RoleBindings 时,我们没有讨论任何namespace的限制。
下面是一个例子:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: viewer rules: # Authorization rules - apiGroups: # 1st API group - '' # An empty string designates the core API group. resources: - persistentvolumes - nodes verbs: - get - list - watch
但是,如果我们尝试提交该配置并将其关联到服务帐户,大家可能会意识到它不起作用。
持久卷和节点是集群范围的资源。
但是,角色可以授予对namespace范围内资源的访问权限。
如果我们想使用适用于整个集群的 Role,我们可以使用 ClusterRole(以及相应 ClusterRoleBinding的为它分配subject)。
之前的配置应改为:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: viewer rules: # Authorization rules - apiGroups: # 1st API group - '' # An empty string designates the core API group. resources: - persistentvolumes - nodes verbs: - get - list - watch
请注意,唯一的变化是kind属性,而其他一切都保持不变。
我们可以使用 ClusterRoles 授予对所有资源的权限——例如,集群中的所有 Pod。
此功能不限于集群范围的资源。
Kubernetes 已经附带了一些角色和集群角色。
让我们来看一下。
$ kubectl get roles -n kube-system | grep "^system:" NAME system::leader-locking-kube-controller-manager system::leader-locking-kube-scheduler system:controller:bootstrap-signer system:controller:cloud-provider system:controller:token-cleaner # truncated output...
system:前缀表示资源由集群控制平面直接管理。
此外,所有默认的 ClusterRoles 和 ClusterRoleBindings 都标有kubernetes.io/bootstrapping=rbac-defaults。
让我们也列出 ClusterRoles:
$ kubectl get clusterroles -n kube-system | grep "^system:" NAME system:aggregate-to-admin system:aggregate-to-edit system:aggregate-to-view system:discovery system:kube-apiserver system:kube-controller-manager system:kube-dns system:kube-scheduler # truncated output...
我们可以使用以下命令检查每个 Role 和 ClusterRole 的详细信息:
$ kubectl get role <role> -n kube-system -o yaml # or $ kubectl get clusterrole <clusterrole> -n kube-system -o yaml
至此,我们了解了 Kubernetes RBAC 的基本内容。
通过上述内容,我们学习到了如下内容:
1、如何使用用户、服务帐户和组创建身份
2、如何为具有角色的namespace中的资源分配权限
3、如何使用 ClusterRole 为集群资源分配权限
4、如何将 Roles 和 ClusterRoles 关联到subject
最后让我们再研究一下几个案例:RBAC的几个不寻常的边缘案例。
理解 Roles、RoleBindings、ClusterRoles 和 ClusterBindings
在较高级别上,Roles 和 RoleBindings集群内部关联,并授予对特定namespace的访问权限,而ClusterRoles和ClusterRoleBinding不属于namespace,并授予对整个集群的访问权限。
但是,可以混合使用这两种类型的资源。
例如,当 RoleBinding 将帐户关联到 ClusterRole 时会发生什么?
接下来让我们通过一些实践来探索这个问题。
让我们使用 minikube 创建一个本地集群:
$ minikube start minikube v1.24.0 ✨ Automatically selected the docker driver Starting control plane node in cluster Pulling base image ... Creating docker container (CPUs=2, Memory=4096MB) ... Preparing Kubernetes v1.22.3 on Docker 20.10.8 ... ▪ Generating certificates and keys ... ▪ Booting up control plane ... ▪ Configuring RBAC rules ... Verifying Kubernetes components... ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5 Enabled addons: storage-provisioner, default-storageclass Done! kubectl is now configured to use the cluster and "default" namespace by default
首先,创建四个namespace:
$ kubectl create namespace test namespace/test created $ kubectl create namespace test2 namespace/test2 created $ kubectl create namespace test3 namespace/test3 created $ kubectl create namespace test4 namespace/test4 created
最后,在namespace中创建一个服务帐户test:
apiVersion: v1 kind: ServiceAccount metadata: name: myaccount namespace: test
我们可以通过以下方式提交资源:
$ kubectl apply -f service-account.yaml serviceaccount/myaccount created
此时,我们的集群应如下所示:
场景 1:Role和RoleBinding在同一个namespace
让我们从创建 Role 和 RoleBinding 来授予服务帐户对test namespace的访问权限开始:
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: testadmin namespace: test rules: - apiGroups: ['*'] resources: ['*'] verbs: ['*'] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: testadminbinding namespace: test subjects: - kind: ServiceAccount name: myaccount namespace: test roleRef: kind: Role name: testadmin apiGroup: rbac.authorization.k8s.io
我们可以通过以下方式提交配置:
$ kubectl apply -f scenario1.yaml role.rbac.authorization.k8s.io/testadmin createdrolebinding.rbac.authorization.k8s.io/testadminbinding created
我们的集群如下所示:
所有资源(Service Account, Role以及 RoleBinding)都在namespace中。
Role 授予对所有资源的访问权限,并且 RoleBinding 将 Service Account 和 Role 关联起来。
我们如何测试服务帐户是否可以访问资源?
我们可以结合 kubectl 的两个功能:
1、使用kubectl–as=jenkins进行用户模拟访问(https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation)
2、使用kubectl auth can-i验证API访问(https://kubernetes.io/docs/reference/access-authn-authz/authorization/#checking-api-access)
请注意,我们的用户应该拥有impersonateverb作为允许此操作的权限。
要作为myaccount服务帐户发出请求,并检查是否可以在namespace中列出Pod,可以使用以下命令:
$ kubectl auth can-i get pods -n test --as=system:serviceaccount:test:myaccount yes
让我们详解一下命令:
· auth can-i需要查询授权模型(RBAC)
· get pods是verb和resource
· -n test是我们要发出命令的namespace
· –as=system:serviceaccount:test:myaccount用于模拟myaccount服务帐户
请注意–as=标志需要一些额外的提示来识别服务帐户。
整个部分可以分解为:
--as=system:serviceaccount:{namespace}:{service-account-name} ^^^^^^^^^^^^^^^^^^^^^ This should always be included for Service Accounts.
通过这个 Role+ServiceAccount+RoleBindings组合,我们可以访问test namespace中的所有资源。
让我们往下继续看一个更复杂的例子。
场景 2:不同namespace中的 Role 和 RoleBinding
让我们在namespace中创建一个新的 Role 和 RoleBinding test2。
注意RoleBinding如何关联test2中的角色和test中的服务帐户:
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: test2 name: testadmin rules: - apiGroups: ['*'] resources: ['*'] verbs: ['*'] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: testadminbinding namespace: test2 subjects: - kind: ServiceAccount name: myaccount namespace: test roleRef: kind: Role name: testadmin apiGroup: rbac.authorization.k8s.io
我们可以通过以下方式提交资源:
$ kubectl apply -f scenario2.yaml role.rbac.authorization.k8s.io/testadmin created rolebinding.rbac.authorization.k8s.io/testadminbinding created
我们的集群如下所示:
让我们测试位于test中的服务帐户是否可以访问test2中的资源:
$ kubectl auth can-i get pods -n test2 --as=system:serviceaccount:test:myaccount yes
我们成功授予服务帐户访问它创建的namespace之外的资源。
值得注意的是,RoleBinding中的roleRef(https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#roleref-v1-rbac-authorization-k8s-io) 属性没有namespace字段。
kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: testadminbinding namespace: test2 subjects: - kind: ServiceAccount name: myaccount namespace: test roleRef: kind: Role name: testadmin apiGroup: rbac.authorization.k8s.io
这意味着 RoleBinding 只能引用同一namespace中的 Role。
场景 3:使用有 RoleBinding 的 ClusterRole
如前所述,ClusterRoles 不属于namespace。
这意味着 ClusterRole 不会将权限范围限定为单个namespace。
但是,当 ClusterRole 通过 RoleBinding 关联到服务帐户时,ClusterRole 权限仅适用于创建 RoleBinding 的namespace。
让我们看一个例子。
在namespace中创建 RoleBindingtest3并将服务帐户关联到 ClusterRole cluster-admin:
cluster-admin是 Kubernetes 中内置的 ClusterRoles 之一。
kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: testadminbinding namespace: test3 subjects: - kind: ServiceAccount name: myaccount namespace: test roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io
我们可以通过以下方式提交资源:
$ kubectl apply -f scenario3.yaml rolebinding.rbac.authorization.k8s.io/testadminbinding created
我们的集群如下所示:
让我们测试位于test中的服务帐户是否可以访问test3中的资源:
$ kubectl auth can-i get pods -n test3 --as=system:serviceaccount:test:myaccount yes
但它无权访问其他namespace:
$ kubectl auth can-i get pods -n test4 --as=system:serviceaccount:test:myaccount no $ kubectl auth can-i get pods --as=system:serviceaccount:test:myaccount no
在这种情况下,当我们使用 RoleBindings 将服务帐户关联到 ClusterRole 时,ClusterRole 的行为就像是常规角色一样。
它仅向 RoleBinding 所在的当前namespace授予权限。
场景 4:使用 ClusterRole 和 ClusterRoleBinding 授予集群范围的访问权限
在最后一个场景中,我们将创建一个 ClusterRoleBinding 以将 ClusterRole 关联到服务帐户:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: testadminclusterbinding subjects: - kind: ServiceAccount name: myaccount namespace: test roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io
请注意roleRef缺少namespace字段。
这意味着 ClusterRoleBinding 无法识别要关联的角色,因为角色属于namespace,并且 ClusterRoleBindings(连同它们引用的 ClusterRolnamespace有namespace。
我们可以通过以下方式提交资源:
$ kubectl apply -f scenario3.yaml rolebinding.rbac.authorization.k8s.io/testadminbinding created
我们的集群如下所示:
尽管 ClusterRole 和 ClusterRole 都没有定义任何namespace,但服务帐户现在可以访问所有内容:
$ kubectl auth can-i get pods -n test4 --as=system:serviceaccount:test:myaccount yes $ kubectl auth can-i get namespaces --as=system:serviceaccount:test:myaccount Warning: resource 'namespaces' is not namespace scoped yes
从这些示例中,我们可以观察到 RBAC 资源的一些行为和限制:
· Roles 和 RoleBindings 必须存在于同一个namespace中
· RoleBindings 可以存在于服务帐户的单独namespace中
· RoleBindings 可以关联 ClusterRoles,但它们只授予对 RoleBinding 所在 namespace的访问权限
· ClusterRoleBindings 将帐户关联到 ClusterRoles 并授予对所有资源的访问权限
· ClusterRoleBindings 不能引用角色
这里最有趣的意思可能是,当RoleBinding引用时,ClusterRole可以定义在单个namespace中表示的公共权限。这样就不需要在namespace中有重复的角色。
附加 #1:使 RBAC 策略更简洁
Role 或 ClusterRole的典型rules部分如下所示:
rules: - apiGroups: -" resources: - pods - endpoints - namespaces verbs: - get - watch - list - create - delete
但是,可以使用以下格式重写上述配置:
- apiGroups: [''] resources: ['services', 'endpoints', 'namespaces'] verbs: ['get', 'list', 'watch', 'create', 'delete']
这种方式明显减少了行数并且更简洁。
但是,当我们从数据库中检索内容时,Kubernetes 仍将内容作为 YAML 列表进行管理。
所以每次拿到 Role 时,数组都会被渲染成一个列表:
$ kubectl get role pod-reader -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role rules: - apiGroups: -"" resources: - pods # truncated output...
附加 #2:使用服务帐户创建 Kubernetes 帐户
服务帐户通常由 API 服务器自动创建,并与集群中运行的 pod 相关联。
三个独立的组件完成了这项任务:
1、在 Pod 定义中注入 Service Account 属性的ServiceAccount准入控制器
2、一个创建伴随 Secret 对象的Token 控制器
3、ServiceAccount 控制器在每个namespace中创建默认服务帐户
服务帐户可以在集群外部使用,为希望与 Kubernetes API 对话的用户或长期存在的作业创建身份。
要手动创建服务帐户,我们可以使用以下命令:
$ kubectl create serviceaccount demo-sa serviceaccount/demo-sa created $ kubectl get serviceaccounts demo-sa -o yaml apiVersion: v1 kind: ServiceAccount metadata: name: demo-sa namespace: default resourceVersion: "1985126654" selfLink: /api/v1/namespaces/default/serviceaccounts/demo-sa uid: 01b2a3f9-a373-6e74-b3ae-d89f6c0e321f secrets: - name: demo-sa-token-hrfq2
我们可能会注意到secrets服务帐户 YAML 定义末尾的一个字段。
那是什么?
每次创建服务帐户时,Kubernetes 都会创建一个 Secret。
Secret 保存服务帐户的令牌,我们可以使用该令牌调用 Kubernetes API。
它还包括 API 服务器的公共证书颁发机构 (CA):
$ kubectl get secret demo-sa-token-hrfq2 -o yaml apiVersion: v1 data: ca.crt: (APISERVER'S CA BASE64 ENCODED) namespace: ZGVmYXVsdA== token: (BEARER TOKEN BASE64 ENCODED) kind: Secret metadata: # truncated output ... type: kubernetes.io/service-account-token
令牌是一个签名的JWT,可以用作承载令牌,以针对kube-apiserver服务进行身份验证。通常,这些secret被挂载到POD中,用于访问API服务,但可以从集群外部使用。
附加 #3:Kubernetes 默认的角色
有一些角色是kubernetes内置的一些默认角色,我们无需进行修改。
默认的角色:
核心组件角色: (https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/#core-component-roles)
其他组件角色:
(https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/#other-component-roles)
内置控制器的角色
(https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/#controller-roles)
Kubernetes 控制器管理器 运行内建于 Kubernetes 控制面的控制器。当使用 –use-service-account-credentials 参数启动时, kube-controller-manager 使用单独的服务账户来启动每个控制器。每个内置控制器都有相应的、前缀为 system:controller: 的角色。如果控制管理器启动时未设置 –use-service-account-credentials, 它使用自己的身份凭据来运行所有的控制器,该身份必须被授予所有相关的角色。这些角色包括:
· system:controller:attachdetach-controller
· system:controller:certificate-controller
· system:controller:clusterrole-aggregation-controller
· system:controller:cronjob-controller
· system:controller:daemon-set-controller
· system:controller:deployment-controller
· system:controller:disruption-controller
· system:controller:endpoint-controller
· system:controller:expand-controller
· system:controller:generic-garbage-collector
· system:controller:horizontal-pod-autoscaler
· system:controller:job-controller
· system:controller:namespace-controller
· system:controller:node-controller
· system:controller:persistent-volume-binder
· system:controller:pod-garbage-collector
· system:controller:pv-protection-controller
· system:controller:pvc-protection-controller
· system:controller:replicaset-controller
· system:controller:replication-controller
· system:controller:resourcequota-controller
· system:controller:root-ca-cert-publisher
· system:controller:route-controller
· system:controller:service-account-controller
· system:controller:service-controller
· system:controller:statefulset-controller
· system:controller:ttl-controller
附加 #4:初始化与预防权限升级
RBAC API 会阻止用户通过编辑角色或者角色绑定来提升权限。由于这一点是在 API 级别实现的,所以在 RBAC 鉴权组件未启用的状态下依然可以正常工作。
对角色创建或更新的限制
(https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/#%E5%AF%B9%E8%A7%92%E8%89%B2%E5%88%9B%E5%BB%BA%E6%88%96%E6%9B%B4%E6%96%B0%E7%9A%84%E9%99%90%E5%88%B6)
只有在符合下列条件之一的情况下,你才能创建/更新角色:
1、你已经拥有角色中包含的所有权限,且其作用域与正被修改的对象作用域相同。(对 ClusterRole 而言意味着集群范围,对 Role 而言意味着相同名字空间或者集群范围)。
2、你被显式授权在 rbac.authorization.k8s.io API 组中的 roles 或 clusterroles 资源 使用 escalate 动词。
例如,如果 user-1 没有列举集群范围所有 Secret 的权限,他将不能创建包含该权限的 ClusterRole。若要允许用户创建/更新角色:
1、根据需要赋予他们一个角色,允许他们根据需要创建/更新 Role 或者 ClusterRole 对象。
2、授予他们在所创建/更新角色中包含特殊权限的权限:
- · 隐式地为他们授权(如果它们试图创建或者更改 Role 或 ClusterRole 的权限, 但自身没有被授予相应权限,API 请求将被禁止)。· 通过允许他们在 Role 或 ClusterRole 资源上执行 escalate 动作显式完成授权。这里的 roles 和 clusterroles 资源包含在 rbac.authorization.k8s.io API 组中。
对角色绑定创建或更新的限制
(https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/#%E5%AF%B9%E8%A7%92%E8%89%B2%E7%BB%91%E5%AE%9A%E5%88%9B%E5%BB%BA%E6%88%96%E6%9B%B4%E6%96%B0%E7%9A%84%E9%99%90%E5%88%B6)
只有你已经具有了所引用的角色中包含的全部权限时,或者你被授权在所引用的角色上执行 bind 动词时,你才可以创建或更新角色绑定。这里的权限与角色绑定的作用域相同。例如,如果用户 user-1 没有列举集群范围所有 Secret 的能力,则他不可以创建 ClusterRoleBinding 引用授予该许可权限的角色。如要允许用户创建或更新角色绑定:
1、赋予他们一个角色,使得他们能够根据需要创建或更新 RoleBinding 或 ClusterRoleBinding 对象。
2、授予他们绑定某特定角色所需要的许可权限:
- · 隐式授权下,可以将角色中包含的许可权限授予他们;· 显式授权下,可以授权他们在特定 Role (或 ClusterRole)上执行 bind 动词的权限。
例如,下面的 ClusterRole 和 RoleBinding 将允许用户 user-1 把名字空间 user-1-namespace 中的 admin、edit 和 view 角色赋予其他用户:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: role-grantor rules: - apiGroups: ["rbac.authorization.k8s.io"] resources: ["rolebindings"] verbs: ["create"] - apiGroups: ["rbac.authorization.k8s.io"] resources: ["clusterroles"] verbs: ["bind"] # 忽略 resourceNames 意味着允许绑定任何 ClusterRole resourceNames: ["admin","edit","view"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: role-grantor-binding namespace: user-1-namespace roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: role-grantor subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: user-1
当启动引导第一个角色和角色绑定时,需要为初始用户授予他们尚未拥有的权限。对初始角色和角色绑定进行初始化时需要:
· 使用用户组为 system:masters 的凭据,该用户组由默认绑定关联到 cluster-admin 这个超级用户角色。
· 如果你的 API 服务器启动时启用了不安全端口(使用 –insecure-port), 你也可以通过 该端口调用 API ,这样的操作会绕过身份验证或鉴权。
概括
Kubernetes 中的 RBAC 是一种机制,使我们能够配置细粒度的特定权限集,这些权限集定义给定用户或用户组如何与集群或特定集群namespace中的 Kubernetes 对象进行交互。
在本文中,我们了解到:
· RBAC 如何通过更灵活的模型将权限与用户分离
· RBAC 如何与 Kubernetes API 集成
· 如何使用用户、服务帐户和组识别 RBAC 的subject
· 如何使用verbs和 API 组将资源映射到规则中
· 如何将规则分组到角色中并使用 RoleBindings 将这些角色关联到身份
· Roles、RoleBindings、ClusterRoles 和 ClusterRole Bindings 之间的关系
参考:
- https://cloud.google.com/kubernetes-engine/docs/how-to/role-based-access-control?hl=zh-cn
- https://zh.m.wikipedia.org/zh/%E4%BB%A5%E8%A7%92%E8%89%B2%E7%82%BA%E5%9F%BA%E7%A4%8E%E7%9A%84%E5%AD%98%E5%8F%96%E6%8E%A7%E5%88%B6
原文:https://learnk8s.io/rbac-kubernetes