RBAC 之所以一直没有写这个,一方面是因为它确实并不复杂,二来平常确实接触不多,今天就来顺路讲讲它

定义

Role-Based Access Control 我们常说的 RBAC,我们知道在一个后台管理系统里面经常会有权限管理。而最常用的一种权限设计方式就是基于角色的权限设计,A 用户是管理员拥有所有的权限,B 是普通用户角色只有部分权限等等,而 k8s 也是如此,k8s 内部也有许许多多的资源,通过 RBAC 的权限设计进行管理授权工作。

  • Role: 角色,定义了一组对 Kubernetes API 对象的操作权限
  • Subject: 用户,绑定角色的对象
  • RoleBinding: 用户和角色的绑定关系

其实非常好理解: 用户 -> 角色 -> 权限

Role

1
2
3
4
5
6
7
8
9
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" 标明 core API 组
resources: ["pods"]
verbs: ["get", "watch", "list"]

这就是 k8s 里面的角色,这里定义了一个角色 pod-reader 这个角色可以对 default 命名空间中的 pod 资源进行 get watch list 操作

ClusterRole

1
2
3
4
5
6
7
8
9
10
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
# "namespace" 被忽略,因为 ClusterRoles 不受名字空间限制
name: secret-reader
rules:
- apiGroups: [""]
# 在 HTTP 层面,用来访问 Secret 对象的资源的名称为 "secrets"
resources: ["secrets"]
verbs: ["get", "watch", "list"]

ClusterRole 属于集群范围,所以整个集群对应的资源都可以被使用

RoleBinding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定允许 "jane" 读取 "default" 名字空间中的 Pods
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
# 你可以指定不止一个“subject(主体)”
- kind: User
name: jane # "name" 是区分大小写的
apiGroup: rbac.authorization.k8s.io
roleRef:
# "roleRef" 指定与某 Role 或 ClusterRole 的绑定关系
kind: Role # 此字段必须是 Role 或 ClusterRole
name: pod-reader # 此字段必须与你要绑定的 Role 或 ClusterRole 的名称匹配
apiGroup: rbac.authorization.k8s.io

有了角色自然就是将用户绑定到对应的角色上去了,这个没有什么好说的,很容易理解

ClusterRoleBinding

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
# 此集群角色绑定允许 “manager” 组中的任何人访问任何名字空间中的 secrets
kind: ClusterRoleBinding
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager # 'name' 是区分大小写的
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io

同样的 ClusterRoleBinding 也是类似

ServiceAccount

其实 User 不多,其实我们更多的使用 k8s 里的内置用户也就是 ServiceAccount,这个 ServiceAccount 会生成一个 secrets 利用这个可以跟 APIServer 进行交互

1
2
3
4
5
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: mynamespace
name: example-sa
1
2
3
4
5
6
7
8
9
10
11
12
13
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: example-rolebinding
namespace: mynamespace
subjects:
- kind: ServiceAccount
name: example-sa
namespace: mynamespace
roleRef:
kind: Role
name: example-role
apiGroup: rbac.authorization.k8s.io
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
namespace: mynamespace
name: sa-token-test
spec:
containers:
- name: nginx
image: nginx:1.7.9
serviceAccountName: example-sa

通过 serviceAccountName 指定对应的 ServiceAccount 就可以使用了

通过 client-go 来使用

如果只是上面那样有点空,也有点虚,不如直接开官网文档来的快。下面就直接使用实际的案例来看看 rbac 到底是怎么样作用的。

目标

我们的目标是创建一个用户,然后绑定对应的权限,有了对应的权限之后,创建的对应的 deployment 使用对应的用户,然后获取到对应的资源,我们使用 client-go 直接获取对应的资源信息看看。

创建用户

这里我们使用 ClusterRole,并且直接绑定已有的角色 cluster-admin, 然后创建需要使用的 ServiceAccount

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: admin
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: admin
namespace: my-namespace
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin
namespace: my-namespace
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile

一个简单的 client-go 应用

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package main

import (
"context"
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
//
// Uncomment to load all auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth"
//
// Or uncomment to load specific auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)

func main() {
config, err := rest.InClusterConfig()
if err != nil {
panic(err.Error())
}

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}

fmt.Printf("all namespaces: ")
nsList, err := clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
if err != nil {
panic(err.Error())
}
for _, item := range nsList.Items {
fmt.Printf("%s ", item.Name)
}

fmt.Println()
fmt.Printf("all deployments in default namespace: ")
deployments, err := clientset.AppsV1().Deployments("default").List(context.TODO(), metav1.ListOptions{})
if err != nil {
panic(err.Error())
}
for _, item := range deployments.Items {
fmt.Printf("%s ", item.Name)
}

select {}
}

因为我们的应用运行在 k8s 内部,所以使用 client-go 非常容易,使用 rest.InClusterConfig() 就可以获取到对应配置

只要当前 deployment 有对应的权限,就可以获取到对应的资源 pod 或者 namespace 等

创建 deployment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
name: rabc-test
namespace: my-namespace
labels:
app: rabc-test
spec:
replicas: 1
selector:
matchLabels:
app: rabc-test
template:
metadata:
labels:
app: rabc-test
spec:
containers:
- name: rabc-test
image: linkinstars.com/rabc-test:latest
serviceAccountName: admin

创建 deployment 使用 serviceAccountName 指定刚才创建的 ServiceAccount admin 这里需要注意缩进,它是 template 下的 spec 的一个属性

运行后查看日志则可以获取到对应的所有的 namespace 列表和 default 下的所有 deployment

总结

  • 对于 k8s 的 rbac 其实使用还是非常简单的,基本上没有必要单独去记,用到的时候需要创建对应权限角色的时候查询对应的文档使用就可以了。

  • 而当我们有了对应权限之后就可以在 k8s 内部的应用使用 client-go 去获取对应的 k8s 的资源信息,并且还可以对相应的资源进行操作,这样就大大的丰富了你开发 k8s 原生应用的想象力

参考文档:https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/