# ServiceAccount
Kubernetes中所有的访问，无论外部内部，都会通过API Server处理，访问Kubernetes资源前需要经过认证与授权。
- Authentication：用于识别用户身份的认证，Kubernetes分外部服务账号和内部服务账号，采取不同的认证机制，具体请参见[认证与ServiceAccount]。
- Authorization：用于控制用户对资源访问的授权，对访问的授权目前主要使用RBAC机制，将在[RBAC](https://support.huaweicloud.com/basics-cce/kubernetes_0033.html)介绍。
图1API Server的认证授权   
![](https://support.huaweicloud.com/basics-cce/zh-cn_image_0261300123.png "点击放大")
 #### 认证与ServiceAccount
Kubernetes的用户分为服务账户（ServiceAccount）和普通账户两种类型。
- 服务账户与Namespace绑定，关联一套凭证，Pod创建时挂载Token，从而允许与API Server之间调用。
- Kubernetes中没有代表普通账户的对象，这类账户默认由外部服务独立管理，比如在CCE的用户是由IAM管理的。
与Pod、ConfigMap类似，ServiceAccount是Kubernetes中的资源，属于命名空间级别。当创建一个新的命名空间时，系统会自动在其中生成一个名为default的ServiceAccount。
使用下面命令可以查看ServiceAccount。
**kubectl get sa**
```
NAME     SECRETS   AGE
default  1         30d
```
![](https://support.huaweicloud.com/basics-cce/public_sys-resources/note_3.0-zh-cn.png)
- 1.21以前版本的集群中，Pod中获取Token的形式是通过挂载ServiceAccount的Secret来获取Token，这种方式获得的Token是永久的。该方式在1.21及以上的版本中不再推荐使用，并且根据社区版本迭代策略，在1.25及以上版本的集群中，ServiceAccount将不会自动创建对应的Secret。 1.21及以上版本的集群中，直接使用[TokenRequest](https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/authentication-resources/token-request-v1/) API[获得Token](https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/service-accounts-admin/#bound-service-account-token-volume)，并使用投射卷（Projected Volume）挂载到Pod中。使用这种方法获得的Token具有固定的生命周期，并且当挂载的Pod被删除时这些Token将自动失效。详情请参见[Token安全性提升说明](https://support.huaweicloud.com/usermanual-cce/cce_10_0477.html)。
  
- 如果您在业务中需要一个永不过期的Token，您也可以选择[手动管理ServiceAccount的Secret](https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/service-accounts-admin/#manual-secret-management-for-serviceaccounts)。尽管存在手动创建永久ServiceAccount Token的机制，但还是推荐使用[TokenRequest](https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/authentication-resources/token-request-v1/)的方式使用短期的Token，以提高安全性。
 
1.25以前版本的集群中，ServiceAccount会自动创建对应的Secret。1.25及以上版本的集群中，ServiceAccount将不会自动创建对应的Secret。下面分别查看两种集群下的ServiceAccount状态。
- 1.25以前版本集群，查看名为default的ServiceAccount状态。 **kubectl describe sa default**
  回显内容如下，说明default自动创建对应的Secret，即default-token-vssmw。
  ```
  Name:                  default
  Namespace:             default
  Labels:                <none>
  Annotations:           <none>
  Image pull secrets:    <none>
  Mountable secrets:     default-token-vssmw
  Tokens:                default-token-vssmw
  Events:                <none>
  ```
  
- 1.25及以上版本集群，查看名为default的ServiceAccount状态。 **kubectl describe sa default**
  由回显内容可知，default未自动创建对应的Secret。
  ```
  Name:                     default
  Namespace:                default 
  Labels:                   <none>
  Annotations:              <none>
  Image pull secrets:       <none>
  Mountable secrets:        <none>
  Tokens:                   <none>
  Events:                   <none>
  ```
  
在Pod的定义文件中，可以用指定账户名称的方式将一个ServiceAccount赋值给一个Pod，如果不指定就会使用默认的ServiceAccount。当API Server接收到一个带有认证Token的请求时，API Server会用这个Token来验证发送请求的客户端所关联的ServiceAccount是否允许执行请求的操作。
#### 创建ServiceAccount
1. 以1.29版本集群为例，使用如下命令在default命名空间内创建ServiceAccount。 
   **kubectl create serviceaccount *sa-example***
   ```
   serviceaccount/sa-example created
   ```
   使用以下命令可以检查sa-example是否创建成功，若NAME列出现sa-example则说明创建成功。
   **kubectl get sa**
   ```
   NAME            SECRETS   AGE
   default         1         30d
   sa-example      0         2s
   ```
   由于本案例使用的集群版本在1.25以上，ServiceAccount将不会自动创建对应的Secret。使用以下命令可以查看创建的ServiceAccount的详细信息，回显中Mountable secrets和Tokens值为none，则说明该ServiceAccount没有自动创建Secret。
   **kubectl describe sa *sa-example***
   ```
   Name:                   sa-example
   Namespace:              default
   Labels:                 <none>
   Annotations:            <none>
   Image pull secrets:     <none>
   Mountable secrets:      <none>
   Tokens:                 <none>
   Events:                 <none>
   ```
   
   
2. 这里选择手动管理Secret，从而得到永不过期的Token。利用以下代码，手动创建名为sa-example-token的Secret，并与名为sa-example的ServiceAccount关联。 
   ```
   kubectl apply -f - <<EOF
   apiVersion: v1 
   kind: Secret 
   metadata:
     namespace: default   
     name: sa-example-token   
     annotations:     
       kubernetes.io/service-account.name: sa-example 
   type: kubernetes.io/service-account-token 
   EOF
   ```
   
   
3. 检查sa-example-token是否创建成功。若命名空间default的Secrets中出现sa-example-token，则说明创建成功。 
   **kubectl get secrets**
   ```
   NAME                        TYPE                                    DATA        AGE
   default-secret              kubernetes.io/dockerconfigjson          1           6d20h
   paas.elb                    cfe/secure-opaque                       1           6d20h
   sa-example-token            kubernetes.io/service-account-token     3           16s
   ```
   查看Secret的内容，可以发现ca.crt、namespace和token三个数据。
   **kubectl describe secret *sa-example-token***
   ```
   Name:         sa-example-token
   Namespace:    default
   Labels:       <none>
   Annotations:  kubernetes.io/service-account.name: sa-example
                 kubernetes.io/service-account.uid: 4b7d3e19-1dfe-4ee0-bb49-4e2e0c3c5e25
   Type:  kubernetes.io/service-account-token
   Data
   ====
   ca.crt:     1123 bytes
   namespace:  7 bytes
   token:      eyJhbGciOiJSU...
   ```
   
   
4. 检验ServiceAccount与新建的Secret关联是否成功，即检查ServiceAccount是否获取到Token。由回显内容可知，sa-example与sa-example-token关联成功。 
   **kubectl describe sa *sa-example***
   ```
   Name:                 sa-example
   Namespace:            default
   Labels:               <none>
   Annotations:          <none>
   Image pull secrets:   <none>
   Mountable secrets:    <none>
   Tokens:               sa-example-token
   Events:               <none>
   ```
   
   
 
 #### 在Pod中使用ServiceAccount
Pod中使用ServiceAccount非常方便，只需要指定ServiceAccount的名称即可。下面以"nginx:latest"为例，演示具体步骤。
1. 创建一个名为sa-pod.yaml的描述文件。其中，mysql.yaml为自定义名称，您可以随意命名。 
   **vim *sa-pod.yaml***
   ![](https://support.huaweicloud.com/basics-cce/public_sys-resources/notice_3.0-zh-cn.png)
   为了确保Pod能够使用手动创建的Secret中的Token，您需要明确地将该Secret挂载到容器中，挂载方式请参见描述文件中的加粗代码。
   文件内容如下：
   ```
   apiVersion: v1
   kind: Pod
   metadata:
     name: sa-pod
   spec:
     serviceAccountName: sa-example                  #  指定sa-example为Pod使用的服务账户
     imagePullSecrets:
     - name: default-secret
     containers:
     - image: nginx:latest
       name: container-0
       resources:
         limits:
           cpu: 100m
           memory: 200Mi
         requests:
           cpu: 100m
           memory: 200Mi
       volumeMounts:                             #  将名为secret-volume的存储卷挂载到Pod
       - name: secret-volume
         readOnly: true                          #  表示挂载的存储卷是只读的
         mountPath: "/etc/secret-volume"         #  指定存储卷在容器内部的挂载路径，可以自定义
     volumes:                                    #  定义Pod可以使用的Secret存储卷
     - name: secret-volume                       #  指定Secret存储卷的名称，可以自定义
       secret:                                   #  指定这个存储卷的类型为Secret
         secretName: sa-example-token            #  将之前建立的sa-example-token挂载到定义的存储卷
   ```
   
   
2. 创建并查看这个Pod，可以看到Pod挂载了sa-example-token，即Pod可以使用这个Token来做认证。 
   **kubectl create -f *sa-pod.yaml***
   回显内容如下：
   ```
   pod/sa-pod created
   ```
   使用以下代码，检验Pod是否创建成功。
   **kubectl get pod**
   回显内容如下，若sa-pod的状态为Running，则说明Pod创建成功
   ```
   NAME                     READY   STATUS              RESTARTS   AGE
   sa-pod               1/1     running             0          5s
   ```
   
   
3. 查看sa-pod的具体信息，可以检验sa-example-token是否挂载成功。 
   **kubectl describe pod *sa-pod***
   回显内容如下：
   ```
   ...
   Containers:
     container-0:
       Container ID:   
       Image:          nginx:latest
       Image ID:       
       Port:           <none>
       Host Port:      <none>
       State:          Waiting
         Reason:       ImagePullBackOff
       Ready:          False
       Restart Count:  0
       Limits:
         cpu:     100m
         memory:  200Mi
       Requests:
         cpu:        100m
         memory:     200Mi
       Environment:  <none>
       Mounts:
          #  表示Pod已挂载sa-example-token，即Pod可以使用这个Token来做认证 
         /etc/secret-volume from secret-volume (ro)                                 
          #  自动挂载的TokenRequest，可以提供短期的Token
         /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-2s4sw (ro)  
   ...
   ```
   进入Pod内部，还可以看到对应的文件，具体命令如下。其中，cd后的路径与secret-volume的挂载路径一致。
   **kubectl exec -it *sa-pod* -- /bin/sh**
   **cd** ***/etc/secret-volume***
   **ls**
   回显内容如下：
   ```
   ca.crt     namespace  token
   ```
   
   
4. 验证手动创建的ServiceAccount Token能否生效。 
   1. 在Kubernetes集群中，默认为API Server创建了一个名为kubernetes的Service，通过这个Service可以访问集群内的Pod资源。ctrl+d退出Pod后，使用以下命令可以查看该服务的具体信息。 **kubectl get svc**
      回显内容如下：
      ```
      NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
      kubernetes     ClusterIP   10.247.0.1       <none>        443/TCP          34
      ```
      
   
   2. 进入Pod，并检查Pod是否能够在不使用Token的情况下通过API Server访问集群内的Pod资源。 **kubectl exec -it *sa-pod* -- /bin/sh**
      **curl *https://10.247.0.1:443***/api/v1/namespaces/default/pods
      回显结果如下，说明Pod不能直接通过API Server访问集群内的Pod资源。
      ```
      curl: (60) SSL certificate problem: unable to get local issuer certificate
      More details here: https://curl.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.
      ```
      
   
   3. 设置ca.crt的环境变量。将ca.crt的路径设置到CURL_CA_BUNDLE环境变量中，这将指示curl命令使用该证书文件作为信任锚点。 **export CURL_CA_BUNDLE=** ***/etc/secret-volume/ca.crt***
      
   
   4. 将Token的内容放到TOKEN中。 **TOKEN=$(cat** ***/etc/secret-volume/token*** **)**
      检验TOKEN是否设置成功。
      **echo $TOKEN**
      若回显内容如下，则设置成功。
      ```
      eyJhbGciOiJSUzI1NiIsImtpZCI6I...
      ```
      
   
   5. 利用TOKEN访问API Server。 **curl -H "Authorization: Bearer $TOKEN"** ***https://10.247.0.1:443***/api/v1/namespaces/default/pods
      回显结果如下，则说明Pod能够通过认证，即手动创建的ServiceAccount Token能够生效。若API Server返回的是cannot get path \\"/api/v1/namespaces/default/pods\\""，则说明没有权限访问，需要得到授权才能访问，授权机制请参见[RBAC](https://support.huaweicloud.com/basics-cce/kubernetes_0033.html)。
      ```
      "kind": "PodList",
        "apiVersion": "v1",
        "metadata": {
          "resourceVersion": "13267712"
        },
        "items": [
          {
            "metadata": {
              "name": "hpa-example-77b9b446f6-nc7b6",
      ...
      ```
      
   
   
   
   
 
