更新时间:2024-01-26 GMT+08:00

ServiceAccount

Kubernetes中所有的访问,无论外部内部,都会通过API Server处理,访问Kubernetes资源前需要经过认证与授权。

  • Authentication:用于识别用户身份的认证,Kubernetes分外部服务账号和内部服务账号,采取不同的认证机制,具体请参见认证与ServiceAccount
  • Authorization:用于控制用户对资源访问的授权,对访问的授权目前主要使用RBAC机制,将在RBAC介绍。
图1 API Server的认证授权

认证与ServiceAccount

Kubernetes的用户分为服务账户(ServiceAccount)和普通账户两种类型。

  • 服务账户与Namespace绑定,关联一套凭证,存储在Secret中,Pod创建时挂载Secret,从而允许与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自动创建一个Secret,使用下面命令可以查看到。

$ 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三个数据。

$ 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,也就是sa-example这个ServiceAccount对应的Token,即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中介绍。