理解Kubernetes中RBAC是什么

Kubernetes RBAC角色权限控制

在Kubernetes中所有的API对象都保存在ETCD里,可是,对这些API对象的操作,却一定是通过访问kube-apiserver实现的。我们需要APIServer来帮助我们授权工作,而在Kubernetes项目中,负责完成授权(Authorization)的工作机制就是RBAC: 基于角色的访问控制 (Role-Based Access Control)

RBAC是基于角色的访问控制 (Role-Based Access Control) 在RBAC中,权限与角色相关联。Kubernetes 基于角色的访问控制使用rbac.authorization.k8s.io API组来实现权限控制,RBAC允许管理员通过Kubernetes API动态的配置权限策略。如果需要开启RBAC授权需要在apiserver组件中指定--authorization-mode=Node,RBAC

修改完毕后需要重启apiserver,在我提供的二进制安装已经将下面参数添加进去。如果使用的是kubeadm安装在1.6版本默认已经启用了RBAC

这里我们需要明确三个RBAC最基本的概念

[√] Role: 角色,它定义了一组规则,定义了一组对Kubernetes API对象的操作权限
[√] Subject: 被作用者,既可以是"人",也可以是机器,当然也可以是我们Kubernetes中定义的用户(ServiceAccount主要负责kubernetes内置用户)
[√] RoleBinding: 定义了"被作用者"和"角色"的绑定关系

RBAC API对象

Kubernetes有一个很基本的特性就是它的所有资源都是模型化的API对象,允许执行CRUD(Create、Read、Update、Delete)操作。资源如下

[√] Pods
[√] ConfigMaps
[√] Deployments
[√] Nodes
[√] Secrets
[√] Namespaces

资源对象可能存在的操作有如下

[√] create
[√] get
[√] delete
[√] list
[√] update
[√] edit
[√] watch
[√] exec

这些资源和API Group进行关联,比如Pods属于Core API Group,而Deployment属于apps API Group,要在kubernetes中进行RBAC授权

Role && Rolebinding

实际上,Role本身就是一个Kubernetes的API对象,定义文件如下

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: mynamespace
  name: example-role
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
  
文件解释

首先,Role对象指定了它能产生作用的Namespace(mynamespace)。Namespace是kubernetes项目中的一个逻辑管理单位。不同Namespace的API对象,在通过kubectl命令进行操作的时候,是互相隔离的

namespace并不会提供任何实际的隔离或者多租户能力,如果没有设置namespace默认则是default

rules字段定义它的是权限规则,这条规则的含义就是允许"被作用者",对namespace下面pod (resources中定义)有哪些权限

用户的权限对应的API资源对象已经创建了,但是还没有绑定。也就是只有一个规则没有规定哪些用户使用这个权限
这里进行Rolebinding介绍

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: example-rolebinding
  namespace: mynamespace
subjects:
- kind: User
  name: example-user
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: example-role
  apiGroup: rbac.authorization.k8s.io


在Rolebinding中定义了一个subject字段,即"被作用者"。它的类型是User,即Kubernetes里的用户,名称为example-user

在kubernetes里的User,也就是用户,只是一个授权系统里的逻辑概念。它需要通过外部认证服务,比如Keystone,来提供。或者直接给APIServer指定一个用户名、密码文件。那么kubernetes的授权系统就能够从这个文件里找到对象的用户

Rolebinding对象通过roleRef字段可以直接通过名字,来引用前面定义的Role对象(example-role),从而定义了"被作用者(Subject)"和"角色(Role)"之间的绑定关系

Role和RoleBinding 他们的权限限制规则仅仅在他们自己的namespace内有效,roleRef也只能引用当前namespace里的Role对象

ClusterRole && ClusterRoleBinding

对于某一个role想要作用的namespace的时候,就必须需要使用ClusterRole和ClusterRoleBinding两个组合。这两个API 对象的用法跟Role和Rolebinding完全一样

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: example-clusterrole
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
 
###############################################
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: example-clusterrolebinding
subjects:
- kind: User
  name: example-user
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: example-clusterrole
  apiGroup: rbac.authorization.k8s.io
  
在上面的例子中,ClusterRole和ClusterRoleBinding的组合,意味着名叫example-user的用户,拥有对所有namespace里的Pod进行Get、Watch、List操作的权限。

类似的,Role对象的rules字段也可以进一步细化,可以只针对某一个具体权限对象进行设置

rules:
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["my-config"]
  verbs: ["get"]
  
上面的例子表示,这条规则的"被作用者",只对my-config的configmap对象有权限进行get操作

前面也说过,在Kubernetes中已经内置了很多个位系统保留的ClusterRole,它们的名字都是以system:开头。一般来说,这些内置的ClusterRole,是绑定给Kubernetes系统组件对应的ServiceAccount使用

#我们可以通过下面的命令获取到
[root@abcdocker sa-test]# kubectl get clusterroles
[root@kubernetes-m mnt]#  kubectl get clusterroles
NAME                                                                   CREATED AT
admin                                                                  2021-10-27T08:09:35Z
cluster-admin                                                          2021-10-27T08:09:35Z
edit                                                                   2021-10-27T08:09:35Z
flannel                                                                2021-10-27T08:14:35Z
kubeadm:get-nodes                                                      2021-10-27T08:09:37Z
system:aggregate-to-admin                                              2021-10-27T08:09:35Z
system:aggregate-to-edit                                               2021-10-27T08:09:35Z
system:aggregate-to-view                                               2021-10-27T08:09:35Z
system:auth-delegator                                                  2021-10-27T08:09:35Z
---
此外,Kubernetes还提供了四个预先定义好的ClusterRole来提供用户直接使用

[√] cluster-admin
[√] admin
[√] edit
[√] view

其中cluster-admin角色,对应的是整个Kubernetes项目中最高权限(verbs=*)

#我们可以通过下面的命令查看clusterrole的权限
[root@abcdocker sa-test]# kubectl describe clusterrole cluster-admin -n kube-system
[root@kubernetes-m mnt]# kubectl describe clusterrole cluster-admin -n kube-system
Name:         cluster-admin
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  *.*        []                 []              [*]
             [*]                []              [*]

ServiceAccount
前面所介绍的大多数不使用,ServiceAccount主要负责kubernetes内置用户,下面简单定义一个ServiceAccount

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: mynamespace
  name: example-sa
  
我们定义了一个serverAccount对象,只需要name以及namespace最基础字段就可以。然后通过编写rolebinding的yaml文件,来为这个serviceAccount分配权限


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
  
在Rolebinding对象里,subject字段的类型(kind),不在是一个User,而是一个名叫example-sa的ServerAccount。而roleRef引用的Role对象,依然名叫example-role。也就是我们上面定义的

接下来我们创建演示

[root@kubernetes-m mnt]# cat pod-sa.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: sa-token-test
spec:
  containers:
  - name: nginx
    image: nginx:alpine
  serviceAccountName: example-sa


[root@kubernetes-m mnt]# cat role-binding.yaml 
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
  
  
[root@kubernetes-m mnt]# cat role.yaml 
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: mynamespace
  name: example-role
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
  


#接下来我们创建命名空间

[root@kubernetes-m mnt]# kubectl get sa  -n mynamespace
NAME      SECRETS   AGE
default   1         6m1s
[root@kubernetes-m mnt]# kubectl get sa -n mynamespace default -o yaml >sa.yaml
[root@kubernetes-m mnt]# cat sa.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2021-11-10T03:41:46Z"
  name: default
  namespace: mynamespace
  resourceVersion: "1881808"
  selfLink: /api/v1/namespaces/mynamespace/serviceaccounts/default
  uid: 9a958122-eeb8-4672-9c9e-fd5dff34fd55
secrets:
- name: default-token-d8nzp

Kubernetes会为ServiceAccount自动创建一个Secret对象,即ServiceAccount定义最下面的secrets字段。其中,这里的secret就是ServiceAccount对应来跟APIServer进行交互的授权文档,一般为TOken,保存证书和密码等,它以一个Secret对象保存在ETCD中

这时候我们就可以直接引用这个sa(serviceAccount)了

apiVersion: v1
kind: Pod
metadata:
  namespace: mynamespace
  name: sa-token-test
spec:
  containers:
  - name: nginx
    image: nginx
  serviceAccountName: default

接下来我们可以通过describe查看pod状态

[root@kubernetes-m mnt]# kubectl describe pod sa-token-test 
---
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-xjwbk (ro)

ServiceAccount的token也就是secret对象是被Kubernetes自动挂载到了容器/var/run/secrets/kubernetes.io/serviceaccount目录下

我们可以到Pod目录进行查看

[root@kubernetes-m mnt]# kubectl exec -it sa-token-test -- /bin/sh
/ # ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt     namespace  token

在Kubernetes集群访问主要是通过ca.crt来访问Apiserver,更重要的是此时它只可以做GET、WATCH和LIST操作。因为example-sa这个ServiceAccount的权限已经被我们绑定了role限制

如果一个Pod没有声明sa,Kubernetes会自动在它的namespace创建一个名称为default的默认ServiceAccount,然后分配给Pod。但是这种情况下默认ServiceAccount没有关联任何Role。也就是说它有APIServer的绝大多数权限

User && Group

Kubernetes除了有用户(User),还拥有用户组(Group)概念,如果我们Kubernetes配置了外部认证服务的话,这个用户组的概念就由外部认证服务提供

一个ServiceAccount在Kubernetes对应的用户的名字是
system:serviceaccount:<ServiceAccount名字>

而对应的用户组则是
system:serviceaccounts:<Namespace名字>

比如,我们可以在RoleBinding中定义如下subjects

subjects:
- kind: Group
  name: system:serviceaccounts:mynamespace
  apiGroup: rbac.authorization.k8s.io
 
#这就意味着role的规则权限,作用于mynamespace里的所有ServiceAccount,这就用到了用户组的概念
 
 
 
subjects:
- kind: Group
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io
 
#这个role的规则权限,则作用于整个系统里ServiceAccount

在Kubernetes中已经内置了很多歌为系统保留的ClusterRole,它们的名字都以system:开头,可以使用kubectl get clusterroles查找到

现在我们可以理解,所谓的角色(Role),其实就是一组规则权限列表,而我们分配这些权限的方式,就是通过创建RoleBinding对象,将被用者(subject)和权限列表绑定。

而对应的ClusterRole和ClusterRoleBinding,则是Kubernetes集群级别的Role和RoleBinding,它们不受namespace限制

一、创建一个只能访问某个namespace的ServiceAccount

当我们创建一个sa之后,会自动帮我们创建一个secrets
[root@kubernetes-m mnt]# kubectl create sa test-sa -n kube-system
serviceaccount/test-sa created

接下来可以查看一下sa

[root@kubernetes-m mnt]# kubectl get sa -n kube-system test-sa -o yaml >test-sa.yaml
[root@kubernetes-m mnt]# cat test-sa.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2021-11-10T03:54:52Z"
  name: test-sa
  namespace: kube-system
  resourceVersion: "1883725"
  selfLink: /api/v1/namespaces/kube-system/serviceaccounts/test-sa
  uid: 12b14ac8-1303-4257-88c8-144917cba48c
secrets:
- name: test-sa-token-v5zrp
[root@kubernetes-m mnt]# 

可以通过get secret查看

[root@kubernetes-m mnt]# kubectl get secrets -n kube-system |grep test-sa-token
test-sa-token-v5zrp                              kubernetes.io/service-account-token   3      147m


我们需要创建一个role (角色)对象与sa进行关联

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: sa-role
  namespace: kube-system
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
- apiGroups: ["apps", "extensions"]
  resources: ["pods", "deployment", "replicasets"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  
[root@kubernetes-m mnt]# kubectl apply -f sa-role.yaml 
role.rbac.authorization.k8s.io/sa-role created
[root@kubernetes-m mnt]# kubectl get role -n kube-system
NAME                                             CREATED AT
extension-apiserver-authentication-reader        2021-10-27T08:09:36Z
kube-proxy                                       2021-10-27T08:09:38Z
kubeadm:kubelet-config-1.18                      2021-10-27T08:09:36Z
kubeadm:nodes-kubeadm-config                     2021-10-27T08:09:36Z
sa-role                                          2021-11-10T06:23:39Z
system::leader-locking-kube-controller-manager   2021-10-27T08:09:36Z
system::leader-locking-kube-scheduler            2021-10-27T08:09:36Z
system:controller:bootstrap-signer               2021-10-27T08:09:36Z
system:controller:cloud-provider                 2021-10-27T08:09:36Z
system:controller:token-cleaner                  2021-10-27T08:09:36Z


角色创建完成之后我们就需要创建一个RoleBinding

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: sa-rolebinding
  namespace: kube-system
subjects:
- kind: ServiceAccount
  name: test-sa
  namespace: kube-system
roleRef:
  kind: Role
  name: sa-role
  apiGroup: rbac.authorization.k8s.io


[root@kubernetes-m mnt]# kubectl apply -f sa-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/sa-rolebinding created
[root@kubernetes-m mnt]# kubectl get rolebinding -n kube-system|grep sa
sa-rolebinding                                      Role/sa-role                                          7s

这时候我们创建的ServerAccount已经和我们role进行绑定了,通过rolebinding进行绑定。下面可以进行测试

#首先我们复制我们创建sa中secret里面的token

[root@kubernetes-m mnt]# kubectl get sa -n kube-system test-sa -o yaml >test-sa.yaml
[root@kubernetes-m mnt]# cat test-sa.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2021-11-10T03:54:52Z"
  name: test-sa
  namespace: kube-system
  resourceVersion: "1883725"
  selfLink: /api/v1/namespaces/kube-system/serviceaccounts/test-sa
  uid: 12b14ac8-1303-4257-88c8-144917cba48c
secrets:
- name: test-sa-token-v5zrp

[root@kubernetes-m mnt]# kubectl get secrets default-token-xjwbk -o yaml > default.yaml
[root@kubernetes-m mnt]# cat default.yaml 
apiVersion: v1
data:
  ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeE1UQXlOekE0TURreE5Wb1hEVE14TVRBeU5UQTRNRGt4TlZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTFZJCnhoR0p4N1lyWDQ5cFVwcGZUbGdQVFZSanllc0FPRFN2bFpOcmRnbFg3S3VMTnVJODdhUXN6VElEZjhVOGtWdFIKV3V1eDB4Y3V1OW92SWxOSFBwdnpabXRWWDNHTmNLUS9vK25aa1ppZkd3NW5TekZlRjZpcDBxNEc4TFZUM3hwNQpBeG8vTEtRS0NoZkpkajgrU1J1a3U4TUlld1h6TFBNUzM4MlhDQ3U2UEhUaW4wTWhVUXI1M1dqbmdUeHZDa3RGClhvRDVHRXVjZXpvQUFLbklMNHRCWkVjNmVPZXk4UHVES0ViN2Z1RmQ4bTNWQTJjTjF5ek9LODZ0cnZUR0phckkKYldJZjM4eXgzdlBEK2JYVWdJUkhQYS9aM1BZNzFKemt6YTdTbHM3V0swUnI5dUFGTXJYWnNlZVJxenppdDJDaQorTnArWGcyYTRvNVhndUhkUXE4Q0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFHN2Y1a0FzOVFyT0gvUVF4SXhPdGo4Z3NTSE0KQXUzTE5SOTVzNE44NkZVdmlJci8zcU42NGF1UTU5TUhQTnE1bmRlSS9oaTVQSnFJcFpzUTBxcSt2SVRadUtHaAo4U2x5a0U2L2duRERFUTNsdGR3M0wvRjVUKythZTAwL1VlWE5rQjRqUFlNZWhCMHY1c002aUxFN09aSXdBd0s5Cm9FVmNvV2Y2bC9VTWZIRmlqWTZWR21oVzZiWDBIYjdZT3NqQmdpdkZJQXluRGlLdzhxNSs2WXlYZDJ2RkNXR3AKaG02NWN4TDFVSGQ0dGdvRlN6a3psd1NPYUQ4YlVkWEhUTklXakU0NnJwaXBjR0JzU3NkczhqK3FaWmdydzRNawo2K3dnUTZseURWVEtkUXZWd3FtMzlXQ3lpRUVwU2RKMm8vMmFwVnIxKyt4UGlKWXlKaTZoOE1ZY0ZNbz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
  namespace: ZGVmYXVsdA==
  token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklsSk1RV2szYm5GQ1F6ZDZiWEZ6TFVwaWFVSlJWWGxCWTNVeFlWSjRlbmg1UjAxUE5qSlNSVzlPT0djaWZRLmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUprWldaaGRXeDBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpXTnlaWFF1Ym1GdFpTSTZJbVJsWm1GMWJIUXRkRzlyWlc0dGVHcDNZbXNpTENKcmRXSmxjbTVsZEdWekxtbHZMM05sY25acFkyVmhZMk52ZFc1MEwzTmxjblpwWTJVdFlXTmpiM1Z1ZEM1dVlXMWxJam9pWkdWbVlYVnNkQ0lzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVnlkbWxqWlMxaFkyTnZkVzUwTG5WcFpDSTZJakExWVdVMVlqWmxMV1JrTVRBdE5EZGlPUzFoTVdFM0xUVTFPRGhqTUdKak5UVTJNU0lzSW5OMVlpSTZJbk41YzNSbGJUcHpaWEoyYVdObFlXTmpiM1Z1ZERwa1pXWmhkV3gwT21SbFptRjFiSFFpZlEuZGhTV2kwVmJZX01EQUFHSEFXc1RDS2dkSUJ5Z0d6ZGRrWWNoOUNvMHhfdUtmUm1vMmtNS3hKSUdweFNoRFRzcHpWQ21wSmNUTkJUd2J5NWh0SkdxZmx5OVVzY041UVNCdGdkOGthOU9LUzZvVjczTUh6S3o5TWFyMnRJazZOa1d0X1l5WTlfN1haWFFpU0ozdFZPTkJ5Wmk5eG1VUFVmWUw4YzdVTGk2VTllMlFCei0wUTlLeDZneFJRUmMzSmIyb1Rfd0Z6OUplR1lLbTA4MFJXbURnQmhHMWU4T3pKYi02Ymsxa181anJWR19qUzhsT2ZSOFMtTURsb2sxOHRnOGM5eEV1bzVYcVZrSjU2cExmbG9QZnVteWxkcGZOSndFN2dMOWNWWXdZc0IweTczRWhQUTVMc09ta3p4QnB2VGpvZjhMQkhNU3cyQ3VXU1pPM05RU1NB
kind: Secret
metadata:
  annotations:
    kubernetes.io/service-account.name: default
    kubernetes.io/service-account.uid: 05ae5b6e-dd10-47b9-a1a7-5588c0bc5561
  creationTimestamp: "2021-10-27T08:09:54Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:ca.crt: {}
        f:namespace: {}
        f:token: {}
      f:metadata:
        f:annotations:
          .: {}
          f:kubernetes.io/service-account.name: {}
          f:kubernetes.io/service-account.uid: {}
      f:type: {}
    manager: kube-controller-manager
    operation: Update
    time: "2021-10-27T08:09:54Z"
  name: default-token-xjwbk
  namespace: default
  resourceVersion: "367"
  selfLink: /api/v1/namespaces/default/secrets/default-token-xjwbk
  uid: bc3c19b1-6f81-4463-a20d-5e0f08b9a0f1
type: kubernetes.io/service-account-token
posted @ 2021-12-17 11:09  Layzer  阅读(226)  评论(0)    收藏  举报