k8s Service Accounts

发布时间 2023-08-01 19:19:37作者: 小吉猫

Service Accounts 介绍

服务帐户是一种非人类帐户,在 Kubernetes 中,它在 Kubernetes 集群中提供独特的身份。应用程序 Pod、系统组件以及集群内部和外部的实体可以使用特定 ServiceAccount 的凭据来标识该 ServiceAccount。此身份在各种情况下都很有用,包括向 API 服务器进行身份验证或实施基于身份的安全策略。
服务帐户作为 API 服务器中的 ServiceAccount 对象存在。服务帐户具有以下属性:

  namespace:每个服务帐户都绑定到一个 Kubernetes 名称空间。每个命名空间在创建时都会获得一个default ServiceAccount。
 
  轻量级:服务帐户存在于集群中,并在 Kubernetes API 中定义。您可以快速创建服务帐户以启用特定任务。

  可移植:复杂容器化工作负载的配置包可能包括系统组件的服务帐户定义。服务帐户的轻量级性质和命名空间身份使配置可移植。
服务帐户与用户帐户不同,用户帐户是集群中经过身份验证的人类用户。默认情况下,Kubernetes API 服务器中不存在用户帐户;相反,API 服务器将用户身份视为不透明数据。您可以使用多种方法作为用户帐户进行身份验证。某些 Kubernetes 发行版可能会添加自定义扩展 API 来表示 API 服务器中的用户帐户。
Description ServiceAccount User or group
Location Kubernetes API (ServiceAccount object) External
Access control Kubernetes RBAC or other authorization mechanisms Kubernetes RBAC or other identity and access management mechanisms
Intended use Workloads, automation People

用户帐户与服务帐户

Kubernetes 出于多种原因区分用户帐户和服务帐户的概念:
  1. 用户账号是针对人而言的。而服务账号是针对运行在 Pod 中的应用进程而言的, 在 Kubernetes 中这些进程运行在容器中,而容器是 Pod 的一部分。
  2. 用户账号是全局性的。其名称在某集群中的所有名字空间中必须是唯一的。 无论你查看哪个名字空间,代表用户的特定用户名都代表着同一个用户。 在 Kubernetes 中,服务账号是名字空间作用域的。 两个不同的名字空间可以包含具有相同名称的 ServiceAccount。
  3. 通常情况下,集群的用户账号可能会从企业数据库进行同步, 创建新用户需要特殊权限,并且涉及到复杂的业务流程。 服务账号创建有意做得更轻量,允许集群用户为了具体的任务按需创建服务账号。 将 ServiceAccount 的创建与新用户注册的步骤分离开来, 使工作负载更易于遵从权限最小化原则。
  4. 对人员和服务帐户的审核考虑因素可能有所不同;分离使得这一目标更容易实现。
  5. 针对复杂系统的配置包可能包含系统组件相关的各种服务账号的定义。 因为服务账号的创建约束不多并且有名字空间域的名称,因此此类配置通常是可移植的。

默认服务帐户

当您创建集群时,Kubernetes 会自动创建一个default以集群中的每个命名空间命名的 ServiceAccount 对象。默认情况下,每个命名空间中的服务default 帐户不会获得任何权限,除非 启用了基于角色的访问控制 (RBAC) 时 Kubernetes 向所有经过身份验证的主体授予的默认 API 发现权限。如果删除default命名空间中的 ServiceAccount 对象, 控制平面 将其替换为新的。

如果您在命名空间中部署 Pod,并且没有 手动为该 Pod 分配 ServiceAccount,则 Kubernetes 会default将该命名空间的 ServiceAccount 分配给该 Pod。

默认服务帐户挂载地址

# kubectl describe pod pod-name -n default
Containers:
...
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-7rkqr (ro)
...
Volumes:
  kube-api-access-7rkqr:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true

默认服务帐户挂载点信息

各容器的该挂载点目录中通常存在3个文件:ca.crt、namespace和token,其中,token文件保存了ServiceAccount的认证令牌,容器中的进程使用该账户认证到API Server,进而由认证插件完成用户认证并将其用户名传递给授权插件
# kubectl exec  demoappv10-78b6586d58-48nkf -n demoapp -- ls -l /var/run/secrets/kubernetes.io/serviceaccount
total 0
lrwxrwxrwx    1 root     root            13 Jul 28 09:28 ca.crt -> ..data/ca.crt
lrwxrwxrwx    1 root     root            16 Jul 28 09:28 namespace -> ..data/namespace
lrwxrwxrwx    1 root     root            12 Jul 28 09:28 token -> ..data/token

默认服务帐户详细信息

# kubectl describe serviceaccounts/default -n demoapp
Name:                default
Namespace:           demoapp
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>

Kubernetes 服务帐户的用例

作为一般准则,您可以在以下场景中使用服务帐户提供身份:

1. 您的 Pod 需要与 Kubernetes API 服务器进行通信,例如在以下情况下:
     提供对 Secrets 中存储的敏感信息的只读访问权限。
     授予跨命名空间访问权限,例如允许命名空间中的 Podexample读取、列出和监视kube-node-lease命名空间中的 Lease 对象。
2. 您的 Pod 需要与外部服务进行通信。例如,工作负载 Pod 需要商业云 API 的身份,并且商业提供商允许配置合适的信任关系。
3. 使用imagePullSecret.
4. 外部服务需要与 Kubernetes API 服务器进行通信。例如,作为 CI/CD 管道的一部分对集群进行身份验证。
5. 您在集群中使用第三方安全软件,该软件依赖于不同 Pod 的 ServiceAccount 身份将这些 Pod 分组到不同的上下文中。

如何使用服务帐户

要使用 Kubernetes 服务帐户,您需要执行以下操作:

1. kubectl使用 Kubernetes 客户端(例如定义该对象的清单)创建 ServiceAccount 对象。

2. 使用授权机制(例如 RBAC )向 ServiceAccount 对象授予权限。

3. 在 Pod 创建过程中将 ServiceAccount 对象分配给 Pod。

4. 如果您使用来自外部服务的身份, 请检索 ServiceAccount 令牌并从该服务使用它。

向 ServiceAccount 授予权限

使用 Kubernetes 内置的 基于角色的访问控制 (RBAC) 机制来授予每个服务帐户所需的最低权限。您创建一个授予访问权限的角色,然后将该角色绑定到您的 ServiceAccount。RBAC 允许您定义最小权限集,以便服务帐户权限遵循最小权限原则。使用该服务帐户的 Pod 不会获得比正常运行所需的权限更多的权限。

使用 ServiceAccount 进行跨命名空间访问

您可以使用 RBAC 允许一个命名空间中的服务帐户对集群中不同命名空间中的资源执行操作。例如,考虑这样一个场景:您在dev命名空间中有一个服务帐户和 Pod,并且您希望您的 Pod 看到在maintenance命名空间中运行的作业。您可以创建一个角色对象来授予列出作业对象的权限。然后,您将在命名空间中创建一个 RoleBinding 对象maintenance,以将 Role 绑定到 ServiceAccount 对象。现在,命名空间中的 Poddev可以maintenance使用该服务帐户列出命名空间中的作业对象。

将 ServiceAccount 分配给 Pod

要将 ServiceAccount 分配给 Pod,您可以spec.serviceAccountName 在 Pod 规范中设置该字段。然后,Kubernetes 会自动向 Pod 提供该 ServiceAccount 的凭据。在 v1.22 及更高版本中,Kubernetes使用API获取一个短暂的、自动轮换的TokenRequest令牌 ,并将该令牌挂载为 预计卷。

默认情况下,Kubernetes 为 Pod 提供已分配 ServiceAccount 的凭证,无论该 defaultServiceAccount 是您指定的 ServiceAccount 还是自定义 ServiceAccount。

要防止 Kubernetes 自动注入指定 ServiceAccount 或defaultServiceAccount 的凭据,请将 automountServiceAccountTokenPod 规范中的字段设置为false。

在 1.22 之前的版本中,Kubernetes 向 Pod 提供长期静态令牌作为 Secret。

手动检索 ServiceAccount 凭据

如果您需要将 ServiceAccount 的凭据安装在非标准位置,或者需要非 API 服务器的受众,请使用以下方法之一:

TokenRequest API (推荐):从您自己的应用程序代码 中请求短期服务帐户令牌。令牌会自动过期,并且可以在过期后轮换。如果您有一个不支持 Kubernetes 的旧应用程序,您可以使用同一 Pod 中的 sidecar 容器来获取这些令牌并使它们可供应用程序工作负载使用。
令牌卷投影 (也推荐):在 Kubernetes v1.20 及更高版本中,使用 Pod 规范告诉 kubelet 将服务帐户令牌作为 投影卷添加到 Pod 中。预计的令牌会自动过期,并且 kubelet 在令牌过期之前轮换令牌。
服务帐户令牌密钥 (不推荐):您可以将服务帐户令牌作为 Kubernetes Secret 挂载到 Pod 中。这些令牌不会过期,也不会轮换。不建议使用此方法,尤其是在大规模情况下,因为存在与静态、长期凭证相关的风险。在 Kubernetes v1.24 及更高版本中, LegacyServiceAccountTokenNoAutoGeneration 功能门 会阻止 Kubernetes 自动为 ServiceAccounts 创建这些令牌。LegacyServiceAccountTokenNoAutoGeneration默认启用;换句话说,Kubernetes 不会创建这些令牌。
笔记:
对于在 Kubernetes 集群外部运行的应用程序,您可能会考虑创建一个存储在 Secret 中的长期存在的 ServiceAccount 令牌。这允许身份验证,但 Kubernetes 项目建议您避免这种方法。长期不记名代币存在安全风险,因为一旦泄露,代币可能会被滥用。相反,请考虑使用替代方案。例如,您的外部应用程序可以使用受良好保护的私钥和and证书进行身份验证,或者使用自定义机制(例如您自己实现的身份验证 Webhook)进行身份验证。

您还可以使用 TokenRequest 为外部应用程序获取短期令牌。

验证服务帐户凭据

ServiceAccounts 使用签名 JSON 网络令牌(JWT) 向 Kubernetes API 服务器以及存在信任关系的任何其他系统进行身份验证。根据令牌的颁发方式(使用有时间限制的TokenRequest或使用具有 Secret 的旧机制),ServiceAccount 令牌还可能具有到期时间、受众以及令牌开始有效的时间。当充当 ServiceAccount 的客户端尝试与 Kubernetes API 服务器通信时,客户端会Authorization: Bearer <token>在 HTTP 请求中包含一个标头。API 服务器按如下方式检查该持有者令牌的有效性:
1. 检查令牌签名。
2. 检查token是否过期。
3. 检查令牌声明中的对象引用当前是否有效。
4. 检查令牌当前是否有效。
5. 检查观众的说法。
TokenRequest API为 ServiceAccount生成绑定令牌。此绑定与充当该 ServiceAccount 的客户端(例如 Pod)的生命周期相关联。

对于使用TokenRequestAPI 发出的令牌,API 服务器还会检查使用 ServiceAccount 的特定对象引用是否仍然存在,与唯一身份那个物体的。对于在 Pod 中作为 Secret 挂载的旧令牌,API 服务器会根据 Secret 检查令牌。

在代码中验证服务帐户凭据

如果您有自己的服务需要验证 Kubernetes 服务帐户凭据,您可以使用以下方法:

  TokenReview API (推荐)
  OIDC发现
Kubernetes 项目建议您使用 TokenReview API,因为当删除 Secret、ServiceAccounts 和 Pod 等 API 对象时,该方法会使这些对象绑定的令牌失效。例如,如果您删除包含预计 ServiceAccount 令牌的 Pod,集群会立即使该令牌失效,并且 TokenReview 会立即失败。如果您改用 OIDC 验证,您的客户端将继续将令牌视为有效,直到令牌达到其过期时间戳。

您的应用程序应始终定义它接受的受众,并应检查令牌的受众是否与应用程序期望的受众相匹配。这有助于最小化令牌的范围,以便它只能在您的应用程序中使用,而不能在其他地方使用。

备择方案

1. 使用另一种机制颁发您自己的令牌,然后使用 Webhook 令牌身份验证 通过您自己的验证服务来验证不记名令牌。
2. 向 Pod 提供您自己的身份。
     使用 SPIFFE CSI 驱动程序插件将 SPIFFE SVID 作为 X.509 证书对提供给 Pod。
     使用 Istio 等服务网格向 Pod 提供证书。
3. 从集群外部向 API 服务器进行身份验证,无需使用服务帐户令牌:
     配置 API 服务器以接受来自身份提供商的 OpenID Connect (OIDC) 令牌。
     使用服务帐户或使用外部身份和访问管理 (IAM) 服务(例如来自云提供商的服务)创建的用户帐户来对集群进行身份验证。
     将 CertificateSigningRequest API 与客户端证书结合使用。
4. 配置 kubelet 以从镜像注册表检索凭证。
5. 使用设备插件访问虚拟可信平台模块 (TPM),然后允许使用私钥进行身份验证。     

绑定的服务账号令牌卷机制

默认情况下,Kubernetes 控制平面(特别是 ServiceAccount 准入控制器) 添加一个预计卷到 Pod, 此卷包括了访问 Kubernetes API 的令牌。
# kubectl get pod demoappv10-78b6586d58-gb8vw  -n demoapp -o yaml
...
spec:
  containers:
  ...
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-4q4lt
      readOnly: true
  ...
  volumes:
  - name: kube-api-access-4q4lt
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token     # 必须与应用所预期的路径匹配
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace
该清单片段定义了由三个数据源组成的预计卷。在当前场景中,每个数据源也代表该卷内的一条独立路径。这三个数据源是:
1. 一个serviceAccount Token 数据源,包含 kubelet 从 kube-apiserver 获取的令牌。 kubelet 使用 TokenRequest API 获取有时间限制的令牌。为 TokenRequest 服务的这个令牌会在 Pod 被删除或定义的生命周期(默认为 1 小时)结束之后过期。kubelet 还会在令牌过期之前刷新该令牌,该令牌绑定到特定的 Pod, 并将其 audience(受众)设置为与 kube-apiserver 的 audience 相匹配。 这种机制取代了之前基于 Secret 添加卷的机制,之前 Secret 代表了针对 Pod 的 ServiceAccount 但不会过期。
2. 一个configMap 数据源。ConfigMap 包含一组证书颁发机构数据。 Pod 可以使用这些证书来确保自己连接到集群的 kube-apiserver(而不是连接到中间件或意外配置错误的对等点上)。
3. 一个downwardAPI 数据源,用于查找包含 Pod 的命名空间的名称,并使该名称信息可供 Pod 内运行的应用程序代码使用。
Pod 中挂载此特定卷的任何容器都可以访问上述信息。
说明:
没有特定的机制可以使通过 TokenRequest 签发的令牌无效。 如果你不再信任为某个 Pod 绑定的服务账号令牌, 你可以删除该 Pod。删除 Pod 将使其绑定的服务账号令牌过期。

手动管理 ServiceAccount 的 Secret

v1.22 之前的 Kubernetes 版本会自动创建凭据访问 Kubernetes API。 这种更老的机制基于先创建令牌 Secret,然后将其挂载到正运行的 Pod 中。

在包括 Kubernetes v1.27 在内最近的几个版本中,使用 TokenRequest API 直接获得 API 凭据, 并使用投射卷挂载到 Pod 中。使用这种方法获得的令牌具有绑定的生命周期, 当挂载的 Pod 被删除时这些令牌将自动失效。

你仍然可以手动创建 Secret 来保存服务账号令牌;例如在你需要一个永不过期的令牌的时候。

一旦你手动创建一个 Secret 并将其关联到 ServiceAccount, Kubernetes 控制平面就会自动将令牌填充到该 Secret 中。
说明:
尽管存在手动创建长久 ServiceAccount 令牌的机制,但还是推荐使用 TokenRequest 获得短期的 API 访问令牌。

控制平面详细信息

ServiceAccount控制器

ServiceAccount控制器负责为名称空间管理相应的资源对象,它需要确保每个名称空间中都存在一个名为default的服务账户对象。

ServiceAccount准入控制器

Pod 的修改是通过一个名为准入控制器 的插件来实现的。它是 API 服务器的一部分。该准入控制器会在 Pod 创建时同步进行修改。当此插件处于活动状态时(大多数发行版默认情况下都是如此),它会在创建 Pod 时执行以下操作:
1. 如果 Pod 没有设置.spec.serviceAccountName,准入控制器会将此传入 Pod 的 ServiceAccount 名称设置为default。
2. 准入控制器确保传入 Pod 引用的 ServiceAccount 存在。如果没有名称匹配的 ServiceAccount,则准入控制器会拒绝传入的 Pod。该检查甚至适用于 ServiceAccount default。
3. 前提是 ServiceAccount 的automountServiceAccountToken字段和 Pod 的automountServiceAccountToken字段都没有设置为false:
   准入控制器会改变传入的 Pod,添加一个额外的卷,其中包含用于 API 访问的令牌
   准入控制器将 volumeMount 添加到 Pod 中的每个容器, 忽略已为 /var/run/secrets/kubernetes.io/serviceaccount 路径定义的卷挂载的所有容器。 对于 Linux 容器,此卷挂载在 /var/run/secrets/kubernetes.io/serviceaccount; 在 Windows 节点上,此卷挂载在等价的路径上。
4. 如果新来 Pod 的规约不包含任何 imagePullSecrets,则准入控制器添加 imagePullSecrets, 并从 ServiceAccount 进行复制。

令牌控制器

令牌控制器是控制平面组件Controller Manager中的一个专用控制器,它工作于异步模式,负责完成如下任务:
监测 ServiceAccount 的删除并删除所有相应的服务账号令牌 Secret。
监测服务账号令牌 Secret 的添加,保证相应的 ServiceAccount 存在, 如有需要,向 Secret 中添加令牌。
监测服务账号令牌 Secret 的删除,如有需要,从相应的 ServiceAccount 中移除引用。
kube-controller-manager您必须在使用标志时将服务帐户私钥文件传递给令牌控制器--service-account-private-key-file 。私钥用于对生成的服务帐户令牌进行签名。同样,您必须将相应的公钥传递给kube-apiserver using--service-account-key-file标志。公钥将用于在身份验证期间验证令牌。

TokenRequest API

您可以使用ServiceAccount 的TokenRequest 子资源来获取该 ServiceAccount 的有时限令牌。您不需要调用此方法来获取在容器中使用的 API 令牌,因为 kubelet 使用预计卷为您设置了此令牌。
Kubernetes 控制平面(具体来说,ServiceAccount 准入控制器)向 Pod 添加一个预计卷,并且 kubelet 确保该卷包含一个令牌,让容器能够以正确的 ServiceAccount 身份进行身份验证。
(此机制取代了早期基于 Secret 添加卷的机制,其中 Secret 代表 Pod 的 ServiceAccount 但不会过期。)
# kubectl get pod demoappv10-78b6586d58-gb8vw  -n demoapp -o yaml
...
spec:
  containers:
  ...
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-4q4lt
      readOnly: true
  ...
  volumes:
  - name: kube-api-access-4q4lt
    projected:
      defaultMode: 420    # 这个十进制数等同于八进制 0644
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token     # 必须与应用所预期的路径匹配
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace
该清单片段定义了一个结合了来自三个来源的信息的预计卷:
1. 一个serviceAccountToken源,包含 kubelet 从 kube-apiserver 获取的令牌。kubelet 使用 TokenRequest API 获取有时限的令牌。为 TokenRequest 提供的令牌会在 Pod 被删除时或在定义的生命周期(默认情况下为 1 小时)之后过期。该令牌绑定到特定的 Pod,并以 kube-apiserver 作为其受众。
2. 一个configMap来源。ConfigMap 包含一组证书颁发机构数据。Pod 可以使用这些证书来确保它们连接到集群的 kube-apiserver(而不是连接到中间件或意外配置错误的对等点)。
3. 一个downwardAPI来源。该downwardAPI卷使包含 Pod 的命名空间的名称可供 Pod 内运行的应用程序代码使用。
Pod 中挂载该卷的任何容器都可以访问上述信息。

创建ServiceAccount

每个命名空间至少有一个 ServiceAccount:默认的 ServiceAccount 资源,称为default。

命令式ServiceAccount资源创建

# kubectl create serviceaccount my-service-account -n demoapp
serviceaccount/my-service-account created

ServiceAccount资源清单

apiVersion: v1          # ServiceAccount所属的API群组及版本
kind: ServiceAccount    # 资源类型标识
metadata:
  name: <string>        # 资源名称
  namespace: <string>   # ServiceAccount是名称空间级别的资源
automountServiceAccountToken: <boolean> # 是否让Pod自动挂载API令牌
secrets: <[]Object>     # 以该SA运行的Pod要使用的Secret对象所组成的列表
  apiVersion: <string>  # 引用的Secret对象所属的API群组及版本,可省略
  kind: <string>        # 引用的资源类型,这里是指Secret,可省略
  name: <string>        # 引用的Secret对象的名称,通常仅给出该字段即可
  namespace: <string>   # 引用的Secret对象所属的名称空间
  uid: <string>         # 引用的Secret对象的标识符  
imagePullSecrets: <[]Object> # 引用的用于下载Pod中容器镜像的Secret对象列表
  name: <string>        # docker-registry类型的Secret资源名称

创建serviceaccount资源

kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-service-account
  namespace: demoapp
automountServiceAccountToken: true
EOF

查看ServiceAccount资源

# kubectl get serviceaccounts -n demoapp
NAME                 SECRETS   AGE
default              0         36m
my-service-account   0         93s

禁止pod自动挂载API令牌

如果你不希望 kubelet 自动挂载某 ServiceAccount 的 API 访问凭据,你可以选择不采用这一默认行为。 通过在 ServiceAccount 对象上设置 automountServiceAccountToken: false,可以放弃在 /var/run/secrets/kubernetes.io/serviceaccount/token 处自动挂载该服务账号的 API 凭据。

例如:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
automountServiceAccountToken: false
...

您还可以选择不自动挂载特定 Pod 的 API 凭据:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  serviceAccountName: build-robot
  automountServiceAccountToken: false
  ...
如果 ServiceAccount 和 Pod 的 .spec 都设置了 automountServiceAccountToken 值, 则 Pod 上 spec 的设置优先于服务账号的设置。

为 ServiceAccount 手动创建 API 令牌

注意:
只有令牌请求机制不合适,才需要创建长久的 API 令牌。 令牌请求机制提供有时间限制的令牌;因为随着这些令牌过期,它们对信息安全方面的风险也会降低。
要为 ServiceAccount 创建一个不过期、持久化的 API 令牌, 请创建一个类型为 kubernetes.io/service-account-token 的 Secret, 附带引用 ServiceAccount 的注解。控制平面随后生成一个长久的令牌, 并使用生成的令牌数据更新该 Secret。

查看ServiceAccount资源

# kubectl get serviceaccounts -n demoapp
NAME                 SECRETS   AGE
default              0         36m
my-service-account   0         93s

为 ServiceAccount 手动创建长期 API 令牌

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: my-service-account-secret
  namespace: demoapp
  annotations:
    kubernetes.io/service-account.name: my-service-account
type: kubernetes.io/service-account-token
EOF

查看 Secret 

# kubectl get secret/my-service-account-secret -n demoapp -o yaml
apiVersion: v1
items:
- apiVersion: v1
  data:
    ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURsRENDQW55Z0F3SUJBZ0lVUkNuUVRpeTB0KzFscU0ydTZlS2VoN0RSR3p3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1lURUxNQWtHQTFVRUJoTUNRMDR4RVRBUEJnTlZCQWdUQ0VoaGJtZGFhRzkxTVFzd0NRWURWUVFIRXdKWQpVekVNTUFvR0ExVUVDaE1EYXpoek1ROHdEUVlEVlFRTEV3WlRlWE4wWlcweEV6QVJCZ05WQkFNVENtdDFZbVZ5CmJtVjBaWE13SUJjTk1qSXhNREV6TURZME16QXdXaGdQTWpFeU1qQTVNVGt3TmpRek1EQmFNR0V4Q3pBSkJnTlYKQkFZVEFrTk9NUkV3RHdZRFZRUUlFd2hJWVc1bldtaHZkVEVMTUFrR0ExVUVCeE1DV0ZNeEREQUtCZ05WQkFvVApBMnM0Y3pFUE1BMEdBMVVFQ3hNR1UzbHpkR1Z0TVJNd0VRWURWUVFERXdwcmRXSmxjbTVsZEdWek1JSUJJakFOCkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQTZHc2JveDVXOEpyeG1wajR0S1JvVjdKN3RRYkMKMFpTcjZ4SnFXRGpQRUhrYlRUQWRmaUtDWnRRSGQ2enh3TG9nNUo4UFFJb2xmU25BbFV4Mk90YmNQODdpOTNiaApYYVNyc1ZsKy9XV1NPbUdNSWtqY3U1RWxZazdrQmRoSHR6YW54bkRBaCtwbkY2NVBJc3pCRkQrVjdoRTBZSlRSCkRrQ0RVUEw5WVZ0Rmh1TXczU1RRU0V1MVY0TXBXdGxSSDlTRUV6SWtSTTJ0MEJNZDFicjlHQVgxazJzZGkvdkYKSVRXUUJzd1htK2V6SzNhaW9WNnB3K1hJKzN5S0ZydHVPbjhZTiszQkRYb3A5d2xJb3czRStEK3pMeXd2Wm1ZOApaTVk3ZFV1anlxWUI0aFJmMkxFZkNJRW9vUzZGWCs2RDJna2hzWFA5T09CREhPYStjQTlVYjF0eW13SURBUUFCCm8wSXdRREFPQmdOVkhROEJBZjhFQkFNQ0FRWXdEd1lEVlIwVEFRSC9CQVV3QXdFQi96QWRCZ05WSFE0RUZnUVUKa0UrVlV2K3lzZmNFWUJ2WUllcVA5dVk1elNzd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFPYXRBME1FRWE4VwozNXh3L1RoZ1FieEttM1ZsQm5ibVpIbFJtdm9Bb1ZaLzNBeUdiSXBTcEVreGZ4UTdMaEJZNDFkUXI0U1JDMm9SCitIdWFXbEsyczRkS2lDQ3EydktTQ2dlOUJ1RkM0eHpZenJnOFZ6R21KZE4vaGRLV0JHRG9KYlErM291WWErTjcKSGxTcVU5RldqRXNBcDM0VkhwT05Zd1pneWVycy9PNmQ3UmRWLzhaWXpNcVJBUUNIa3phL3R4NHUwalZNbTh3bAowMldMbWZUVnBNSldLc2lzT2RQT3ZmSUtNZktxNFErdTNCdUMrT1RIczA1MDlibjU4RHc3dXJJVGV4NG9QSEdqCjFYUzliRzVsSjhHOTBDTEc2NGVxK0FaWDNLaWJjWEpSMmhFd0EzcVBiNWs2bUE0MjhrTGUwNmhYRDFpNXZ5aVAKS2huNE1CcW9BQWs9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    namespace: ZGVtb2FwcA==
    token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklrbGtZV1JtUVU1M2FWOHlZbFU0YW1kaGVHWTBhWGhzZVZORE1EaE9aRmczU0VsVmRqTkZiSG93YUZFaWZRLmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUprWlcxdllYQndJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpXTnlaWFF1Ym1GdFpTSTZJbTE1TFhObGNuWnBZMlV0WVdOamIzVnVkQzF6WldOeVpYUWlMQ0pyZFdKbGNtNWxkR1Z6TG1sdkwzTmxjblpwWTJWaFkyTnZkVzUwTDNObGNuWnBZMlV0WVdOamIzVnVkQzV1WVcxbElqb2liWGt0YzJWeWRtbGpaUzFoWTJOdmRXNTBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpYSjJhV05sTFdGalkyOTFiblF1ZFdsa0lqb2lZalU1WVRnMVptUXRNREUwTWkwME1qZ3dMV0ZpTURVdFpUWXhObVpqTURoa1lXVTBJaXdpYzNWaUlqb2ljM2x6ZEdWdE9uTmxjblpwWTJWaFkyTnZkVzUwT21SbGJXOWhjSEE2YlhrdGMyVnlkbWxqWlMxaFkyTnZkVzUwSW4wLnZXRkhwTmhLaGhHRFIydWN5X0xPeDk2Umh1RnJsRnByY0ExT2tLZXhCRi1NWkltVU5ncF9sdVJRMUdZQWtWTy1LTDE4MmwtWUZmMC15Wm5pTzVKWEVid2MtcUQtTHBzQWo0ZHk5N2xaNkhxeVpQWEdsQ2lGNlhIbUZIejZubDdXZWk1Sl9nenRCX2R2Qm5UTXdsM0hwNE9aeUFDQm03eUFCLU9NLU5JREcwaGRsSWF2UWhYWG1HclpOTnBFYndabk4xcF8tUU15U2cyeU45UERocU9oUldXWXpOYUxNUXA0ZUFBbEhYemUtQTdfbWk4NmdUVkFuY2hUa2ZxbUNad1ItVU9nZ3Z3Vk9yVkVjcVZicDRSRV9HNmIxd3lvZG1Mbi1rNXFyWDhBSTBqamp4TG9hZUJKVDk0TnNaaWRuV1ctT0x4Y0ZfZVpwRVE3bnlXbnRMZ3duQQ==
  kind: Secret
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"v1","kind":"Secret","metadata":{"annotations":{"kubernetes.io/service-account.name":"my-service-account"},"name":"my-service-account-secret","namespace":"demoapp"},"type":"kubernetes.io/service-account-token"}
      kubernetes.io/service-account.name: my-service-account
      kubernetes.io/service-account.uid: b59a85fd-0142-4280-ab05-e616fc08dae4
    creationTimestamp: "2023-08-01T09:23:31Z"
    name: my-service-account-secret
    namespace: demoapp
    resourceVersion: "2268079"
    uid: d7ce3b7f-75e1-4382-96e1-6c1ff60e846f
  type: kubernetes.io/service-account-token
kind: List
metadata:
  resourceVersion: ""

查看 ServiceAccount API令牌

由于设置的注释,控制平面会自动为该 ServiceAccounts 生成一个令牌,并将它们存储到关联的 Secret 中。控制平面还会清理已删除的 ServiceAccount 的令牌。
# kubectl describe secret/my-service-account-secret -n demoapp
Name:         my-service-account-secret
Namespace:    demoapp
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: my-service-account
              kubernetes.io/service-account.uid: b59a85fd-0142-4280-ab05-e616fc08dae4

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1302 bytes
namespace:  7 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IklkYWRmQU53aV8yYlU4amdheGY0aXhseVNDMDhOZFg3SElVdjNFbHowaFEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZW1vYXBwIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Im15LXNlcnZpY2UtYWNjb3VudC1zZWNyZXQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoibXktc2VydmljZS1hY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiYjU5YTg1ZmQtMDE0Mi00MjgwLWFiMDUtZTYxNmZjMDhkYWU0Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlbW9hcHA6bXktc2VydmljZS1hY2NvdW50In0.vWFHpNhKhhGDR2ucy_LOx96RhuFrlFprcA1OkKexBF-MZImUNgp_luRQ1GYAkVO-KL182l-YFf0-yZniO5JXEbwc-qD-LpsAj4dy97lZ6HqyZPXGlCiF6XHmFHz6nl7Wei5J_gztB_dvBnTMwl3Hp4OZyACBm7yAB-OM-NIDG0hdlIavQhXXmGrZNNpEbwZnN1p_-QMySg2yN9PDhqOhRWWYzNaLMQp4eAAlHXze-A7_mi86gTVAnchTkfqmCZwR-UOggvwVOrVEcqVbp4RE_G6b1wyodmLn-k5qrX8AI0jjjxLoaeBJT94NsZidnWW-OLxcF_eZpEQ7nyWntLgwnA
当删除具有关联 Secret 的 ServiceAccount 时,Kubernetes 控制平面会自动清除该 Secret 中的长期令牌。

查看ServiceAccount和secret关联信息

# kubectl describe serviceaccount/my-service-account -n demoapp
Name:                my-service-account
Namespace:           demoapp
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              my-service-account-secret
Events:              <none>

为 Pod 配置ServiceAccount

要使用非默认的服务账号,将 Pod 的 spec.serviceAccountName 字段设置为你想用的服务账号名称。

只能在创建 Pod 时或者为新 Pod 指定模板时,你才可以设置 serviceAccountName。 你不能更新已经存在的 Pod 的 .spec.serviceAccountName 字段。
说明:
.spec.serviceAccount 字段是 .spec.serviceAccountName 的已弃用别名。 如果要从工作负载资源中删除这些字段,请在 Pod 模板上将这两个字段显式设置为空。

depoly-demoapp.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: demoappv10
    version: v1.0
  name: demoappv10
  namespace: demoapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demoapp
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: demoapp
        version: v1.0
    spec:
      containers:
      - image: ikubernetes/demoapp:v1.0
        name: demoapp
        env:
        - name: PORT
          value: "8080"
        resources: {}
      serviceAccountName: my-service-account        # serviceAccount 名称
      automountServiceAccountToken: true            # 自动挂载ServiceAccount API

查看pod使用的ServiceAccount名称

# kubectl get pods demoappv10-5fdf96fddb-xxgqz -n demoapp -o yaml |grep serviceAccountName
  serviceAccountName: my-service-account

将 ImagePullSecrets 添加到服务帐户

创建一个 imagePullSecret

# kubectl create secret docker-registry myregistrykey --docker-server=DUMMY_SERVER \
        --docker-username=DUMMY_USERNAME --docker-password=DUMMY_DOCKER_PASSWORD \
        --docker-email=DUMMY_DOCKER_EMAIL -n demoapp

查看secret

# kubectl get secrets myregistrykey -n demoapp
NAME             TYPE                              DATA    AGE
myregistrykey    kubernetes.io/.dockerconfigjson   1       1d

将 ImagePullSecrets 添加到服务帐户

# kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}' -n demoapp

验证 imagePullSecrets

# kubectl get pod demoappv10-5fdf96fddb-xxgqz -n demoapp -o yaml |grep imagePullSecrets
  imagePullSecrets:myregistrykey

删除/废止 ServiceAccount 令牌

# kubectl delete secret my-service-account-secret -n demoapp
secret "my-service-account-secret" deleted

参考文档

https://kubernetes.io/docs/concepts/security/service-accounts/