RBAC权限控制
我们知道对于资源对象的操作都是通过API Server进行的,那么集群是怎么知道我们的请求是合法的请求呢?这就需要了解Kubernetes的另外一个重要知识点:RBAC(基于角色的权限控制)。
管理员可以通过Kubernetes API动态配置策略来启动RBAC
,需要在kube-apiserver
中天剑参数--authorization-mode=RBAC
,如果使用kubeadm安装的集群那么是默认开启了RBAC
的,可以通过查看Master节点上apiserver的静态Pod定义文件:
# cat /etc/kubernetes/manifests/kube-apiserver.yaml
......
- --authorization-mode=Node,RBAC
......
如果是二进制的方式搭建的集群,添加这个参数过后,需要重启kube-apiserer服务。
API对象
在 Kubernetes 项目中,一个 API 对象在 Etcd 里的完整资源路径,是由:Group(API 组)、 Version(API 版本)和 Resource(API 资源类型)三个部分组成的 。
通过这样的结构,整个Kubernetes里所有的API对象,实际上就可以用如下的树形结构表示:
从上图中我们也可以看出 Kubernetes 的 API 对象的组织方式,在顶层,我们可以看到有一个核心组/api/v1
和命名组(路径 /apis/$NAME/$VERSION
)和系统范围内的实体,比如 /metrics
。我们也可以用下面的命令来查看集群中的 API 组织形式:
# kubectl get --raw /
{
"paths": [
"/api",
"/api/v1",
"/apis",
"/apis/",
"/apis/admissionregistration.k8s.io",
......
"/version"
]
}
但是这个操作和我们平时操作 HTTP 服务的方式不太一样,这里我们可以通过 kubectl proxy
命令来开启对 apiserver 的访问:
# kubectl proxy
Starting to serve on 127.0.0.1:8001
然后重新开启一个新的终端,我们可以通过如下方式来访问批处理的 API 服务:
# curl http://127.0.0.1:8001/
{
"paths": [
"/api",
"/api/v1",
"/apis",
"/apis/",
"/apis/admissionregistration.k8s.io",
......
"/version"
]
}
通常,Kubernetes API 支持通过标准 HTTP POST
、PUT
、DELETE
和 GET
在指定 PATH 路径上创建、更新、删除和检索操作,并使用 JSON 作为默认的数据交互格式。
比如现在我们要创建一个 Deployment 对象,那么我们的 YAML 文件的声明就需要怎么写:
apiVersion: apps/v1
kind: Deployment
其中 Deployment
就是这个 API 对象的资源类型(Resource),apps
就是它的组(Group),v1
就是它的版本(Version)。API Group、Version 和 资源就唯一定义了一个 HTTP 路径,然后在 kube-apiserver 端对这个 url 进行了监听,然后把对应的请求传递给了对应的控制器进行处理。
RBAC
上面介绍了 Kubernetes 所有资源对象都是模型化的 API 对象,允许执行 CRUD(Create、Read、Update、Delete)
操作(也就是我们常说的增、删、改、查操作),比如:Pods、ConfigMaps 、Deployments、Nodes、Secrets、Namespaces ......
对于上面这些资源对象的可能存在的操作有:create 、get、delete、list、update、edit、watch、exec、patch
在更上层,这些资源和 API Group 进行关联,比如 Pods 属于 Core API Group,而 Deployements 属于 apps API Group,现在我们要在 Kubernetes 中通过 RBAC 来对资源进行权限管理,除了上面的这些资源和操作以外,我们还需要了解另外几个概念:
Rule
:规则,一组属于不同 API Group 资源上的一组操作的集合
Role
和 ClusterRole
:角色和集群角色,这两个对象都包含上面的 Rules 元素,二者的区别在于,在 Role 中,定义的规则只适用于单个命名空间,也就是和 namespace 关联的,而 ClusterRole 是集群范围内的,因此定义的规则不受命名空间的约束。另外 Role 和 ClusterRole 在Kubernetes 中都被定义为集群内部的 API 资源,和Pod、Deployment 这些对象类似,都是集群的资源对象,所以同样的可以使用 YAML 文件来描述,用 kubectl 工具来管理。
Subject
:主题,对应集群中尝试操作的对象,集群中定义了3种类型的主题资源:
-
User Account
:用户,这是由外部独立服务进行管理的,管理员进行私钥的分配,用户可以使用 KeyStone 或者 Goolge 帐号,甚至一个用户名和密码的文件列表也可以。对于用户的管理集群内部没有一个关联的资源对象,所以用户不能通过集群内部的 API 来进行管理 -
Group
:组,这是用来关联多个账户的,集群中有一些默认创建的组,比如 cluster-admin -
Service Account
:服务帐号,通过 Kubernetes API 来管理的一些用户帐号,和 namespace 进行关联的,适用于集群内部运行的应用程序,需要通过 API 来完成权限认证,所以在集群内部进行权限操作,我们都需要使用到 ServiceAccount -
RoleBinding
和ClusterRoleBinding
:角色绑定和集群角色绑定,简单来说就是把声明的 Subject 和我们的 Role 进行绑定的过程(给某个用户绑定上操作的权限),二者的区别也是作用范围的区别:RoleBinding 只会影响到当前 namespace 下面的资源操作权限,而 ClusterRoleBinding 会影响到所有的 namespace。
接下来我们来通过几个简单的示例,来学习下在 Kubernetes 集群中如何使用 RBAC
。
只能访问某个namespace的普通用户
我们想要创建一个 User Account,只能访问 kube-system 这个命名空间,对应的用户信息如下所示:
username: Negan
group: dev
创建用户凭证
Kubernetes 没有 User Account 的 API 对象,不过要创建一个用户帐号的话也是挺简单的,利用管理员分配给你的一个私钥就可以创建了,这个我们可以参考官方文档中的方法,这里我们来使用 OpenSSL
证书来创建一个 User 。
给用户Negan创建一个私钥,命名为Negan.key
:
# openssl genrsa -out Negan.key 2048
Generating RSA private key, 2048 bit long modulus
..........................+++
..........+++
e is 65537 (0x10001)
使用我们刚刚创建的私钥创建一个证书签名请求文件:Negan.csr
,要注意需要确保在-subj
参数中指定用户名和组(CN表示用户名,O表示组):
# openssl req -new -key Negan.key -out Negan.csr -subj "/CN=Negan/O=dev"
找到我们的 Kubernetes 集群的 CA
证书,我使用的是 kubeadm 安装的集群,CA 相关证书位于 /etc/kubernetes/pki/
目录下面,如果你是二进制方式搭建的,你应该在最开始搭建集群的时候就已经指定好了 CA 的目录,我们会利用该目录下面的 ca.crt
和 ca.key
两个文件来批准上面的证书请求。生成最终的证书文件,我们这里设置证书的有效期为 500 天:
# openssl x509 -req -in Negan.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out Negan.crt -days 500
Signature ok
subject=/CN=Negan/O=dev
Getting CA Private Key
现在查看我们当前文件夹下面是否生成了一个证书文件:
# ls
Negan.crt Negan.csr Negan.key
现在我们可以使用刚刚创建的证书文件和私钥文件在集群中创建新的凭证和上下文(Context):
# kubectl config set-credentials Negan --client-certificate=Negan.crt --client-key=Negan.key
User "Negan" set.
我们可以看到一个用户 Negan
创建了,然后为这个用户设置新的 Context,我们这里指定特定的一个 namespace:
# kubectl config set-context negan-context --cluster=kubernetes --namespace=kube-system --user=Negan
Context "negan-context" created.
到这里,我们的用户 Negan
就已经创建成功了,现在我们使用当前的这个配置文件来操作 kubectl 命令的时候,应该会出现错误,因为我们还没有为该用户定义任何操作的权限呢 :
# kubectl get pods --context=negan-context
Error from server (Forbidden): pods is forbidden: User "Negan" cannot list resource "pods" in API group "" in the namespace "kube-system"
创建角色
用户创建完成后,接下来就需要给该用户添加操作权限,我们来定义一个 YAML 文件,创建一个允许用户操作 Deployment、Pod、ReplicaSets 的角色,如下定义:(negan-role.yaml)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: negan-role
namespace: kube-system
rules:
- apiGroups: ["","apps"]
resources: ["deployments", "replicasets", "pods"]
verbs: ["get","list","watch","create","update","path","delete"] # 也可以用["*"]
其中 Pod 属于 core
这个 API Group,在 YAML 中用空字符就可以,而 Deployment 和 ReplicaSet 现在都属于 apps
这个 API Group(如果不知道则可以用 kubectl explain
命令查看),所以 rules
下面的 apiGroups
就综合了这几个资源的 API Group:["", "apps"],其中verbs
就是我们上面提到的可以对这些资源对象执行的操作,我们这里需要所有的操作方法,所以我们也可以使用['*']来代替。然后直接创建这个 Role:
# kubectl apply -f negan-role.yaml
role.rbac.authorization.k8s.io/negan-role created
注意这里我们没有使用上面的 negan-context
这个上下文,因为暂时还没有权限。
创建角色权限绑定
Role 创建完成了,但是很明显现在我们这个 Role
和我们的用户 Negan
还没有任何关系,这里就需要创建一个 RoleBinding
对象,在 kube-system 这个命名空间下面将上面的 negan-role
角色和用户 Negan
进行绑定:(Negan-rolebinding.yaml)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: negan-rolebinding
namespace: kube-system
subjects:
- kind: User
name: Negan
apiGroup: ""
roleRef:
kind: Role
name: negan-role
apiGroup: rbac.authorization.k8s.io # 留空字符串也可以,则使用当前的apiGroup
上面的 YAML 文件中我们看到了 subjects
字段,这里就是我们上面提到的用来尝试操作集群的对象,这里对应上面的 User
帐号 Negan
,使用kubectl 创建上面的资源对象:
# kubectl apply -f negan-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/negan-rolebinding created
测试
现在我们应该可以上面的 negan-context
上下文来操作集群了:
# kubectl get pods --context=negan-context
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-59877c7fb4-t5qvj 1/1 Running 0 2d3h
calico-node-67xqv 1/1 Running 0 27h
calico-node-8t2n5 1/1 Running 0 2d3h
calico-node-jnst5 1/1 Running 0 2d3h
coredns-7ff77c879f-9zmp4 1/1 Running 0 2d4h
coredns-7ff77c879f-kbmqc 1/1 Running 0 2d4h
etcd-master 1/1 Running 0 2d4h
kube-apiserver-master 1/1 Running 0 2d4h
kube-controller-manager-master 1/1 Running 0 2d4h
kube-proxy-255pm 1/1 Running 0 2d4h
kube-proxy-7b2qg 1/1 Running 0 27h
kube-proxy-xcd9f 1/1 Running 0 2d4h
kube-scheduler-master 1/1 Running 0 2d4h
metrics-server-5f55b696bd-rfxkv 1/1 Terminating 0 25h
metrics-server-5f55b696bd-sxb2q 1/1 Running 0 59m
# kubectl get deploy,rs --context=negan-context
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/calico-kube-controllers 1/1 1 1 2d3h
deployment.apps/coredns 2/2 2 2 2d4h
deployment.apps/metrics-server 1/1 1 1 25h
NAME DESIRED CURRENT READY AGE
replicaset.apps/calico-kube-controllers-59877c7fb4 1 1 1 2d3h
replicaset.apps/coredns-7ff77c879f 2 2 2 2d4h
replicaset.apps/metrics-server-5f55b696bd 1 1 1 25h
我们可以看到我们使用 kubectl 的使用并没有指定 namespace,这是因为我们我们上面创建这个 Context 的时候就绑定在了 kube-system 这个命名空间下面,如果我们在后面加上一个-n default
试看看呢?
# kubectl --context=negan-context get pods --namespace=default
Error from server (Forbidden): pods is forbidden: User "Negan" cannot list resource "pods" in API group "" in the namespace "default"
如果去获取其他的资源对象呢:
# kubectl --context=negan-context get svc
Error from server (Forbidden): services is forbidden: User "Negan" cannot list resource "services" in API group "" in the namespace "kube-system"
我们可以看到没有权限获取,因为我们并没有为当前操作用户指定其他对象资源的访问权限,是符合我们的预期的。这样我们就创建了一个只有单个命名空间访问权限的普通 User 。
只能访问某个namespace的ServiceAccount
上面我们创建了一个只能访问某个命名空间下面的普通用户,我们前面也提到过 subjects
下面还有一种类型的主题资源:ServiceAccount
,现在我们来创建一个集群内部的用户只能操作 kube-system 这个命名空间下面的 pods 和 deployments,首先来创建一个 ServiceAccount
对象:
# kubectl create sa negan-sa -n kube-system
当然我们也可以定义成 YAML 文件的形式来创建 :
apiVersion: v1
kind: ServiceAccount
metadata:
name: negan-sa
namespace: kube-system
然后新建一个Role对象:(negan-sa-role.yaml)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: negan-sa-role
namespace: kube-system
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
可以看到我们这里定义的角色没有创建、删除、更新
Pod 的权限,待会我们可以重点测试一下。
接着我们创建一个RoleBinding
对象,将上面的negan-sa
和角色negan-sa-role
进行绑定:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: negan-sa-rolebinding
namespace: kube-system
subjects:
- kind: ServiceAccount
name: negan-sa
namespace: kube-system
roleRef:
kind: Role
name: negan-sa-role
apiGroup: rbac.authorization.k8s.io
ServiceAccount 会生成一个 Secret 对象和它进行映射,这个 Secret 里面包含一个 token,我们可以利用这个 token 去登录 Dashboard,然后我们就可以在 Dashboard 中来验证我们的功能是否符合预期了:
# kubectl get secret -n kube-system |grep negan-sa
negan-sa-token-hpfcq kubernetes.io/service-account-token 3 11m
# kubectl get secret negan-sa-token-hpfcq -o jsonpath={.data.token} -n kube-system |base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6ImpROXdoRW1kRDhqV0I2R3pNYjJickp3aEhCT25QWUh0OUVjbjZ1WjZ1QUEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJuZWdhbi1zYS10b2tlbi1ocGZjcSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJuZWdhbi1zYSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImIxMzZjOWExLTBjY2EtNDAxOS04ZDdkLWRhY2MwYWI0NTczYiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTpuZWdhbi1zYSJ9.Fu58HJQ68sLkO1PlBlfsjVdMQsOhKY8tPsTYAF4hKcVJEX3bxfiyheTDzUAB9HE-NemBa3fXNeYbKygnj-C5n4Ch_DmPuk_RnO7-IJYTvjmGQ09FBd_4FetABSpJ6RtMp5iHL8zmTjQOMK4gt6M0sGJqnRsDLcw92eHbTDQyRAzjZsNbLIxlg5va-bdzCoz3dipEd2P14k9NKkejySyEbk6v9IjsOMJGyPJso9dmir8CVGcUJrJGwZT4Fq7gq2WTwRKFIQnwQjUG-ZDZoUjH5IKyTc6rA6p-HRN2XIAC1Nc1x7fhZuvBJHwXqSI0ajFrjc0YMU-0fQh1u-6ofTIuLA
使用这里的 token 去 Dashboard 页面进行登录:
我们可以看到上面的提示信息说我们现在使用的这个 ServiceAccount 没有权限获取当前命名空间下面的资源对象,这是因为我们登录进来后默认跳转到 default 命名空间,我们切换到 kube-system 命名空间下面就可以了:
我们可以看到可以访问 pod 列表了,但是也会有一些其他额外的提示:events is forbidden: User “system:serviceaccount:kube-system:negan-sa” cannot list events in the namespace “kube-system”
,这是因为当前登录用只被授权了访问 pod 和 deployment 的权限,同样的,访问下deployment看看可以了吗?
同样的,可以根据自己的需求来对访问用户的权限进行限制,可以自己通过 Role 定义更加细粒度的权限,也可以使用系统内置的一些权限……
可以全局访问的 ServiceAccount
刚刚我们创建的 negan-sa 这个 ServiceAccount
和一个 Role
角色进行绑定的,如果我们现在创建一个新的 ServiceAccount,需要它操作的权限作用于所有的 namespace,这个时候我们就需要使用到 ClusterRole
和 ClusterRoleBinding
这两种资源对象了。同样,首先新建一个 ServiceAcount 对象:
apiVersion: v1
kind: ServiceAccount
metadata:
name: negan-sa2
namespace: kube-system
然后创建一个 ClusterRoleBinding 对象(negan-clusterolebinding.yaml):
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: negan-sa2-clusterrolebinding
subjects:
- kind: ServiceAccount
name: negan-sa2
namespace: kube-system
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
从上面我们可以看到我们没有为这个资源对象声明 namespace,因为这是一个 ClusterRoleBinding 资源对象,是作用于整个集群的,我们也没有单独新建一个 ClusterRole 对象,而是使用的 cluster-admin
这个对象,这是 Kubernetes 集群内置的 ClusterRole 对象,我们可以使用 kubectl get clusterrole
和 kubectl get clusterrolebinding
查看系统内置的一些集群角色和集群角色绑定,这里我们使用的 cluster-admin
这个集群角色是拥有最高权限的集群角色,所以一般需要谨慎使用该集群角色。
创建上面集群角色绑定资源对象,创建完成后同样使用 ServiceAccount 对应的 token 去登录 Dashboard 验证下:
# kubectl get secret -n kube-system |grep negan-sa2
negan-sa2-token-k5hsd kubernetes.io/service-account-token 3 64s
# kubectl get secret negan-sa2-token-k5hsd -o jsonpath={.data.token} -n kube-system |base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6ImpROXdoRW1kRDhqV0I2R3pNYjJickp3aEhCT25QWUh0OUVjbjZ1WjZ1QUEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJuZWdhbi1zYTItdG9rZW4tazVoc2QiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoibmVnYW4tc2EyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiN2YxMDgzNDAtMjQ5Mi00NWFjLWFiMjktZjljYTFjMmEzMzlkIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOm5lZ2FuLXNhMiJ9.YYvrwTOMM4kb_e0BJnG1tFgZK3mZT1CSBLGxNtVb2054-QjztQ5lca0YRa4eyVRVXrx100GIkfsYumYT29QvGNLm455MLdUEqZXMaIH3WeHLtOx8C6VP87--9OsdCRukRKhXO8lUVsj8GwJR31LyibBcAfOND0FqjJfsH78YMOEqH9ZgeDJdqpbDVoh_j5sNqxdTJmgK5o9ZVauJ_0ohuNGfXSB64MYO_Z9eRaxjoYtXZNh990tjRPlH_soMOippdgbyU95oMme_RGD-tzKcbjnLkVlK7Ug93UKIYlJnsMf0N7q-oxO4tRFgia1LUcxZfXATN9zv3Q2AevzNZGiyQg