深入理解Kubernetes的认证与授权机制

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Kubernetes的认证与授权机制相关的知识,希望对你有一定的参考价值。

参考技术A

众所周知,任意一个系统的安全机制的核心都是基于 认证与授权 (Authentication and Authorization),即首先通过某种方式确认“你”的身份,再根据一定的授权策略确定“你”在我的系统里面能做什么操作。对于K8S来说,就是体现为客户端对于 kube-apiserver 的api接口操作的鉴权(客户端可以是集群内的工作负载pod,任意服务器上的kubectl程序,或是计算节点上的kubelet组件等等)。Kubernets项目作为一个成熟的开源云计算基础设施项目,让我们来看看他们是如何解决认证与授权这两个核心问题的:

k8s的用户认证机制的官方文档地址: https://kubernetes.io/docs/reference/access-authn-authz/authentication/ 。

Kubernetes中的用户类型分为 service accounts 和 normal users 两类。service accounts是k8s内部自建的一套用户认证体系,主要用于pod里直接调用kube-apiserver过程中的认证。而普通用户类型依赖于外部认证服务,k8本身不关心。

当创建service account对象时,会对应的创建一个secret对象,内含了这个用户的认证token,当pod启动时,只要yaml里申明了绑定特定的service account账号,那么系统会自动把secret的token注入到pod中的指定目录,接下来当pod调用apiserver接口时,系统都会自动的附加上这个token,这样apiserver就可以识别出这个pod的身份,结合role和rolebinding的配置信息,就可以正确的授权了。service account是基于k8内部的认证体系,使用起来比较方便,直接在集群内创建sa资源即可。此种类型的用户不是本篇文章讨论的重点,想了解具体的操作可以参考我之前的这篇文章: 构建云原生微服务网关系列-篇二:Zuul ,里面有详细的service account的使用说明。

本文讨论的重点,针对普通用户类型,很多人的理解会比较模糊,对此官网有一段说明:

也就是说,k8项目认为普通用户认证应该由外部服务供应商解决,k8本身不关心认证过程,只要告诉他最终的认证结果,即这个用户是“谁”。认证方式可以用公私钥对,或者openstack 的 keystone认证服务、google的Google Accounts服务,甚至是一个有着用户名和密码列表的文件,对于k8s来说,都是一样的。不管用何种方式去认证,最终结果都是告诉k8s,这个用户是“谁”,也就是它的用户id。这里需要注意的时,对于普通用户类型,k8是不会存储用户信息的,而对于service account类型的用户,k8会保存在etcd里面。普通用户也无法通过api调用直接创建。

Kubernetes 支持使用客户端证书、bearer token、认证代理或者http basic auth等方式进行认证,而无论使用哪种方式,认证插件都需要将以下的用户信息和http请求进行关联:

api-server目前支持的认证方式有:

使用由 k8s 根 CA 签发的证书,提取cn字段作为用户id,O字段作为用户组。我们可以使用openssl工具来进行证书的签发(kubernetes 1.4之后支持了证书中携带用户组信息):

上述操作会生成一个证书请求,username为 jbeda ,并同时属于两个用户组 app1 和 app2 。

静态token列表文件,需要预先在 API Server 服务器上放置该文件,并且在api server的启动参数中加上 --token-auth-file=SOMEFILE ,token文件为csv格式,应至少包含token, user name, user uid这三个字段(逗号分隔)以及一个可选的group names字段,例如:

注意如果用户组有多个的话,整个用户组需要用双引号括起来。

1.18版本进入稳定版的新特性,支持可以在集群启动时动态的创建和管理token,配置比较多,这里不多赘述,有兴趣直接参考官方文档

跟静态 Token 文件类似,只是使用了用户名密码的形式进行认证,使用的是http basic auth类型的认证方式,启动参数为 --basic-auth-file=SOMEFILE ,文件格式为:

很好理解,不多说了

之前介绍过了,k8s内部用户体系 Service Account 使用的 Token,认证方式也是bearer token。这里需要注意的是官方文档有一个描述:

因为api-server本身并不关注流量是从哪里过来的,所以基于service account创建的token,只要你拿到了这个token,是可以从 集群外部 发起请求的,api-server会将此请求认证为对应的service account用户。拿到token的方式官网也做了说明:

注意和serviceaccount绑定的secret类型为 kubernetes.io/service-account-token ,其中token字段即为我们需要的令牌(jwt格式),拿着这个令牌就可以直接发起请求了。 注意在secret中保存的token是经过base64编码的,实际使用时还需要先进行base64解码操作,可以使用jwt.io网站来查看这个令牌,以下是k8s签发的一个jwt令牌payload部分字段的示例:

新出来的一种认证方式,基于Oauth2,比较复杂,有兴趣可以参考官方文档,这里不介绍了。对于Oauth2认证以及JWT技术比较感兴趣的,可以参考我之前的博文 深入理解Spring Cloud Security OAuth2及JWT 。(阅读量4万多了,也算爆款了:)

搞定了认证,接下来就是授权了。得益于k8s优良的设计,认证和授权是解耦的,所以只要k8系统识别出了用户身份(username或者uid),接下来要做的事情就是一样的了。关于授权部分的官方文档地址: https://kubernetes.io/docs/reference/access-authn-authz/rbac/

事实上k8s本身也支持多种授权类型,比如rbac,abac,node,dynamic admission 等等。这里只介绍下最常用的rbac(基于角色的访问控制),实际使用中,api-server开启 --authorization-mode=RBAC 参数,即启动了rbac功能。

如果你对于rbac本身已经比较了解,那么其实k8s里面的rbac功能就非常容易理解了。涉及rbac的有两个api对象,role定义了一个角色,申明了此角色可以操作的功能列表,rolebinding其实就是把用户和角色做了一个绑定。

role的api对象示例:

这个yaml定义了一个Role对象,名称为 pod-reader , 作用域为 default 这个namespace,可以对 pods 这个对象进行 get 、 watch 、 list 操作。
kubernetes完整的操作类型列表如下,都很好理解,不一一说明了:

值得注意的是,有些资源还有子类型,比如pod的logs,如果需要查看,也是需要授权的(添加 pods/log 资源类型)

RoleBinding资源的作用也非常容易理解, 就是绑定Role和用户。下面是一个RoleBinding的示例:

这个例子里把一个类型为User,name叫做jane的用户,和pod-reader的Role做了绑定。注意subjects里面的 kind 字段,即用户类型,前面介绍过了,分别是 User 、 Group 和 ServiceAccount 。绑定完成之后,当使用 jane 这个用户身份对k8s的api进行调用,就可以进行指定的 watch 、 get 、 list 操作了。

这两资源其实和Role、RoleBinding的配置方式是完全一样的,区别在于ClusterRole和ClusterRoleBinding的作用域是集群范围的,并且可以操作 node 这样的集群范围资源,而Role、RoleBinding在metadata中需要指定namespace字段,其他方面没有区别。

弄清原理之后,现在让我们来实际操作一下,目标是使用kubectl客户端工具对于给定的k8集群进行受限操作。基于上述的认证策略的介绍,我们使用 客户端证书 方式来进行用户认证,即使用K8集群的根证书来签发一个用户证书,使用该证书来进行用户认证及授权操作。

关于RSA证书的制作,可以参考官网文档: https://kubernetes.io/docs/concepts/cluster-administration/certificates/ ,这里我们使用常用的openssl工具来制作证书:

1、创建一个2048位长度的RSA格式私钥

2、创建证书签名请求(csr),CN-对应Username O-对应用户组,上面的文章中已经介绍过

3、使用集群根证书签发这个证书请求(days是证书到期时间,可根据实际需要配置)

首先先找到一台准备作为客户端访问k8集群的linux服务器(或者windows、mac都可以),确保客户端与集群的api-server端口网络联通(一般为6443端口,注意必须是https连接),出于安全考虑,最好开一个操作k8的专用的操作系统账号。把集群master节点中的kubectl二进制文件拷贝至此服务器/usr/bin目录内,同时拷贝release.csr、release.key、ca.pem这三个文件至服务器上的指定目录。

在新建用户的home目录下创建.kube目录,在此目录下新建config文件(或者直接执行kubectl config set-cluster test操作,kubectl会自动创建该文件),编辑该文件填写如下内容:

完成之后可以执行 kubectl config view 来验证一下配置是否正确。

使用管理员登陆k8集群,进行权限配置,这里以添加集群范围的运维用户权限为例:

可以看到,我们定义了一个角色 release ,用于应用的部署及日常运维操作。为了满足日常运维,给其分配了一组受限的资源权限。

具体来说,该角色对"deployments","services","configmap","pvc"资源有全部的操作权限,对于"nodes","events","pods","pods/log","endpoints"只有查看权限,对于其他资源没有任何权限。

这里我们定义了一个ClusterRoleBinding,把User和ClusterRole进行了绑定,到这里全部操作就完成了。

登陆客户端,一切顺利的话,执行 kubectl get pods 就可以返回远程集群的default命名空间下的pods列表了,其他配置的权限应该也可以正常操作。而如果这个用户想访问受限资源,比如想查看secrets信息,则会出现如下的报错信息(403 Forbidden):

验证成功!

基于上述的描述,可以知道,其实在集群里面创建一个service account,把它的token拷贝出来,配置在客户端的kubectl配置文件中,可以达到同样的效果,这里就不再演示了。

因为service account的token机密信息实际上都是存放于secret对象中,而secret经常被人吐槽的是存放的数据是明文(只是做了base64编码),所以这里多说一句secret的安全性问题。其实k8s是支持secret加密存放的,支持的加密类型还挺多,具体可以看我这篇文章: 使用加密插件加密secrets中的数据 。但其实我并不建议使用这种方式,原因是使用加密插件只能加密存放在etcd里面的数据,而使用api server调取出的数据仍然是解密过后的,这意味着你执行 kubectl get secrets 或者是进入容器的环境变量查看,仍然可以看到明文数据。k8s项目更推荐的权限管理方式是:

做好上面两点,对于一般公司的安全管控来讲已经足够,毕竟集群管理员的权限只是掌握在很小一部分人的手中。而对于安全审计要求更高的企业(比如金融机构),审计可能会有要求敏感数据必须加密存放,此时可使用api-server的加密插件。

云原生 | 从零开始学Kubernetes二十八完结篇—rbac授权深入讲解

该篇文章已经被专栏《从零开始学k8s》收录
上一篇文章:配置管理中心Secret和rbac授权 点击跳转


rbac授权


上一篇文章的下部分

1、ServiceAccount

Service account 是为了方便 Pod 里面的进程调用 Kubernetes API 或其他外部服务而设计的。它与 User account 不同,User account 是为人设计的,而 service account 则是为 Pod 中的进程调用 Kubernetes API 而设计;User account 是跨 namespace 的,而 service account 则是仅局限它所在的 namespace;每个 namespace 都会自动创建一个 default service account; 开启 ServiceAccount Admission Controller 后

1)每个 Pod 在创建后都会自动设置 spec.serviceAccount 为 default(除非指定了其他 ServiceAccout)

2)验证 Pod 引用的 service account 已经存在,否则拒绝创建;

当创建 pod 的时候,如果没有指定一个 serviceaccount,系统会自动在与该 pod 相同的 namespace 下为其指派一个 default service account。这是 pod 和 apiserver 之间进行通信的账号,如下:

[root@k8smaster secret]# kubectl get pods
NAME                               READY   STATUS              RESTARTS   AGE
mysql-pod-volume                   1/1     Running             0          50m
nginx-f89759699-5qdtn              1/1     Running             1          19d
pod-secret                         1/1     Running             0          17m
pod-secret-volume                  1/1     Running             0          11m
[root@k8smaster secret]# kubectl get pods mysql-pod-volume -o yaml | grep "serviceAccountName"
  serviceAccountName: default
[root@k8smaster secret]# kubectl describe pods mysql-pod-volume
Volumes:
  mysql-config:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      mysql
    Optional:  false
  default-token-788ff:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-788ff
    Optional:    false

 
从上面可以看到每个 Pod 无论定义与否都会有个存储卷,这个存储卷为 default-token-***。pod 和 apiserver 的认证信息通过 secret 进行定义,由于认证信息属于敏感信息,所以需要保存在 secret 资源当中,并以存储卷的方式挂载到 Pod 当中。从而让 Pod 内运行的应用通过对应的 secret 中的信息来连接 apiserver,并完成认证。每个 namespace 中都有一个默认的叫做 default 的 serviceaccount 资源。查看名称空间内的 secret,也可以看到对应的 default-token。让当前名称空间中所有的 pod 在连接 apiserver 时可以使用的预制认证信息,从而保证 pod 之间的通信。
 
[root@k8smaster secret]# kubectl get sa
NAME              SECRETS   AGE
default           1         19d

[root@k8smaster secret]# kubectl get secret
NAME                          TYPE                                  DATA   AGE
default-token-788ff           kubernetes.io/service-account-token   3      19d

默认的 service account 仅仅只能获取当前 Pod 自身的相关属性,无法观察到其他名称空间 Pod 的相关属性信息。如果想要扩展 Pod,假设有一个 Pod 需要用于管理其他 Pod 或者是其他资源对象,是无法通过自身的名称空间的 serviceaccount 进行获取其他 Pod 的相关属性信息的,此时就需要进行手动创建一个 serviceaccount,并在创建 Pod 时进行定义。那么 serviceaccount 该如何进行定义呢?实际上,service accout 也属于一个 k8s 资源,serviceAccount 也属于标准的 k8s 资源,可以创建一个 serviceAccount,创建之后由我们创建的 pod 使用 serviceAccountName 去加载自己定义的 serviceAccount 就可以了,如下:

[root@k8smaster secret]# kubectl create sa paopao
serviceaccount/paopao created
[root@k8smaster secret]# kubectl get sa
NAME              SECRETS   AGE
default           1         19d
nfs-provisioner   1         47h
paopao            1         3s
[root@k8smaster secret]# kubectl describe sa paopao
Name:                paopao
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   paopao-token-g72jh
Tokens:              paopao-token-g72jh			#自动生成token
Events:              <none>
[root@k8smaster secret]# kubectl get secret
NAME                          TYPE                                  DATA   AGE
default-token-788ff           kubernetes.io/service-account-token   3      19d
mysecret                      Opaque                                2      27m
mysql-password                Opaque                                1      40m
nfs-provisioner-token-tv5dl   kubernetes.io/service-account-token   3      2d
paopao-token-g72jh            kubernetes.io/service-account-token   3      31s		#自动生成secret
[root@k8smaster secret]# kubectl describe secret paopao-token-g72jh 
Name:         paopao-token-g72jh
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: paopao
              kubernetes.io/service-account.uid: 74643ebf-6198-4b3a-9aee-ea0da0e6b1a8

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  7 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6ImpCVzZXa0U2WDczX0NMeU1yUDM5YkluTWVPdlNFbUp0UnF3aVNnVzhXTkUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InBhb3Bhby10b2tlbi1nNzJqaCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJwYW9wYW8iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI3NDY0M2ViZi02MTk4LTRiM2EtOWFlZS1lYTBkYTBlNmIxYTgiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpwYW9wYW8ifQ.IzS3gXVOMXlKWSyVk-4Xh04351XkoG5axWba1fOwhrFLMNRT2RdMm3LRvvki_WIuL8prvGNerMt4cG3YzRIgLMTZ1Rk3BtLzo3HY9D4qr4FxZxXdFsqd87kHG5_ExpqzAHn__anL5758XMvsuQCwf-k6wRktbGOIDb6jXZuCVViRyomWWPJaLIBXa5BBlXHKft9mXuM6--293w21-RPTW29tMaIVf18bWXU3OpaPFiULtpUB3faqqmS3H7dKrbwn1uhSwmp4mxU1ossXCcPAdxL-Shh9L3yZ6fTuZTDxgmKb9yF6twVwcmuLr5HFZ3a5Tr_TVI91Z6IBXYlnu0bYuw	#可以看到有这个

上面可以看到生成了 paopao-token-g72jh 的 token 详细信息,这个 token 就是 sa 连接 apiserver 的认证信息,这个 token 也是登陆 k8s dashboard 的 token,这些是一个认证信息,能够登陆k8s,能认证到 k8s,但是不能做别的事情,不代表权限,想要做其他事情,需要授权
#比如在pod部署一个prometheus,如果要获取k8s监控指标要和apiserver交互,不然无法获取,这个时候需要生成sa,做一个rbac授权,比如可以对k8s所有资源下的所有名称空间都访问,才能获取到k8s资源

2、kubeconfig 文件

在 K8S 集群当中,每一个用户对资源的访问都是需要通过 apiserver 进行通信认证才能进行访问的,那么在此机制当中,对资源的访问可以是 token,也可以是通过配置文件的方式进行保存和使用认证信息,可以通过 kubectl config 进行查看配置,如下:

[root@k8smaster secret]# kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://192.168.11.139:6443	#apiserver 的地址
  name: kubernetes						#集群的名字
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes		#上下文的名字
current-context: kubernetes-admin@kubernetes	#当前上下文的名字
kind: Config
preferences: 
users:
- name: kubernetes-admin
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED
在上面的配置文件当中,定义了集群、上下文以及用户。其中 Config 也是 K8S 的标准资源之一,在该配置文件当中定义了一个集群列表,指定的集群可以有多个;用户列表也可以有多个,指明集群。而在上下文列表当中,是进行定义可以使用哪个用户对哪个集群进行访问,以及当前使用的上下文是什么。

授权

如果用户通过认证,什么权限都没有,需要一些后续的授权操作,如对资源的增删该查等, kubernetes1.6 之后开始有 RBAC(基于角色的访问控制机制)授权检查机制。 Kubernetes 的授权是基于插件形成的,其常用的授权插件有以下几种:

1)Node(节点认证)

2)ABAC(基于属性的访问控制)

3)RBAC(基于角色的访问控制)

4)Webhook(基于 http 回调机制的访问控制)

什么是 RBAC(基于角色的访问控制)?

让一个用户(Users)扮演一个角色(Role),角色拥有权限,从而让用户拥有这样的权限,随后在 授权机制当中,只需要将权限授予某个角色,此时用户将获取对应角色的权限,从而实现角色的访问 控制。如图:

在 k8s 的授权机制当中,采用 RBAC 的方式进行授权,其工作逻辑是,把对对象的操作权限定义到 一个角色当中,再将用户绑定到该角色,从而使用户得到对应角色的权限。如果通过 rolebinding 绑定 role,只能对 rolebingding 所在的名称空间的资源有权限,上图 user1 这个用户绑定到 role1 上,只对 role1 这个名称空间的资源有权限,对其他名称空间资源没有权限,属于名称空间级别的;

另外,k8s 为此还有一种集群级别的授权机制,就是定义一个集群角色(ClusterRole),对集群内的所有资源都有可操作的权限,从而将 User2 通过 ClusterRoleBinding 到 ClusterRole,从而使 User2 拥有集群的操作权限。

Role、RoleBinding、ClusterRole 和 ClusterRoleBinding 的关系如下图:

通过上图可以看到,可以通过 rolebinding 绑定 role,rolebinding 绑定 clusterrole, clusterrolebinding 绑定 clusterrole。

上面我们说了两个角色绑定:

(1)用户通过 rolebinding 绑定 role

(2)用户通过 clusterrolebinding 绑定 clusterrole

还有一种:rolebinding 绑定 clusterrole

rolebinding 绑定 clusterrole 的好处:
假如有 6 个名称空间,每个名称空间的用户都需要对自己的名称空间有管理员权限,那么需要定义6个 role 和 rolebinding,然后依次绑定,如果名称空间更多,我们需要定义更多的 role,这个是很麻烦的,所以我们引入 clusterrole,定义一个 clusterrole,对 clusterrole 授予所有权限,然后用户通过 rolebinding 绑定到 clusterrole,就会拥有自己名称空间的管理员权限了

注:RoleBinding 仅对当前名称空间有对应的权限。

准入控制

一般而言,准入控制只是用来定义我们授权检查完成之后的后续的其他安全检查操作的,进一步补充了授权机制,由多个插件组合实行,一般而言在创建,删除,修改或者做代理时做补充。

Kubernetes 的 Admission Control 实际上是一个准入控制器(Admission Controller)插件列表,发送到 APIServer 的请求都需要经过这个列表中的每个准入控制器插件的检查,如果某一个控制器插件准入失败,就准入失败。

控制器插件如下:

AlwaysAdmit:允许所有请求通过

AlwaysPullImages:在启动容器之前总是去下载镜像,相当于每当容器启动前做一次用于是否有权使用该容器镜像的检查

AlwaysDeny:禁止所有请求通过,用于测试

DenyEscalatingExec:拒绝 exec 和 attach 命令到有升级特权的 Pod 的终端用户访问。如果集中包含升级特权的容器,而要限制终端用户在这些容器中执行命令的能力,推荐使用此插件

ImagePolicyWebhook ServiceAccount:这个插件实现了 serviceAccounts 等等自动化,如果使用 ServiceAccount 对象,强烈推荐使用这个插件

SecurityContextDeny:将 Pod 定义中定义了的 SecurityContext 选项全部失效。

SecurityContext 包含在容器中定义了操作系统级别的安全选型如 fsGroup,selinux 等选项

ResourceQuota:用于 namespace 上的配额管理,它会观察进入的请求,确保在 namespace 上的配额不超标。推荐将这个插件放到准入控制器列表的最后一个。ResourceQuota 准入控制器既可以限制某个 namespace 中创建资源的数量,又可以限制某个 namespace 中被 Pod 请求的资源总量。ResourceQuota 准入控制器和 ResourceQuota 资源对象一起可以实现资源配额管理。

LimitRanger:用于 Pod 和容器上的配额管理,它会观察进入的请求,确保 Pod 和容器上的配额不会超标。准入控制器 LimitRanger 和资源对象 LimitRange 一起实现资源限制管理

NamespaceLifecycle:当一个请求是在一个不存在的 namespace 下创建资源对象时,该请求会被拒绝。当删除一个 namespace 时,将会删除该 namespace 下的所有资源对象

DefaultStorageClass
DefaultTolerationSeconds
PodSecurityPolicy
当 Kubernetes 版本>=1.6.0,官方建议使用这些插件:
–admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds
当 Kubernetes 版本>=1.4.0,官方建议使用这些插件:
–admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota
以上是标准的准入插件,如果是自己定制的话,k8s1.7版 出了两个 alpha features, Initializers 和 External Admission Webhooks

RBAC 认证授权策略

RBAC 介绍

在 Kubernetes 中,所有资源对象都是通过 API 进行操作,他们保存在 etcd 里。而对 etcd 的操作我们需要通过访问 kube-apiserver 来实现,上面的 Service Account 其实就是 APIServer 的认证过程,而授权的机制是通过 RBAC:基于角色的访问控制实现。

RBAC 有四个资源对象,分别是 Role、ClusterRole、RoleBinding、ClusterRoleBinding

Role:角色

一组权限的集合,在一个命名空间中,可以用其来定义一个角色,只能对命名空间内的资源进行授权。如果是集群级别的资源,则需要使用 ClusterRole。
例如:定义一个角色用来读取 Pod 的权限
apiVersion: rbac.authorization.k8s.io/v1		#认证相关的api
kind: Role
metadata:
  namespace: rbac 								#只能对rbac这个名称空间才有权限
  name: pod-read								
rules:											#策略
  - apiGroups: [""]								#k8s所有的apiversion都支持 kubectl api-versions查看
    resources: ["pods"] 						#资源名字
    resourceNames: []							#如果需要具体的pod就写具体的 不写就是所有的
    verbs: ["get","watch","list"]				#具体的步骤吗,获取,查看,以列表列出来
rules 中的参数说明:
1、apiGroups:支持的 API 组列表,例如:"apiVersion: batch/v1"2、resources:支持的资源对象列表,例如 pods、deplayments、jobs 等
3、resourceNames: 指定 resource 的名称
4、verbs:对资源对象的操作方法列表。

ClusterRole:集群角色

具有和角色一致的命名空间资源的管理能力,还可用于以下特殊元素的授权
1、集群范围的资源,例如 Node
2、非资源型的路径,例如:/healthz
3、包含全部命名空间的资源,例如 Pods
例如:定义一个集群角色可让用户访问任意 secrets
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: secrets-clusterrole
rules:
  - apiGroups: [""]
    resources: ["secrets"]			#对所有名称空间下的secrets都有查看的权限
    verbs: ["get","watch","list"]

RoleBinding:角色绑定、ClusterRolebinding:集群角 色绑定

角色绑定和集群角色绑定用于把一个角色绑定在一个目标上,可以是 User,Group,Service Account,使用 RoleBinding 为某个命名空间授权,使用 ClusterRoleBinding 为集群范围内授权。
例如:将在 rbac 命名空间中把 pod-read 角色授予用户 es
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-read-bind
  namespace: rbac
subjects:									#主体
- kind: User							
  name: es									#要对es这个用户
  apiGroup: rbac.authorization.k8s.io
roleRef:									#通过 pod-read-bind 绑定到roleref上
- kind: Role
  name: pod-read							#角色名字
  apiGroup: rbac.authorizatioin.k8s.io
#用户es在rbac这个命名空间下具有role设置的权限,比如资源查看

RoleBinding 也可以引用 ClusterRole,对属于同一命名空间内的 ClusterRole 定义的资源主体进行授权, 例如:es 能获取到集群中所有的资源信息
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: es-allresource
  namespace: rbac
subjects:
- kind: User
  name: es								#通过RoleBinding绑定到cluster-admin这个ClusterRole上 管理员的角色
  apiGroup: rbac.authorization.k8s.io
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin

集群角色绑定的角色只能是集群角色,用于进行集群级别或对所有命名空间都生效的授权
例如:允许 manager 组的用户读取所有 namaspace 的 secrets
apiVersion: rabc.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: read-secret-global
subjects:
- kind: Group
  name: manager							#通过ClusterRoleBinding绑定到secret-read这个ClusterRole
  apiGroup: rabc.authorization.k8s.io
ruleRef:
- kind: ClusterRole
  name: secret-read						#读的权限
  apiGroup: rabc.authorization.k8s.io

资源的引用方式

多数资源可以用其名称的字符串表示,也就是 Endpoint 中的 URL 相对路径
例如 pod 中的日志是GET /api/v1/namaspaces/namespace/pods/podname/log

如果需要在一个 RBAC 对象中体现上下级资源,就需要使用“/”分割资源和下级资源。
例如:若想授权让某个主体同时能够读取 Pod 和 Pod log,则可以配置 resources 为一个数组
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: logs-reader
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods","pods/log"]				#对pods和podslog下的资源可以读取
  verbs: ["get","list"]

资源还可以通过名称(ResourceName)进行引用,在指定 ResourceName 后,使用 get、delete、update、patch 请求,就会被限制在这个资源实例范围内
例如,下面的声明让一个主体只能对名为 my-configmap 的 Configmap 进行 get 和 update 操作:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namaspace: default
  name: configmap-update
rules:
- apiGroups: [""]
  resources: ["configmap"]
  resourceNames: ["my-configmap"]				#只能对这个名字my-configmap进行get和更新操作
  verbs: ["get","update"]

常见角色示例

(1)允许读取核心 API 组的 Pod 资源

rules:
- apiGroups: [""]					#读取所有apiversion的pods的资源可以查看信息等
  resources: ["pods"]
  verbs: ["get","list","watch"]

(2)允许读写 extensions 和 apps 两个 API 组中的 deployment 资源 rules:

- apiGroups: ["extensions","apps"]									#组名,apiversion下也能看到组
  resources: ["deployments"]										#对上面两个组下的deplo资源有下面的权限
  verbs: ["get","list","watch","create","update","patch","delete"]  #查看 更新 删除等

(3)允许读取 Pod 以及读写 job 信息

rules:
- apiGroups: [""]														#所有核心api
  resources: ["pods"]													#读取pods资源
  verbs: ["get","list","watch"]、
- apiVersion: ["batch","extensions"]									#能对这两个组的jobs资源删除更新等
  resources: ["jobs"]	
  verbs: ["get","list","watch","create","update","patch","delete"]

(4)允许读取一个名为 my-config 的 ConfigMap(必须绑定到一个 RoleBinding 来限制到一个 Namespace 下的 ConfigMap):

rules:
- apiGroups: [""]					#所有api
  resources: ["configmap"]			#获取configmap资源
  resourceNames: ["my-configmap"]	#为资源取得名字,configmap可能很多,只能查看my-configmap这个configmap
  verbs: ["get"]

(5)读取核心组的 Node 资源(Node 属于集群级的资源,所以必须存在于 ClusterRole 中,并使用 ClusterRoleBinding 进行绑定)

rules:		
- apiGroups: [""]	
  resources: ["nodes"]				
  verbs: ["get","list","watch"]

(6)允许对非资源端点“/healthz”及其所有子路径进行 GET 和 POST 操作(必须使用 ClusterRole 和 ClusterRoleBinding):

rules:
- nonResourceURLs: ["/healthz","/healthz/*"]	#定义两个具体路径
  verbs: ["get","post"]

常见的角色绑定示例

(1)用户名 alice
subjects:
- kind: User
  name: alice
  apiGroup: rbac.authorization.k8s.io
 
(2)组名 alice
subjects:
- kind: Group
  name: alice
  apiGroup: rbac.authorization.k8s.io
  
(3)kube-system 命名空间中默认 Service Account
subjects:
- kind: ServiceAccount
  name: default
  namespace: kube-system

(4)qa 命名空间中的所有 Service Account:
subjects:
- kind: Group
  name: system:serviceaccounts:qa			#qa名称空间下所有的sa资源
  apiGroup: rbac.authorization.k8s.io

(5)所有 Service Account
subjects:
- kind: Group
  name: system:serviceaccounts				#所有的sa
  apiGroup: rbac.authorization.k8s.io
 
(6)所有认证用户
subjects:
- kind: Group
  name: system:authenticated				#同
  apiGroup: rbac.authorization.k8s.io
 
(7)所有未认证用户
subjects:
- kind: Group
  name: system:unauthenticated				#同
  apiGroup: rbac.authorization.k8s.io		
 
(8)全部用户
subjects:
- kind: Group
  name: system:authenticated				#包括认证与未认证用户
  apiGroup: rbac.authorization.k8s.io
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

对 Service Account 的授权管理

Service Account 也是一种账号,是给运行在 Pod 里的进程提供了必要的身份证明。需要在 Pod 定义中指明引用的 Service Account,这样就可以对 Pod 进行赋权操作。例如:pod 内可获取 rbac 命名空间的所有 Pod 资源,pod-reader-sc 的 Service Account 是绑定了名为 pod-read 的 Role

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: rbac
spec:
  serviceAccountName: pod-reader-sc		#指定的sc 绑定到rbac命名空间下 可以读取pods
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80
默认的 RBAC 策略为控制平台组件、节点和控制器授予有限范围的权限,但是除 kube-system 外的 Service Account 是没有任何权限的。

1 为一个应用专属的 Service Account 赋权
此应用需要在 Pod 的 spec 中指定一个 serviceAccountName。
用于API,Application,Manifest,kubectl create serviceaccount 等创建 Service Account 的命令。

例如为 my-namespace 中的 my-sa Service Account 授予只读权限
kubectl create rolebinding my-sa-view --clusterrole=view --serviceaccount=my-namespace:my-sa --namespace=my-namespace
kubectl 创建 rolebinding rbd 的名字 --service是my-namespace这个命名空间下通过rolebinding绑定到clusterrole上,view是安装k8s的时候默认的角色 只读权限。

2 为一个命名空间中名为 default 的 Service Account 授权
如果一个应用没有指定 serviceAccountName,则会使用名为 default 的 Service Account。
注意,赋予 Service Account “default”的权限会让所有没有指定 serviceAccountName 的 Pod都具有这些权限

例如,在 my-namespace 命名空间中为 Service Account“default”授予只读权限
kubectl create rolebinding default-view --clusterrole=view --serviceaccount=my-namespace:default --namespace=my-namespace

另外,许多系统级 Add-Ons(附加组件)都需要在 kube-system 命名空间中运行,要让这些 Add-Ons 能够使用超级用户权限,则可以把 cluster-admin 权限赋予 kube-system 命名空间中名为 default 的 Service Account,这一操作意味着 kube-system 命名空间包含了通向 API 超级用户的捷径。
kubectl create clusterrolebinding add-ons-add-admin --clusterrole=cluster-admin --serviceaccount=kube-system:default

(3)为命名空间中所有 Service Account 都授予一个角色
如果希望在一个命名空间中,任何 Service Account 应用都具有一个角色,则可以为这一命名空间的 Service Account 群组进行授权
kubectl create rolebinding serviceaccounts-view --clusterrole=view --group=system:serviceaccounts:my-namespace --namespace=my-namespace

(4)为集群范围内所有 Service Account 都授予一个低权限角色
如果不想为每个命名空间管理授权,则可以把一个集群级别的角色赋给所有 Service Account。 
kubectl create clusterrolebinding serviceaccounts-view --clusterrole=view --group=system:serviceaccounts
让所有sa都具有查看的权限,创建pod时用任何一个sa就只有查看的权限

(5)为所有 Service Account 授予超级用户权限
kubectl create clusterrolebinding serviceaccounts-view --clusterrole=cluster-admin --group=system:serviceaccounts

使用 kubectl 命令行工具创建资源对象

(1)在命名空间 rbac 中为用户 es 授权 admin ClusterRole:
kubectl create rolebinding bob-admin-binding --clusterrole=admin --user=es --namespace=rbac
#对于mes这个用户,他对rbac这个命名空间具有admin权限,通过rolebinding绑定到admin这个ClusterRole上
 
(2)在命名空间 rbac 中为名为 myapp 的 Service Account 授予 view ClusterRole:
kubctl create rolebinding myapp-role-binding --clusterrole=view --serviceaccount=rbac:myapp --namespace=rbac
#对于myapp这个sa用户,他对rbac这个命名空间就只有查看的权限,通过rolebinding绑定到admin这个ClusterRole上
 
(3)在全集群范围内为用户 root 授予 cluster-admin ClusterRole:
kubectl create clusterrolebinding cluster-binding --clusterrole=cluster-admin --user=root
#对于root这个用户,对所有的名称空间都具有管理员权限,通过clusterrolebinding绑定到admin这个ClusterRole上

(4)在全集群范围内为名为 myapp 的 Service Account 授予 view ClusterRole:
kubectl create clusterrolebinding service-account-binding --clusterrole=view --serviceaccount=myapp
#对于myapp这个sa用户,对所有的名称空间都具有查看的权限,通过clusterrolebinding绑定到view这个ClusterRole上

yaml 文件进行 rbac 授权:https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac

限制不同的用户操作 k8s 集群

ssl 认证 生成一个证书 
1 生成一个私钥 
[root@k8smaster secret]# cd /etc/kubernetes/pki/ 
[root@k8smaster pki]# (umask 077; openssl genrsa -out paopao.key 2048) 
Generating RSA private key, 2048 bit long modulus
.......................................................+++
.....................................................+++
e is 65537 (0x10001)

2 生成一个证书请求,并且生成证书
[root@k8smaster pki]# openssl req -new -key paopao.key -out paopao.csr -subj "/CN=paopao" 
[root@k8smaster pki]# openssl x509 -req -in paopao.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out paopao.crt -days 3650 					用ca这个机构颁发证书,paopao.crt这个证书是被ca.crt这个apiservion信任的
Signature ok
subject=/CN=paopao
Getting CA Private Key

在 kubeconfig 下新增加一个 lucky 这个用户 
1 把 paopao 这个用户添加到 kubernetes 集群中,可以用来认证 apiserver 的连接 
[root@k8smaster pki]# kubectl config set-credentials paopao --client-certificate=./paopao.crt --client-key=./paopao.key --embed-certs=true 
User "paopao" set.
#添加了一个用户paopao,用的客户端证书是paopao.crt,key是paopao.key。

[root@k8smaster pki]# vim /root/.kube/config	#不修改 进去查看刚才创建的用户
- name: paopao
  user:
 
2 在 kubeconfig 下新增加一个 lucky 这个账号 
[root@k8smaster pki]# kubectl config set-context paopao@kubernetes --cluster=kubernetes --user=paopao
Context "paopao@kubernetes" created.
这个上下文包括了k8s集群,用户是paopao

[root@k8smaster pki]# vim /root/.kube/config	#不修改 进去查看刚才创建的
- context:
    cluster: kubernetes
    user: paopao
  name: paopao@kubernetes
current-context: kubernetes-admin@kubernetes

3 切换账号到 paopao,默认没有任何权限 
[root@k8smaster pki]# kubectl config use-context paopao@kubernetes 
Switched to context "paopao@kubernetes".
[root@k8smaster pki]# kubectl get pods
Error from server (Forbidden): pods is forbidden: User "paopao" cannot list resource "pods" in API group "" in the namespace "default"
#没有权限!
[root@k8smaster pki]# kubectl config use-context kubernetes-admin@kubernetes 这个是集群用户,有所有权限 
把 user 这个用户通过 rolebinding 绑定到 clusterrole 上,授予权限,权限只是在 paopao 这个名称空间有效 

1 把 lucky 这个用户通过 rolebinding 绑定到 clusterrole 上 
[root@k8smaster ~]# kubectl create ns paopao 
namespace/paopao created
[root@k8smaster ~]# kubectl create rolebinding paopao -n paopao --clusterrole=cluster-admin --user=paopao
rolebinding.rbac.authorization.k8s.io/paopao created

2 测试是否有权限 
[root@k8smaster ~]# kubectl config use-context paopao@kubernetes 
Switched to context "paopao@kubernetes".
[root@k8smaster ~]# kubectl get pods					没有权限操作其他名称空间 
Error from server (Forbidden): pods is forbidden: User "paopao" cannot list resource "pods" in API group "" in the namespace "default"
[root@k8smaster ~]# kubectl get pods -n paopao			有权限操作这个名称空间 
No resources found in paopao namespace.

添加一个 paopao 的普通用户 
[root@k8smaster ~]# useradd paopao
[root@k8smaster ~]# cp /root/.kube/config /root/testconfig
[root@k8smaster ~]# vim /root/testconfig
#删除以下内容
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes

- name: kubernetes-admin
  user:
    client-certificate-data: 
#下面的两大行密钥也删除

[root@k8smaster ~]# cp -ar /root/.kube/ /home/paopao/ 
[root@k8smaster ~]# cp -ar /root/testconfig /home/paopao/.kube/config 
cp: overwrite ‘/home/paopao/.kube/config’? y
[root@k8smaster ~]# rm -f /home/paopao/.kube/testconfig 
[root@k8smaster ~]# chown -R paopao.paopao /home/paopao/ 
[root@k8smaster ~]# su - paopao
Last login: Sat Jul 16 00:47:20 PDT 2022 on pts/0
[paopao@k8smaster ~]$ kubectl get pods -n paopao
No resources found in paopao namespace.
#只能查看paopao这个命名空间

写在最后

创作不易,如果觉得内容对你有帮助,麻烦给个三连关注支持一下我!如果有错误,请在评论区指出,我会及时更改!

目前正在更新的系列:待定

感谢各位的观看,文章掺杂个人理解,如有错误请联系我指出~

以上是关于深入理解Kubernetes的认证与授权机制的主要内容,如果未能解决你的问题,请参考以下文章

云原生 | 从零开始学Kubernetes二十八完结篇—rbac授权深入讲解

深入理解k8s operator 机制

深入理解k8s operator 机制

深入详解windows安全认证机制ntlm&Kerberos

深入理解跨域SSO(单点登录)原理与技术

#云原生征文#深入Kubernetes(k8s)概念