菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)

菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)


Kubernetes系列文章 主要内容
菜鸟学Kubernetes(K8s)系列——(一)关于Pod和Namespace 通过本文你将学习到: (1)什么是Pod,为什么需要它、如何创建Pod、Pod的健康检查机制(三种探针) (2)什么是标签、标签选择器 (3)什么是Namespace、他能做什么、如何创建它等等
菜鸟学Kubernetes(K8s)系列——(二)关于Deployment、StatefulSet、DaemonSet、Job、CronJob 通过本文你将学习到: (1)什么是Deployment,如何创建它、它的扩缩容能力是什么、自愈机制,滚动升级全过程、如何进行回滚 (2)什么是ReplicaSet、它和Deployment的关系是什么 (3)动态扩缩容能力(HPA)、蓝绿部署、金丝雀部署 (4)什么是DaemonSet/Job/CronJob、它们的功能是什么、如何创建使用等等
菜鸟学Kubernetes(K8s)系列——(三)关于Service、Ingress 通过本文你将学习到: (1)什么是Service,如何创建它、它的服务发现能力、对外暴露方式 (2)什么是NodePort类型的Service,它的使用方式、工作原理 (3)什么是Ingress,如何创建它、为什么需要Ingress (4)什么的Ingress-Nginx、他和Nginx是什么关系、Ingress的各种功能 (5)什么是headless服务等等
菜鸟学Kubernetes(K8s)系列——(四)关于Volume卷(PV、PVC、StorageClass等) 通过本文你将学习到: (1)什么是Volume卷、它的几种类型(emptyDir、hostPath、NFS)、这几种类型是使用方式 (2)什么是PV-PVC、为什么要用他们、他们是怎么协作的、如何使用PV-PVC (3)动态配置持久卷是什么,它是怎么工作的、如何实现动态的分配PV等等
菜鸟学Kubernetes(K8s)系列——(五)关于ConfigMap和Secret 通过本文你将学习到: (1)什么是ConfigMap,如何创建它、它能用来做什么事情、在实战中怎么使用ConfigMap (2)什么是Secret,如何创建它,怎么使用它等等
菜鸟学Kubernetes(K8s)系列——(七)关于Kubernetes底层工作原理 通过本文你将学习到: (1)Kubernetes的核心组件:Etcd、Api-Server、Scheduler、Controller-Manager、Kubelet、Kube-proxy的工作方式,工作原理。 (2)Kubernetes集群中核心组件的协作方式、运行原理。
菜鸟学Kubernetes(K8s)系列——(番外)实现Deployment的动态扩缩容能力(HPA) 通过本文你将学会实现Deployment的动态扩缩容能力(HPA)
菜鸟学Kubernetes(K8s)系列——(番外)安装Ingress-Nginx(工作原理) 通过本文你将学会安装Ingress-Nginx

1、Api-Server的认证授权流程

菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第1张图片
前面我们了解到,在k8s中,所有的请求(程序员通过命令行,或者Pod来操作k8s)都会发送到k8s的Api-Server。在Api-Server中进行权限控制流程:

  • 用户携带令牌或者证书给k8s的api-server发送请求,要求修改集群资源
  • k8s通过一系列的Authentication进行身份认证,判断这个请求的身份(用户名、用户ID和组信息)
  • 认证通过,k8s则利用Authorization查询用户的授权(有哪些权限)
  • 用户执行操作 。在这个过程中会利用AdmissionControl判断是否可以操作(CPU、硬盘、网络、内存)

1.1 用户和组

认证插件会返回已经认证过用户的用户名和组(多个组)。Kubernetes不会在任何地方存储这些信息,这些信息被用来验证用户是否被授权执行某个操作。

用户

Kubernetes区分了两种连接到API服务器的客户端

  • 真实的人(用户)
  • Pod(具体说应该是Pod中的应用)

这两种类型的客户端都使用了上述的认证插件进行认证。用户应该被管理在外部系统中,例如单点登录系统。但是Pod使用了一种称为ServiceAccount的机制,该机制被创建和存储在集群中作为ServiceAccount资源。

正常用户和ServiceAccount都可以属于一个或多个组。前面提到过认证插件会返回用户名、用户ID和组信息。组可以一次给多个用户赋予权限,而不是单独给用户赋予权限。

由插件返回的组仅仅是表示组名称的字符串,但是系统内置的组会有一些特殊的含义。

  • system:unauthenticated组用于所有认证插件都不会认证客户端身份的请求
  • system:authenticated组会自动分配给一个成功通过认证的用户
  • system:serviceaccounts组包含所有在系统中的ServiceAccount
  • system:serviceaccounts:组包含了所有在特定命名空间中的ServiceAccount

2、ServiceAccount

前面我们已经了解了,Api-Server要求客户端在服务器上执行操作之前对自己进行身份认证。比如Pod,他会通过发送/var/run/secrets/kubernetes.io/serviceaccount/token文件内容给Api-Server来进行身份认证,这个文件是通过Secret卷挂载在每个容器的文件系统中的。

这个token文件是什么呢?

每个Pod都会和一个ServiceAccount相关联,他代表了运行在Pod中应用程序的身份证明。这个token文件就是ServiceAccount的认证token。应用程序在使用这个token连接Api-Server时,身份认证插件会对ServiceAccount进行身份认证,并将ServiceAccount的用户名传回Api-Server。Api-Server再将这个用户名传给授权插件,这会决定该应用程序所尝试执行的操作是否被ServiceAccount允许执行。

ServiceAccount用户名的格式为:system:serviceaccount::

在每个namespace都会默认存在一个名为default的ServiceAccount,每个Pod都可以与一个ServiceAccount相关联,但是多个Pod可以使用同一个ServiceAccount。注意:Pod只能使用同一个Namespace下的ServiceAccount。
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第2张图片

2.1 创建ServiceAccount

由于创建一个ServiceAccount很简单,因此可以直接通过命令行的方式创建kubectl create serviceaccount test-sa

通过kubectl describe sa test-sa查看这个ServiceAccount。

[root@k8s-node1 ~]# kubectl create serviceaccount test-sa
serviceaccount/test-sa created

[root@k8s-node1 ~]# kubectl describe sa test-sa
Name:                test-sa
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>     ### 这些会被自动地添加到使用这个ServiceAccount的所有Pod中
Mountable secrets:   test-sa-token-fzzc6    ### 如果强制使用可挂载的密钥,那么使用这个ServiceAccount的Pod只能挂载这些密钥。下面会解释这个。
Tokens:              test-sa-token-fzzc6    ### 认证token,第一个token挂载在容器内
Events:              <none>

可以看到,这里已经创建了自定义的token密钥,并将它和ServiceAccount相关联。

通过查看这个secret,可以看到他记录了CA证书、namespace、token(ServiceAccount中使用的身份认证token就是JWT token)
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第3张图片

Mountable secrets

之前学习Secret的时候我们知道,可以为一个Pod挂载一个Secret。默认情况下Pod是可以挂载任何一个Secret的。但是,我们可以通过对ServiceAccount进行配置,让Pod只允许挂载ServiceAccount中列出的可挂载密钥(也就是Mountable secrets中列出的Secret)。

为了开启这个功能,需要在ServiceAccount中添加一个注解:kubernetes.io/enforce-mountable-secrets="true"

当ServiceAccount被加上上面这个注解后,任何使用这个ServiceAccount的Pod只能挂载进ServiceAccount指定的可挂载密钥。

2.2 将ServiceAccount分配给Pod

apiVersion: v1
kind: ServiceAccount
metadata:
  name: backend-sa
---
apiVersion: v1
kind: Pod
metadata:
  name: curl-custom-sa
spec:
  serviceAccountName: backend-sa    ### 这个Pod使用指定的ServiceAccount
  containers:
  - name: ng   
    image: nginx

注意:Pod的ServiceAccount必须在Pod创建时进行设置,后续不能被修改!

为这个Pod分配好了ServiceAccount仅仅只是代表着运行在这个Pod中的应用程序有了一个身份证明。如果这个Pod中的应用程序想去和Api-Server通信,那么他仅仅只能通过认证插件,接着他会被授权插件拦截(RBAC授权插件),然后返回403状态码(如果被认证插件拦截则会返回401状态码)。

3、RBAC授权插件

Kubernets的Api-Server可以配置使用一个授权插件来检查是否允许用户请求的动作执行。

RBAC授权插件将用户角色作为决定用户能否执行操作的关键因素。主体(可以是一个人、一个ServiceAccount,或者一组用户或ServiceAccount)和一个或多个角色相关联,每个角色被允许在特定的资源上执行特定的动作。

3.1 关于RBAC的资源

RBAC授权规则是通过四种资源来进行配置的,他们可以分为两组:

  • Role(角色)和ClusterRole(集群角色),他们指定了在资源上可以执行哪些动作。
  • RoleBinding(角色绑定)和ClusterRoleBinding(集群角色绑定),他们将上述角色绑定到特定的用户、组或ServiceAccount上。

角色定义了可以做什么操作,而绑定定义了谁可以做这些操作。

菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第4张图片
Role和RoleBinding是命名空间的资源,而ClusterRole和ClusterRoleBinding是集群级别的资源。

3.2 Role和RoleBinding实际应用

(1)创建Namespace和Pod

首先分别在foo和bar namespace下创建一个Pod:

kubectl create ns foo

kubectl create ns bar

kubectl run test --image=luksa/kubectl-proxy -n foo   ### 这个镜像启动后会运行一个kubectl proxy进程,这个进程会将接收到的请求发送给Api-Server,同时以foo命名空间下的默认ServiceAccount的Token和Api-Server进行身份认证

kubectl run test --image=luksa/kubectl-proxy -n bar

然后分别打开两个控制台,进入foo下的test的shell窗口

kubectl exec -it test -n foo -- sh

kubectl exec -it test -n bar -- sh

接下来验证RBAC是否已经开启并且阻止Pod读取集群状态,使用下面的命令来列出foo命名空间中的服务:

curl localhost:8001/api/v1/namespaces/foo/services

菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第5张图片

我们可以看到Api-Server拒绝了这个请求,他不允许列出foo命名空间中的Service,返回的code是403,表示的是授权过程失败(RBAC插件起的作用)了,也就是我们默认的ServiceAccount的默认权限不允许他列出或修改任何资源。下面看看如何让这个ServiceAccount可以去操作指定的资源。

(2)使用Role和RoleBinding

Role资源定义了哪些操作可以在哪些资源上执行。(或者是他定义了哪种类型的HTTP请求可以在哪些RESTful资源上执行)

创建Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: service-reader
  #namespace: test        ### 指定允许用户操作test命名空间中的指定资源!
  ### 注意,我在这里没有指定命名空间,而在apply的时候特意指定了!
rules:
- apiGroups: [""]           ### Service是核心apiGroup的资源,所以没有apiGroup名,就是""
  verbs: ["get", "list"]    ### 指定允许获取独立的Service,或者列出所有允许的Service
  resources: ["services"]   ### 指定是什么资源,一定要使用资源的复数形式!

分别在foo和bar命名空间下创建Role

kubectl apply -f service-reader.yaml -n foo ### 指定这个Role所在的命名空间

kubectl apply -f service-reader.yaml -n bar

这两个Role将会允许你在两个Pod中分别列出各自命名空间下Service。但是仅仅创建了这两个Role还不够,还需要将这个Role绑定到各自命名空间下的ServiceAccount上。

通过RoleBinding绑定ServiceAccount和Role

Role定义了哪些操作可以执行,但是没有指定谁可以执行这些操作。要实现这个效果,需要将Role绑定到一个主体(用户、组、ServiceAccount)

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: test
  namespace: foo
roleRef:  ### RoleBinding只能引用一个Role
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: service-reader    ### 这个RoleBinding引用了service-reader Role
subjects:                 ### 并将这个Role绑定到foo命名空间中的default ServiceAccount上
- kind: ServiceAccount    ### Rolebinding可以为一个Role绑定多个ServiceAccount
  name: default
  namespace: foo

这个文件的含义是:将service-reader这个Role绑定到foo这个命名空间下的名为default的ServiceAccount上。这个RoleBinding会被创建在foo命名空间下。

菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第6张图片
接下来在再测试从foo命名空间的Pod种访问Api-Server
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第7张图片
此时bar命名空间中的Pod还没有权限列出foo命名空间下的Service资源,甚至他自己命名空间下的所有Service资源他都无权访问,我们可以修改刚刚创建的RoleBinding,给他添加一个subjects。如下:(这也证明了一个Role可以和多个命名空间下的ServiceAccount进行绑定)

kubectl edit rolebinding test -n foo 

菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第8张图片
可以看到,在bar命名空间下的Pod可以查看到foo命名空间下的Service资源了!

注意:此时在bar命名空间下的Pod依旧不可以查看到自己bar命名空间下的Service资源!这是因为我们是将foo命名空间下的Role和bar命名空间下的ServiceAccount进行了绑定。而没有将bar命名空间下的Role和他自己空间的ServiceAccount绑定。

菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第9张图片

3.3 ClusterRole和ClusterRoleBinding实际应用

由于Role是命名空间级的资源,所以一个常规的角色只允许访问和角色在同一命名空间中的资源。如果你希望允许跨不同命名空间访问资源,就必须要在每个命名空间中创建一个Role和RoleBinding。当创建一个新的命名空间时,必须记住也要在新的命名空间中创建这两个资源。是不是有些太麻烦了。

对于上面的问题,ClusterRole可以解决,因为他们是集群级别的资源,他允许访问:

  • 集群级别的资源
  • 非资源类型的URL
  • 作为单个命名空间内部绑定的公共角色,从而避免必须在每个命名空间中重新定义相同的角色。(第三条就是解决上面问题的方案,具体可以参考(3))

在Kubernetes中存在一些集群级别的资源:Node、PV、Namespace等,而且Api-Server对外暴露了一些不表示资源的URL路径,如/healthz,对于这些资源或者非资源类型的URL,普通的Role都是没办法为他们授权的,但是ClusterRole可以!

(1)允许访问集群级别的资源

下面创建一个ClusterRole,这个角色具有查询所有PV的权限。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pv-reader
rules:
- apiGroups:
  - ""
  resources:
  - persistentvolumes
  verbs:
  - get
  - list

现在我们只是创建了这个ClusterRole,还未将他绑定到ServiceAccount上。这时我们先在foo命名空间下的Pod中尝试查询一下pv,可以看他,这个Pod还没有权限查看pv。
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第10张图片

接着创建一个ClusterRoleBinding,将ClusterRole和主体进行绑定。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: pv-test
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: pv-reader
subjects:
- kind: ServiceAccount
  name: default
  namespace: foo

这时,我们就可以在foo命名空间下的Pod中获取集群中的PV资源了。
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第11张图片

菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第12张图片

需要注意的是:一个RoleBinding是不能授予集群级别的资源访问权限,即使他绑定的是一个ClusterRole!如下图:

菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第13张图片

(2)允许访问非资源型的URL

前面提到过了,Api-Server还会对外暴露一些非资源类型的URL,访问这些URL也必须要显示的授权,否则,Api-Server会拒绝客户端的请求。默认情况下,系统中是存在一个名为system:discovery的ClusterRole和ClusterBinding的,这个ClusterRole默认被绑定到了集群中所有已认证的用户。

我们在foo命名空间中的Pod中试着访问一些非资源类型的URL,发现是可以访问到的。
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第14张图片

下面看看集群中默认创建的ClusterRole和ClusterBinding
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第15张图片

在nonResourceURLs下列出了可以访问的非资源类型的URL

菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第16张图片

可以看到ClusterRoleBinding引用了system:discovery这个ClusterRole,同时将ClusterRole绑定到了所有认证过的用户上。

注意:Group(组)位于身份认证插件的域中。Api-Server接收到一个请求时,它会调用身份认证插件来获取用户所属组的列表,之后授权中会使用这些组的信息。

(3)使用ClusterRole来授权访问指定命名空间中的资源

ClusterRole不是必须一直和集群级别的ClusterRoleBinding捆绑使用。他们也可以和常规的有命名空间的RoleBinding进行捆绑,授权访问RoleBinding命名空间中的资源。(但是需要注意前面所说的,RoleBinding就算和ClusterRole捆绑使用了也不能授予集群级别的资源访问权限)

下面我们再来看一个系统默认定义好的ClusterRole。
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第17张图片

我们可以看到这个系统默认的view ClusterRole他的权限是,可以对resources中列出的所有资源(都是命名空间级的资源)进行一系列查看的操作。

这个ClusterRole可以和ClusterRoleBinding绑定还可以和RoleBinding绑定。会有不同的效果。

  • 和ClusterRoleBinding绑定后,在绑定中列出的主体可以在所有名称空间中查看指定的资源。
  • 和RoleBinding绑定后,那么在绑定中列出的主体只能查看在RoleBinding命名空间中的资源。

在绑定之前,我们先看看在foo命名空间的Pod中查看所有命名空间的Pods和指定命名空间下的Pods的效果。
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第18张图片

下面创建一个ClusterRoleBinding,并将他绑定到Pod的ServiceAccount上:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: view-test
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: view
subjects:
- kind: ServiceAccount
  name: default
  namespace: foo

然后我们再在这个Pod中查看foo命名空间下的Pods和所有命名空间中的Pods,发现都是可以访问到的。
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第19张图片
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第20张图片

菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第21张图片

图中展示了,我们用一个ClusterRoleBinding绑定了ClusterRole和ServiceAccount,这时这个ServiceAccount就有了访问所有命名空间资源的权限。

接下来使用ClusterRole和RoleBinding绑定,看看效果。(别忘了先删除上面创建的ClusterRoleBinding)

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: view-test
  namespace: foo     ### 别忘了给RoleBinding指定命名空间
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: view
subjects:
- kind: ServiceAccount
  name: default
  namespace: foo

现在在用这个foo命名空间下的RoleBinding将ClusterRole和他同一命名空间下的default ServiceAccount绑定到了一起。我们可以看到他仅能查看在RoleBinding命名空间中的资源。
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第22张图片
菜鸟学Kubernetes(K8s)系列——(八)关于Kubernetes的认证机制(RBAC)_第23张图片

3.4 总结Role、ClusterRole、RoleBinding、ClusterRoleBinding如何搭配使用

访问的资源 使用的角色类型 使用的绑定类型
集群级别的资源(Nodes、PVs……) ClusterRole ClusterRoleBinding
非资源型URL(/api、/healthz……) ClusterRole ClusterRoleBinding
在任何命名空间中的资源 ClusterRole ClusterRoleBinding
在具体命名空间中的资源(在多个命名空间中重用这个相同的ClusterRole) ClusterRole RoleBinding
在具体命名空间中的资源(Role必须在每个命名空间中定义好) Role RoleBinding

你可能感兴趣的:(云原生(K8s,Docker),kubernetes,云原生,Docker,运维,容器)