更新时间:2024-09-24 GMT+08:00

ServiceAccount

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

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

认证与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
  • 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,以提高安全性。

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

    为了确保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

      "kind": "PodList",
        "apiVersion": "v1",
        "metadata": {
          "resourceVersion": "13267712"
        },
        "items": [
          {
            "metadata": {
              "name": "hpa-example-77b9b446f6-nc7b6",
      ...