目录

Kubernetes - RBAC 与 ServiceAccount

1 概述

Kubernetes 认证与鉴权机制 中提到,Kubernetes 支持的授权机制有多种,其中 RBAC 是最常用的授权方式。RBAC 基于角色访问控制,全称 Role-Base Access Control

对于理解 RBAC,首先需要知道三个关键的概念:

  • Role :角色,代表一组对 Kubernetes API 对象操作的权限。

  • Subject :被作用者,包括 User、Group、ServiceAccount。

  • RoleBinding :定义 Role 与 Subject 的映射关系。

因此,我们会预先创建一些 Role,然后创建 Subject 时,定义 RoleBinding 来表明对 Subject 的权限控制。

/posts/cloud/cloud_native/kubernetes/k8s_learning/rbac/img1.png
为什么这么设计?
通过 RoleBinding,实现了 Role Subject 之间的解耦。

2 Role 与 ClusterRole

2.1 Role

Role 就是一个 Kubernetes 资源对象,并且是一个 namespaced Resouce,因此其 Role 控制的权限范围也只能是所属的 namespace 下。

基本定义如下:

1
2
3
4
5
6
7
8
9
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
  • metadata.namespace :指定 Role 所属的 namespace
  • rules.apiGroups :表明针对哪些 API Group 生效,为空表示 core API Group;
  • rules.resources :表明针对哪些 Resource 生效;
  • rules.verbs :允许执行的操作;

示例中 rules 定义的规则就是:允许对 “mynamespace” 下的所有 Pod 对象,执行 GET、Watch、List 操作。

能够支持限制的操作为:“get”, “list”, “watch”, “create”, “update”, “patch”, “delete”,“deletecollection” 。

2.2 ClusterRole

ClusterRole 是一个 non-namespaced Resource,所以是针对所有 namespace 的资源生效,也可以实现控制 non-namespaced Resource 的权限。

基本定义如下:

1
2
3
4
5
6
7
8
9
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  # "namespace" omitted since ClusterRoles are not namespaced
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]
  • rules.apiGroups :表明针对哪些 API Group 生效,为空表示 core API Group;
  • rules.resources :表明针对哪些 Resource 生效;
  • rules.verbs :允许执行的操作;

示例中 rules 定义的规则是:允许对所有 namespace 下的 所有 Secret 对象,执行 GET、Watch、List 操作。

2.2.1 聚合 ClusterRole

多个 ClusterRole 可以聚合为一个新的 ClusterRole,用于简化 ClusterRole 的管理工作。

1
2
3
4
5
6
7
8
9
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # rules 规则会自动被设置

aggregationRule 字段设置了匹配 ClusterRole 的规则,当一个 ClusterRole 被匹配到后,其 rules 自动会填充相关的规则。

2.3 对非资源对象限制

某些 Kubernetes API 包含下次子资源,例如 Pod 的日志。

1
2
3
4
5
# ...
rules:
- apiGroups: [""]
  resource: ["pods", "pods/log"]
  verbs: ["get", "list"]

2.4 对指定资源对象限制

通过 rules.resourceName 字段可以限制对指定资源的权限:

1
2
3
4
5
6
# ...
rules:
- apiGroups: [""]
  resource: ["configmap"]
  resourceName: ["mu-configmap"]
  verbs: ["update", "get"]

当然,因为是对指定的资源,因此无法限制 list、watch、create 或 deletecollections 操作。

2.5 内置 Role 与 ClusterRole

默认下,Kubernetes 已经内置了许多个 Role 与 ClusterRole,它们都是以 system: 开头命名。

所有系统默认的 ClusterRole 和 RoleBinding 都会使用 label kubernetes.io/bootstrappiong=rbac-defaults 来标记。

每次集群启动时,APIServer 会更新默认的集群 Role 缺失的权限,也会更新默认 Role 绑定的 Subject。这样可以防止一些破坏性的更改,保证集群升级情况下相关内容能够及时更新。

使用内置 Role 与 ClusterRole 已经完全可以满足大部分的使用场景。

内置的 Role 的含义见 Default roles and role bindings

3 RoleBinding 与 ClusterRoleBinding

3.1 RoleBinding

RoleBinding 用于绑定一个 Role 与多个 Subject,通过 RoleBinding 才能让某个 Subject 有 namespace 相关资源的访问权限。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# This role binding allows "jane" to read pods in the "default" namespace.
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: jane
  apiGroup: rbac.authorization.k8s.io
- kind: ServiceAccount
    name: default
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io
Tip
RoleBinding 也可以引用 ClusterRole,将其权限限制在了 RoleBinding 所属的 namespace 下。

3.2 ClusterRoleBinding

ClusterRoleBinding 让 Subject 有着整个集群相关资源的访问权限。

3.2.1 ClusterRole 聚合

在 ClusterRole 中可以通过 aggregationRule 来与其他 ClusterRole,也就是可以自动组合多个 ClusterRole 为一个。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: monitoring
aggregationRule:  # 通过 label selector 方式自动组合
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # Rules 由组合起来的 ClusterRole 自动填充
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: monitoring-endpoints
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
# These rules will be added to the "monitoring" role.
rules:
- apiGroups: [""]
  resources: ["services", "endpoints", "pods"]
  verbs: ["get", "list", "watch"]

4 Subject

Subject 表明请求者的身份,包括:User、User Group 和 ServiceAccount。

不过,Kubernetes 并不包含一个用户系统,我们并不可以创建或删除用户或者用户组。User 仅仅会在权限检查时使用:请求提供 User 信息,鉴权模块根据 User 判断是否有对应操作的权限。

4.1 User 与 User Group

前面提到,User 只是一个逻辑上的标识。不需要任何的注册与登陆,我们就可以基于一个 User 来设置相关的权限。

下面以设置 User 的权限为示例,为 shiori 用户仅提供 Pod 相关的权限:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: pod-admin
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["*"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: shiori-pod-admin
subjects:
- kind: User
  name: shiori
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: pod-admin
  apiGroup: rbac.authorization.k8s.io

创建后,我们会发现已经可以以 shiori 用户来执行了,并且限制了他的权限:

1
2
3
4
5
$ kubectl get nodes --as=shiori
Error from server (Forbidden): nodes is forbidden: User "shiori" cannot list resource "nodes" in API group "" at the cluster scope

$ kubectl get pods --as=shiori
No resources found in default namespace.

User Group 是 User 的集合,以 group:group 的格式配置,User Group 用于更加方便的管理多个 User 的权限。

Kubernetes 默认创建的 RoleBinding/ClusterRoleBinding 一般会设置几个 User Group 的权限。例如,system:unauthenticated 表示 匿名用户

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:public-info-viewer
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:public-info-viewer
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:authenticated
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:unauthenticated
ServiceAccount
下面会说到,ServiceAccount 也是一种特殊的用户。

4.2 ServiceAccount

ServiceAccount 本质上也是一种特殊的 User,对应的 User 命为 system:serviceaccount:${NAMESPACE}:${SERVICE_ACCOUNT}。可以看到,所有的 ServiceAccount 是属于 system:serviceaccount User Group 的。

Kubernetes 为 ServiceAccount 提供了专门的认证方式 ServiceAccount Token,并且设置权限起来非常方便。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 创建 ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: crossplane-provider
  namespace: infra
---
# ClusterRoleBinding 绑定 ServiceAccount 与 Role
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: crossplane-provider
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: crossplane-provider
subjects:
- kind: ServiceAccount
  name: crossplane-provider
  namespace: infra
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: crossplane-provider
rules:
- apiGroups:
  - "*"
  resources:
  - "*"
  verbs:
  - "*"

ServiceAccount 本质上还是基于 Token 的方式来进行身份认证,创建一个 ServiceAccount 会对应自动创建相关 Secret。Secret 中记录了对应的 JWT Token。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: v1
kind: Secret
metadata:
  annotations:
    kubernetes.io/service-account.name: crossplane-provider
    kubernetes.io/service-account.uid: b8d7500c-66cd-491c-bba3-ff00ef5e1ccf
  name: crossplane-provider-token-x6x7j
  namespace: infra
type: kubernetes.io/service-account-token
data:
  ca.crt: <OMIT>
  namespace: aW5mcmE=
  # JWT base64 encode
  token: <OMIT>

使用 JWT 记录了相关的用户信息,从而使得 Kubernetes 能够知道该 Token 对应于哪个 ServiceAccount,进一步通过 RBAC 进行权限判断。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// JWT Header
{
  "alg": "RS256",
  "kid": "hdxo5HvF6FihotWJ9Mf1uu8bgLAwa37nOLseffIvg6w"
}
// JWT PAYLOAD
{
  "iss": "kubernetes/serviceaccount",
  "kubernetes.io/serviceaccount/namespace": "infra",
  "kubernetes.io/serviceaccount/secret.name": "crossplane-provider-token-x6x7j",
  "kubernetes.io/serviceaccount/service-account.name": "crossplane-provider",
  "kubernetes.io/serviceaccount/service-account.uid": "b8d7500c-66cd-491c-bba3-ff00ef5e1ccf",
  "sub": "system:serviceaccount:infra:crossplane-provider"
}

参考