从外部访问Apiserver的安全端口时,客户端需要携带一个身份凭证,能够让apiserver认证为某个用户以及所属的组。
在集群的容器中,有时也需要访问apiserver。既然要访问API,那么就必须要携带身份凭证,然后能够被apiserver认证出来并进行授权。这就是要介绍的内容ServiceAccount
一、使用流程
我们简化一个场景:我现在要创建一个Pod,从Pod中去获取集群的节点列表(GET /api/v1/nodes
)。那么使用ServiceAccount如何满足这个需要呢?很简单,只需要以下四步:
1.1 创建ServiceAccount
1.2 授权ServiceAccount
创建以下的ClusterRole与ClusterRoleBinding
1.3 创建Pod,使用ServiceAccount
使用以下的yaml文件创建Pod
1.4 从Pod中访问API
在Pod中执行以下的脚本,就能够获取集群的节点列表
二、原理
接下来,我们详细地介绍一下上面每一步发生的事情,以及API请求能够被通过的原理。
注意:此内容只适用于k8s 1.22以前的版本,k8s 1.22开始,已开始变化。(见如下说明: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#manually-create-an-api-token-for-a-serviceaccount)
2.1 创建ServiceAccount
我们使用如上的命令在default
命令空间中创建了一个名字为peng
的SerivceAccount后,我们来看一下这个ServiceAccount的内容。创建之后ServiceAccount的内容如下,它有名字、命名空间,以及一个Secret
那么这个Secret是如何来的呢?答案是,对于每一个ServiceAccount,controller-manager都会为它创建一个Secret。我们来看一下这个Secret的内容,如下。我们可以看到,该Secret的类型为kubernetes.io/service-account-token
,然后data
中有三个字段,这三个字段的值都是经过base64编码的
我们来介绍一下data
中的三个字段是怎么生成的
-
ca.crt
:它来自于controller-manager的启动参数--root-ca-file
指定的文件内容 -
namespace
:该Secret的namespace,与其所属的ServiceAccount的namespace一致 -
token
:把该字段解码后,你会发现其实它是一个JWT(可以把它进行base64解码后,拷贝到 jwt.io 网站查看原始内容),它的signature加密所使用的密钥是由controller-manager的启动参数--service-account-private-key-file
所指定,它的payload如下,
2.2 创建Pod,使用ServiceAccount
我们按照前面yaml文件创建Pod,那么apiserver将会把该ServiceAccount的Secret挂载到Pod的路径/var/run/secrets/kubernetes.io/servicaccount
下。创建之后我们kubectl get
一下这个Pod,发现yaml文件如下
我们去到这个pod的内部,查看目录/var/run/secrets/kubernetes.io/servicaccount
下的内容,发现该目录下有三个文件ca.crt、namesapce与token,且它们的内容就是secret中的对应字段的值通过base64解码得到的
这个时候,我们就可以按照前面给的方法从Pod中去访问apiserver,如下
2.3 认证
apiserver收到了Pod里面的curl进程发来的请求,Header中包含了"Authoriztion: Bearer ${ServiceAccountToken}"
。那么apiserver如何认证这个ServiceAccountToken呢?首先,apiserver必须要开启serviceaccount的准入机制,即在启动参数中声明--admission-control=*,serviceaccount
。然后,然后apiserver的serviceaccount认证器拿到这个ServiceAccountToken后,利用参数--service-account-key-file
指定的公钥文件对这个token进行验证。验证通过后,该token就会被认证为subject: { kind: ServiceAccount, name: peng, namesapce: default}
以及subject: { kind: User, name: system:serviceaccount:default:peng}
2.4 授权
在第二步中,我们创建了相应的授权规则,对subject: { kind: ServiceAccount, name: peng, namesapce: default}
进行了授权,所以能够curl的请求能够通过。当然,由于该token也会被认证为subject: { kind: User, name: system:serviceaccount:default:peng}
,我们也可以对该用户授权,curl的访问也是可以通过的。