访问控制:基于角色的权限控制

todo

一、RBAC是什么

RBAC,是Role-Based Access Control的缩写,即基于角色的访问控制。在Kubernetes项目中,负责完成认证和授权工作的就是RBAC,它主要由三个最基本的内容组成:

  • Subject:使用者,即可以是“人”,也可以是“机器”,还可以是我们在Kubernetes中定义的“用户”。
  • Role:角色,它就是一组权限规则,定义了一组可以对Kubernetes API对象进行操作的权限。
  • RoleBinding:角色绑定,它定义了“使用者”和“角色”的绑定关系。

二、如何使用RBAC

2.1、定义Role

在Kubernetes项目中,Role就是一个API对象,它的定义如下:

1
2
3
4
5
6
7
8
9
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: mynamespace
name: my-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch","list"]

首先,我们在名为mynamespace的Namespace下,定义了一个名为my-role的Role对象。

然后,我们通过rules字段为my-role这个Role对象定义了权限规则。如上,这条规则的含义是:允许“使用者”,对mynamespace下面的Pod对象,进行get、watch和list操作。

2.2、定义RoleBinding

在有了Role后,我们就可以将Role绑定到使用者上了,也即进行RoleBinding。在kubernetes项目中,RoleBinding也是一个API对象,它的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: mynamespace
name: my-rolebinding
subjects:
- kind: User
name: my-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: my-role
apiGroup: rbac.authorization.k8s.io

首先,我们通过subjects字段声明了“使用者”的信息,它的类型是User,它的名字是my-user。

然后,通过roleRef字段,我们直接引用了之前创建的Role对象。

这样,我们就完成了role和"使用者"的绑定关系,是不是很简单。

三、理解Subject

在上面的例子中,我们是不是会有这样的困惑:

在Kubernets中,并没有User类型的API对象。而且,我们也没有创建过User,那这个User到底是从哪里来的呢?

其实,Kubernetes中的“User”,就是“用户”,它只是授权系统中的一个逻辑概念。它需要通过外部认证服务(比如,Keystone)来提供。此外,我们还可以直接给API Server指定一个用户名、密码文件。这样,Kubernetes的授权系统,就可以从这个文件中找到对应的“用户”了。

在大多数时候,我们其实不会使用“用户”这个功能。在私有环境中,我们只要使用Kubernetes提供的内置“用户”,就足够了。而这个内置用户,就是ServiceAccount。

3.1、实践ServiceAccount

3.1.1、定义ServiceAccount

首先,我们定义一个ServiceAccount,它也是一个API对象,定义如下:

1
2
3
4
5
kind: ServiceAccount
apiVersion: v1
metadata:
namespace: mynamespace
name: my-sa

可以看到,一个最简单的 ServiceAccount 对象只需要 Name 和 Namespace 这两个最基本的字段。

3.1.2、定义Role

然后,我们直接使用上面定义的Role对象:

1
2
3
4
5
6
7
8
9
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: mynamespace
name: my-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch","list"]

3.1.3、定义RoleBinding

接着,我们定义一个RoleBinding,将Role绑定到ServiceAccount,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: mynamespace
name: my-rolebinding
subjects:
- kind: ServiceAccount
name: my-sa
namespace: mynamespace
roleRef:
kind: Role
name: my-role
apiGroup: rbac.authorization.k8s.io

可以看到,在这个 RoleBinding 对象里,subjects 字段的类型(kind),不再是一个 User,而是一个名为 my-sa 的 ServiceAccount。

3.1.4、创建对象

最后,我们用 kubectl 命令创建这三个对象:

1
2
3
$ kubectl create -f svc-account.yaml
$ kubectl create -f role.yaml
$ kubectl create -f role-binding.yaml

3.1.5、查看ServiceAccount

当创建完成后,我们可以查看一下这个 ServiceAccount 的详细信息:

1
2
3
4
5
6
7
8
9
10
11
$ kubectl get sa -n mynamespace -o yaml
- apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: 2018-09-08T12:59:17Z
name: example-sa
namespace: mynamespace
resourceVersion: "409327"
...
secrets:
- name: example-sa-token-vmfg6

可以看到,Kubernetes 会为这个 ServiceAccount 自动创建并分配一个 Secret 对象(上述 ServiceAcount 定义里最下面的 secrets 字段)。而这个 Secret对象,就是这个 ServiceAccount 对应的、用来跟 APIServer 进行交互的授权文件,我们一般称它为:Token。Token 文件的内容一般是证书或者密码,它以一个 Secret 对象的方式保存在 Etcd 当中。

3.1.6、Pod使用ServiceAccount

用户有了,也进行了授权,那如何使用这个用户呢?

此时,Kubernetes中的主角Pod就该登场了,我们来看如下定义:

1
2
3
4
5
6
7
8
9
10
kind: Pod
apiVersion: v1
metadata:
namespace: mynamespace
name: sa-token-test
spec:
containers:
- name: nginx
image: nginx:1.7.9
serviceAccountName: my-sa

在这里,我们定义了 Pod 要使用的 ServiceAccount 的名字是:my-sa。这样,这个Pod就可以以my-sa这个内置用户的身份对APIServer进行一些操作了。

3.1.7、ServerAccount的真面目

等这个 Pod 运行起来之后,我们就可以看到,该 ServiceAccount 的 token,也就是Secret 对象,被 Kubernetes 自动挂载到了容器的 /var/run/secrets/kubernetes.io/serviceaccount 目录下,如下所示:

1
2
3
4
5
6
7
8
9
$ kubectl describe pod sa-token-test -n mynamespace
Name: sa-token-test
Namespace: mynamespace
...
Containers:
nginx:
...
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from example-sa-token-vmfg6 (ro)

这时候,我们可以通过 kubectl exec 查看到这个目录里的文件:

1
2
3
$ kubectl exec -it sa-token-test -n mynamespace -- /bin/bash
root@sa-token-test:/# ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt namespace token

如上所示,容器里的应用,就可以使用这个 ca.crt 来访问 APIServer 了。更重要的是,此时它只能够做 GET、WATCH 和 LIST 操作。因为 my-sa 这个 ServiceAccount 的权限,已经被我们绑定的 Role 做了限制。

3.2、默认ServiceAccount

到这里,我们可能会有这样的疑问:

我们平时定义Pod时并没有指定ServiceAccount,那是不是说Pod就不能对APIServer进行任何操作了呢?

并不是。在Kubernetes中,如果一个Pod没有声明ServiceAccount,那么Kubernetes 就会自动在它的 Namespace 下创建一个名为 default 的默认 ServiceAccount,然后分配给这个 Pod。

在默认情况下,这个默认 ServiceAccount 并没有关联任何 Role。也就是说,此时它有访问 APIServer 的绝大多数权限。当然,这个访问所需要的 Token,还是默认 ServiceAccount 对应的 Secret 对象为它提供的,如下所示:

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
$ kubectl describe sa default
Name: default
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: default-token-s8rbq
Tokens: default-token-s8rbq
Events: <none>

$ kubectl get secret
NAME TYPE DATA AGE
default-token-s8rbq kubernetes.io/service-account-token 3 82d

$ kubectl describe secret default-token-s8rbq
Name: default-token-s8rbq
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name=default
kubernetes.io/service-account.uid=ffcb12b2-917f-11e8-abde-42010aa80002

Type: kubernetes.io/service-account-token

Data
====
ca.crt: 1025 bytes
namespace: 7 bytes
token: <TOKEN数据>

可以看到,Kubernetes 会自动为默认 ServiceAccount 创建并绑定一个特殊的 Secret:它的类型是kubernetes.io/service-account-token;它的 Annotation 字段,声明了kubernetes.io/service-account.name=default,即这个 Secret 会跟同一 Namespace 下名叫 default 的 ServiceAccount 进行绑定。

注:在生产环境中,强烈建议为所有 Namespace 下的默认 ServiceAccount,绑定一个只读权限的 Role。

3.3、用户组

除了“用户”(User)外,Kubernetes 还拥有“用户组”(Group)的概念,也就是一组“用户”的意思。如果我们为 Kubernetes 配置了外部认证服务的话,这个“用户组”的概念就会由外部认证服务提供。而对于 Kubernetes 的内置“用户”ServiceAccount 来说,这个“用户组”的概念也同样适用。

3.3.1、ServiceAccount组

实际上,一个 ServiceAccount,在 Kubernetes 里对应的“用户”的名字是:

1
system:serviceaccount:<Namespace名字>:<ServiceAccount名字>

而它对应的内置“用户组”的名字,就是:

1
system:serviceaccounts:<Namespace名字>

也就是说,ServiceAccount所属的Namespace就是它的组。

3.3.2、ServiceAccount组的应用

比如,现在我们可以在 RoleBinding 里定义如下的 subjects:

1
2
3
4
subjects:
- kind: Group
name: system:serviceaccounts:mynamespace
apiGroup: rbac.authorization.k8s.io

这就意味着这个 Role 的权限规则,作用于 mynamespace 里的所有 ServiceAccount。这就用到了“用户组”的概念。

四、理解ClusterRole

4.1、从rules字段说起

我们先来看一下rules字段,它由如下五个字段构成:

  • apiGroups(string array):声明规则是为哪些API组下的资源定义的。如上,空(“”)表示核心API组。
  • resources(string array):定义规则在哪些资源上生效。如上,Pods表示将规则应用的Pod对象上。
  • verbs(string array):定义可以对资源进行哪些操作。如上,表示可以对资源进行get、watch和list等操作。
  • resourceNames(string array):是一个可选字段。它是一个资源名称的白名单,即允许规则在哪些特定的资源上生效。
  • nonResourceURLs(string array):定义了用户可访问的URL。它支持使用“*s”的形式,但只作用于整个url的最后一部分,因为nonResourceURL是没有namespace的,所以该形式,只适用于ClusterRole。一个规则可以在API资源(比如,Pod和Secret)上生效,也可以在非资源的URL(比如,/api)上生效,但不能同时在两者上生效。

从这里可以看出,在kubernetes项目中,还存在一种ClusterRole对象,那么ClusterRole是用来干什么的呢?

4.2、ClusterRole & ClusterRoleBinding

我们需要知道的是,Role 和 RoleBinding 对象都是 Namespaced 对象(Namespaced Object),即它们的权限规则仅在它们自己的 Namespace 内有效,roleRef 也只能引用当前 Namespace 里的 Role 对象。

那么,对于非 Namespaced(Non-namespaced)对象(比如:Node),或者,某一个 Role 想要作用于所有的 Namespace 的时候,我们又该如何去做授权呢?

这时候,我们就必须要使用 ClusterRole 和 ClusterRoleBinding 这两个组合了。这两个 API 对象的用法跟 Role 和 RoleBinding 完全一样。只不过,它们的定义里,没有了 Namespace 字段,如下所示:

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: my-clusterrole
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: my-clusterrolebinding
subjects:
- kind: User
name: my-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: my-clusterrole
apiGroup: rbac.authorization.k8s.io

通过如上ClusterRole 和 ClusterRoleBinding 的组合,名为 my-user 的用户,就拥有对所有 Namespace 里的 Pod 进行 GET、WATCH 和 LIST 操作的权限。

4.3、系统保留的ClusterRole

在 Kubernetes 中已经内置了很多个为系统保留的 ClusterRole,它们的名字都以 system: 开头。我们可以通过 kubectl get clusterroles 查看到它们。

一般来说,这些系统 ClusterRole,是绑定给 Kubernetes 系统组件对应的 ServiceAccount 使用的。比如,其中一个名叫 system:kube-scheduler 的 ClusterRole,定义的权限规则是 kube-scheduler(Kubernetes 的调度器组件)运行所需的必要权限。我们可以通过如下指令查看这些权限的列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ kubectl describe clusterrole system:kube-scheduler
Name: system:kube-scheduler
...
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
...
services [] [] [get list watch]
replicasets.apps [] [] [get list watch]
statefulsets.apps [] [] [get list watch]
replicasets.extensions [] [] [get list watch]
poddisruptionbudgets.policy [] [] [get list watch]
pods/status [] [] [patch update]

这个 system:kube-scheduler 的 ClusterRole,就会被绑定给 kube-system Namesapce 下名叫 kube-scheduler 的 ServiceAccount,它正是 Kubernetes 调度器的 Pod 声明使用的 ServiceAccount。

除此之外,Kubernetes 还提供了四个预先定义好的 ClusterRole 来供用户直接使用:

  • cluster-admin
  • addmin
  • edit
  • view

通过它们的名字,我们应该能大致猜出它们都定义了哪些权限。比如,这个名叫 view 的 ClusterRole,就规定了使用者只有 Kubernetes API 的只读权限。

我们需要注意的是,上面这个 cluster-admin 角色,对应的是整个 Kubernetes 项目中的最高权限(verbs=*),如下所示:

1
2
3
4
5
6
7
8
9
$ kubectl describe clusterrole cluster-admin
Name: cluster-admin
Labels: kubernetes.io/bootstrapping=rbac-defaults
Annotations: rbac.authorization.kubernetes.io/autoupdate=true
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
*.* [] [] [*]
[*] [] [*]

所以,务必要谨慎而小心地使用 cluster-admin。

4.4、权限设置

在 Role 或者 ClusterRole 里面,如果要赋予用户 my-user 所有权限,那我们就可以给它指定一个 verbs 字段的全集,如下所示:

1
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

这些就是当前 Kubernetes(v1.11)里能够对 API 对象进行的所有操作了。

类似地,Role 对象的 rules 字段也可以进一步细化。比如,我们可以只针对某一个具体的对象进行权限设置,如下所示:

1
2
3
4
5
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["my-config"]
verbs: ["get"]

这个例子就表示,这条规则的“被作用者”,只对名叫“my-config”的 ConfigMap 对象,有进行 GET 操作的权限。

五、小结

在这篇文章中,我们主要讨论了Kubernetes项目中的RBAC机制,我们需要掌握如下几个概念:

  • Subject:使用者,即认证用户,它可以是“用户”,也可以是“用户组”,在Kubernetes中通常使用的是ServiceAccount(Kubernetes系统的内置用户)。
  • Role:角色,即一组权限规则,它可以分为Role和ClusterRole,前者的作用范围是特定的Namespace,后者的作用范围是所有的Namespace。
  • Rolebinding:角色绑定,用于将“角色”与“使用”绑定,也即为用户分配权限。

访问控制:基于角色的权限控制
https://kuberxy.github.io/2021/03/14/访问控制:基于角色的权限控制/
作者
Mr.x
发布于
2021年3月14日
许可协议