ServiceAccount
Kubernetes中所有的访问,无论外部内部,都会通过API Server处理,访问Kubernetes资源前需要经过认证与授权。
- Authentication:用于识别用户身份的认证,Kubernetes分外部服务帐号和内部服务帐号,采取不同的认证机制,具体请参见认证与ServiceAccount。
- Authorization:用于控制用户对资源访问的授权,对访问的授权目前主要使用RBAC机制,将在RBAC介绍。
认证与ServiceAccount
Kubernetes的用户分为服务帐户(ServiceAccount)和普通帐户两种类型。
- 服务帐户与Namespace绑定,关联一套凭证,Pod创建时挂载Token,从而允许与API Server之间调用。
- Kubernetes中没有代表普通帐户的对象,这类帐户默认由外部服务独立管理,比如在CCE的用户是由IAM管理的。
普通帐号并不是这里要讨论的内容,这里主要关注ServiceAccount。
ServiceAccount同样是Kubernetes中的资源,与Pod、ConfigMap类似,且作用于独立的命名空间,也就是ServiceAccount是属于命名空间级别的,创建命名空间时会自动创建一个名为default的ServiceAccount。
使用下面命令可以查看ServiceAccount。
$ kubectl get sa NAME SECRETS AGE default 1 30d
同时Kubernetes还会为ServiceAccount自动创建一个Token,使用下面命令可以查看到。
- 1.21以前版本的集群中,Pod中获取Token的形式是通过挂载ServiceAccount的Secret来获取Token,这种方式获得的Token是永久的。该方式在1.21及以上的版本中不再推荐使用,并且根据社区版本迭代策略,在1.25及以上版本的集群中,ServiceAccount将不会自动创建对应的Secret。
1.21及以上版本的集群中,直接使用TokenRequest API获得Token,并使用投射卷(Projected Volume)挂载到Pod中。使用这种方法获得的Token具有固定的生命周期,并且当挂载的Pod被删除时这些Token将自动失效。详情请参见Token安全性提升说明。
- 如果您在业务中需要一个永不过期的Token,您也可以选择手动管理ServiceAccount的Secret。尽管存在手动创建永久ServiceAccount Token的机制,但还是推荐使用TokenRequest的方式使用短期的Token,以提高安全性。
$ kubectl describe sa default Name: default Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> Mountable secrets: default-token-vssmw Tokens: default-token-vssmw Events: <none>
在Pod的定义文件中,可以用指定帐户名称的方式将一个ServiceAccount赋值给一个Pod,如果不指定就会使用默认的ServiceAccount。当API Server接收到一个带有认证Token的请求时,API Server会用这个Token来验证发送请求的客户端所关联的ServiceAccount是否允许执行请求的操作。
创建ServiceAccount
使用如下命令就可以创建ServiceAccount:
$ kubectl create serviceaccount sa-example serviceaccount/sa-example created $ kubectl get sa NAME SECRETS AGE default 1 30d sa-example 1 2s
可以看到已经创建了与ServiceAccount相关联的Token。
$ kubectl describe sa sa-example Name: sa-example Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> Mountable secrets: sa-example-token-c7bqx Tokens: sa-example-token-c7bqx Events: <none>
查看Secret的内容,可以发现ca.crt、namespace和token三个数据。
只有在1.23及之前版本的集群中,ServiceAccount才会自动创建Secret。
$ kubectl describe secret sa-example-token-c7bqx Name: sa-example-token-c7bqx ... Data ==== ca.crt: 1082 bytes namespace: 7 bytes token: <Token的内容>
在Pod中使用ServiceAccount
Pod中使用ServiceAccount非常方便,只需要指定ServiceAccount的名称即可。
apiVersion: v1 kind: Pod metadata: name: sa-example spec: serviceAccountName: sa-example containers: - image: nginx:alpine name: container-0 resources: limits: cpu: 100m memory: 200Mi requests: cpu: 100m memory: 200Mi imagePullSecrets: - name: default-secret
创建并查看这个Pod,可以看到Pod挂载了sa-example-token-c7bqx,即Pod使用这个Token来做认证。
通过这个方式,您可以了解Pod的认证机制,但在实际使用中,出于安全性考虑,1.21及以上版本的集群中Pod中默认挂载的Token是临时的。
$ kubectl create -f sa-pod.yaml pod/sa-example created $ kubectl get pod NAME READY STATUS RESTARTS AGE sa-example 0/1 running 0 5s $ kubectl describe pod sa-example ... Containers: sa-example: Mounts: /var/run/secrets/kubernetes.io/serviceaccount from sa-example-token-c7bqx (ro)
进入Pod内部,还可以看到对应的文件,如下所示。
$ kubectl exec -it sa-example -- /bin/sh / # cd /run/secrets/kubernetes.io/serviceaccount /run/secrets/kubernetes.io/serviceaccount # ls ca.crt namespace token
如上,在容器应用中,就可以使用ca.crt和Token来访问API Server。
下面来验证一下认证是否能生效。在Kubernetes集群中,默认为API Server创建了一个名为kubernetes的Service,通过这个Service可以访问API Server。
$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.247.0.1 <none> 443/TCP 34
进入Pod,使用curl命令直接访问会得到如下返回信息,表示并没有权限。
$ kubectl exec -it sa-example -- /bin/sh / # curl https://kubernetes curl: (60) SSL certificate problem: unable to get local issuer certificate More details here: https://curl.haxx.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above.
使用ca.crt和Token做认证,先将ca.crt放到CURL_CA_BUNDLE这个环境变量中,curl命令使用CURL_CA_BUNDLE指定证书;再将Token的内容放到TOKEN中,然后带上TOKEN访问API Server。
# export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt # TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) # curl -H "Authorization: Bearer $TOKEN" https://kubernetes { "kind": "Status", "apiVersion": "v1", "metadata": { }, "status": "Failure", "message": "forbidden: User \"system:serviceaccount:default:sa-example\" cannot get path \"/\"", "reason": "Forbidden", "details": { }, "code": 403 }
可以看到,已经能够通过认证了,但是API Server返回的是cannot get path \"/\"",表示没有权限访问,这说明还需要得到授权后才能访问,授权机制将在RBAC中介绍。