Service Account
是一种账号,但并不是给Kubernetes
的集群的用户(系统管理员、运维人员、租户用户等),而是给运行在Pod里的进程用的,它为Pod里的进程提供必要的身份证明。
Pod 中访问 Kubernetes API Server 服务的时候,是以 Service 方式访问服务名为 kubernetes 这个服务的,而kubernetes服务又只在 HTTPS 安全端口 443 上提供服务,那么如何进行身份认证呢?
通过查看源码获知这是在用一种类似HTTP Token
的新的认证方式--ServiceAccount Auth
,Pod中的客户端调用Kubernetes API的时候,在HTTP Header
中传递了一个 Token 字符串,这类似于之前提到的HTTP Token
认证方式,存在以下几个不同点:
此处的Token的内容来源于Pod里指定路径下的一个文件(
/run/secrets/kubernetes.io/serviceaccount/token
),这种token是动态生成的,确切的说,是由KubernetesController进程用API Server的私钥(--service-account-private-key-file
指定的私钥)签名生成的一个JWT Secret。官方提供的客户端REST框架代码里,通过HTTPS方式与API Server建立链接后,会用Pod里指定路径下的一个CA证书(
/run/secrets/kubernetes.io/serviceaccount/ca.crt
)验证API Server发来的证书,验证是否是被CA证书签名的合法证书。API Server收到这个Token以后,采用自己的私钥(实际是使用参数service-account-key-file指定的私钥,如果此参数没有设置,则默认采用tls-private-key-file指定的参数,即自己的私钥),对token进行合法性验证。
明白原理之后。接下来分析认证过程中涉及的Pod中的三个文件:
- /run/secrets/kubernetes.io/serviceaccount/token
- /run/secrets/kubernetes.io/serviceaccount/ca.crt
- /run/secrets/kubernetes.io/serviceaccount/namespace(客户端采用这里指定的namespace作为参数调用Kubernetes API)
这三个文件由于参与到Pod进程与API Server认证的过程中,起到了类似Secret(私密凭据)的作用,所以他们被称为Kubernetes Secret对象。Secret从属于ServiceAccount资源对象,属于Service Account的一部分,一个ServiceAccount对象里面可以包括多个不同的Secret对象,分别用于不同目的的认证活动。
下面通过命令来直观的加深对ServiceAccount的认识:
查看系统中ServiceAccount对象,可以看到一个名为default的Service Account对象,包含一个名为default-token-xxx的Secret,这个Secret同时是“Mountable secrets”,表明他是需要被Mount到Pod上的。
- kubectl describe serviceaccounts
- kubectl describe secrets default-token-xxx
default-token-xxx包括三个数据项:
- token
- ca.crt
- namespace
联想到“Mountable secrets”的标记,以及之前看到的Pod中的三个文件的文件名:每个namespace下有一个名为default的默认的ServiceAccount对象,这个ServiceAccount里有一个名为Tokens的可以作作为Volume一样被Mount到Pod里的Secret,当Pod启动时这个Secret会被自动Mount到Pod的指定目录下,用来协助完成Pod中的进程访问API Server时的身份鉴权过程。
一个ServiceAccount可以包括多个Secrets对象:
名为Tokens的Secret用于访问API Server的Secret,也被称为ServiceAccountSecret;
名为Image Pull secrets的Secret用于下载容器镜像时的认证过程,通常镜像库运行在Insecure模式下,所以这个Secret为空;
用户自定义的其他Secret,用于用户的进程;
如果一个Pod在定义时没有指定spec.service.AccountName属性,则系统会自动为其赋值为“Default”,即使用同一namespace下默认的ServiceAccount,如果某个Pod需要使用非default的ServiceAccount,需要在定义时指定:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mycontainer
image: null
serviceAccountName: myserviceaccount
Kubernetes之所以要创建两套独立的账号系统,原因如下:
- User账号是给人用的,ServiceAccount是给Pod里的进程使用的,面向对象不同;
- User账号是全局性的,ServiceAccount则属于某个具体的Namespace;
- 通常来说,User账号是与后端的用户数据库同步的,创建一个新用户通常要走一套复杂的业务流程才能实现,ServiceAccount的创建则需要极轻量级实现方式,集群管理员可以很容易为某些特定任务组创建一个ServiceAccount。
- 对于这两种不同的账户,其审计要求通常不同;
- 对于一个复杂的系统来说,多个组件通常拥有各种账号的配置信息,ServiceAccount是Namespace隔离的,可以针对组件进行一对一的定义,同时具备很好的“便携性”。
下面深入分析Service Account与Secret相关的一些运行机制:
Controller manager创建了ServiceAccountController与Token Controllerl两个安全相关的控制器。
其中ServiceAccountController一直监听Service Account和Namespace的事件,如果一个Namespace中没有default Service Account,那么Service Account Controller就会为该Namespace创建一个默认的(default)的Service Account,这就是我们之前看到的每个namespace下都有一个名为default的ServiceAccount的原因。
如果Controller manager进程在启动时指定了API Server私钥(service-account-private-key-file)参数,那么Controller manager会创建Token Controller。
Token Controller也监听Service Account的事件,如果发现新建的Service Account里没有对应的Service Account Secret,则会用API Server私钥创建一个Token(JWT Token),并用该Token、CA证书Namespace名称等三个信息产生一个新的Secret对象,然后放入刚才的Service Account中;如果监听到的事件是删除Service Account事件,则自动删除与该Service Account相关的所有Secret。
此外,Token Controller对象同时监听Secret的创建、修改和删除事件,并根据事件的不同做不同的处理。
当我们在API Server的鉴权过程中启用了Service Account类型的准入控制器,即在kube-apiserver的启动参数中包括下面的内容时:
--admission_control=ServiceAccount
则针对Pod新增或修改的请求,Service Account准入控制器会验证Pod里Service Account是否合法。
如果spec.serviceAccount域没有被设置,则Kubernetes默认为其制定名字为default的Serviceaccount;
如果Pod的spec.serviceAccount域指定了default以外的ServiceAccount,而该ServiceAccount没有事先被创建,则该Pod将操作失败;
如果在Pod中没有指定“ImagePullSecrets”,那么该spec.serviceAccount域指定的ServiceAccount的“ImagePullSecrets”会被加入该Pod;
给Pod添加一个新的Volume,在该Volume中包含ServiceAccountSecret中的Token,并将Volume挂载到Pod中所有容器的指定目录下(/var/run/secrets/kubernetes.io/serviceaccount);
所以,综上所述,ServiceAccount正常运行需要以下几个控制器:
- Admission Controller
- Service Account Controller
- Token Controller
段落开头的service写错了。
我太粗糙了,感谢。