服务账号

了解 Kubernetes 中的 ServiceAccount 对象。

本页介绍 Kubernetes 中的 ServiceAccount 对象, 讲述服务账号的工作原理、使用场景、限制、替代方案,还提供了一些资源链接方便查阅更多指导信息。

什么是服务账号?

服务账号是在 Kubernetes 中一种用于非人类用户的账号,在 Kubernetes 集群中提供不同的身份标识。 应用 Pod、系统组件以及集群内外的实体可以使用特定 ServiceAccount 的凭据来将自己标识为该 ServiceAccount。 这种身份可用于许多场景,包括向 API 服务器进行身份认证或实现基于身份的安全策略。

服务账号以 ServiceAccount 对象的形式存在于 API 服务器中。服务账号具有以下属性:

  • 名字空间限定: 每个服务账号都与一个 Kubernetes 名字空间绑定。 每个名字空间在创建时,会获得一个名为 default 的 ServiceAccount

  • 轻量级: 服务账号存在于集群中,并在 Kubernetes API 中定义。你可以快速创建服务账号以支持特定任务。

  • 可移植性: 复杂的容器化工作负载的配置包中可能包括针对系统组件的服务账号定义。 服务账号的轻量级性质和名字空间作用域的身份使得这类配置可移植。

服务账号与用户账号不同,用户账号是集群中通过了身份认证的人类用户。默认情况下, 用户账号不存在于 Kubernetes API 服务器中;相反,API 服务器将用户身份视为不透明数据。 你可以使用多种方法认证为某个用户账号。某些 Kubernetes 发行版可能会添加自定义扩展 API 来在 API 服务器中表示用户账号。

服务账号与用户之间的比较
描述服务账号用户或组
位置Kubernetes API(ServiceAccount 对象)外部
访问控制Kubernetes RBAC 或其他鉴权机制Kubernetes RBAC 或其他身份和访问管理机制
目标用途工作负载、自动化工具

默认服务账号

在你创建集群时,Kubernetes 会自动为集群中的每个名字空间创建一个名为 default 的 ServiceAccount 对象。 在启用了基于角色的访问控制(RBAC)时,Kubernetes 为所有通过了身份认证的主体赋予 默认 API 发现权限。 每个名字空间中的 default 服务账号除了这些权限之外,默认没有其他访问权限。 如果基于角色的访问控制(RBAC)被启用,当你删除名字空间中的 default ServiceAccount 对象时, 控制平面会用新的 ServiceAccount 对象替换它。

如果你在某个名字空间中部署 Pod,并且你没有手动为 Pod 指派 ServiceAccount, Kubernetes 将该名字空间的 default 服务账号指派给这一 Pod。

Kubernetes 服务账号的使用场景

一般而言,你可以在以下场景中使用服务账号来提供身份标识:

  • 你的 Pod 需要与 Kubernetes API 服务器通信,例如在以下场景中:

    • 提供对存储在 Secret 中的敏感信息的只读访问。
    • 授予跨名字空间访问的权限,例如允许 example 名字空间中的 Pod 读取、列举和监视 kube-node-lease 名字空间中的 Lease 对象。
  • 你的 Pod 需要与外部服务进行通信。例如,工作负载 Pod 需要一个身份来访问某商业化的云 API, 并且商业化 API 的提供商允许配置适当的信任关系。

  • 使用 imagePullSecret 完成在私有镜像仓库上的身份认证

  • 外部服务需要与 Kubernetes API 服务器进行通信。例如,作为 CI/CD 流水线的一部分向集群作身份认证。

  • 你在集群中使用了第三方安全软件,该软件依赖不同 Pod 的 ServiceAccount 身份,按不同上下文对这些 Pod 分组。

如何使用服务账号

要使用 Kubernetes 服务账号,你需要执行以下步骤:

  1. 使用像 kubectl 这样的 Kubernetes 客户端或定义对象的清单(manifest)创建 ServiceAccount 对象。
  2. 使用鉴权机制(如 RBAC)为 ServiceAccount 对象授权。

  3. 在创建 Pod 期间将 ServiceAccount 对象指派给 Pod。

    如果你所使用的是来自外部服务的身份,可以获取 ServiceAccount 令牌,并在该服务中使用这一令牌。

有关具体操作说明,参阅为 Pod 配置服务账号

为 ServiceAccount 授权

你可以使用 Kubernetes 内置的 基于角色的访问控制 (RBAC)机制来为每个服务账号授予所需的最低权限。 你可以创建一个用来授权的角色,然后将此角色绑定到你的 ServiceAccount 上。 RBAC 可以让你定义一组最低权限,使得服务账号权限遵循最小特权原则。 这样使用服务账号的 Pod 不会获得超出其正常运行所需的权限。

有关具体操作说明,参阅 ServiceAccount 权限

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

你可以使用 RBAC 允许一个名字空间中的服务账号对集群中另一个名字空间的资源执行操作。 例如,假设你在 dev 名字空间中有一个服务账号和一个 Pod,并且希望该 Pod 可以查看 maintenance 名字空间中正在运行的 Job。你可以创建一个 Role 对象来授予列举 Job 对象的权限。 随后在 maintenance 名字空间中创建 RoleBinding 对象将 Role 绑定到此 ServiceAccount 对象上。 现在,dev 名字空间中的 Pod 可以使用该服务账号列出 maintenance 名字空间中的 Job 对象集合。

将 ServiceAccount 指派给 Pod

要将某 ServiceAccount 指派给某 Pod,你需要在该 Pod 的规约中设置 spec.serviceAccountName 字段。 Kubernetes 将自动为 Pod 提供该 ServiceAccount 的凭据。在 Kubernetes v1.22 及更高版本中, Kubernetes 使用 TokenRequest API 获取一个短期的、自动轮换的令牌, 并以投射卷的形式挂载此令牌。

默认情况下,Kubernetes 会将所指派的 ServiceAccount (无论是 default 服务账号还是你指定的定制 ServiceAccount)的凭据提供给 Pod。

要防止 Kubernetes 自动注入指定的 ServiceAccount 或 default ServiceAccount 的凭据, 可以将 Pod 规约中的 automountServiceAccountToken 字段设置为 false

在 Kubernetes 1.22 之前的版本中,Kubernetes 会将一个长期有效的静态令牌以 Secret 形式提供给 Pod。

手动获取 ServiceAccount 凭据

如果你需要 ServiceAccount 的凭据并将其挂载到非标准位置,或者用于 API 服务器之外的受众,可以使用以下方法之一:

  • TokenRequest API(推荐): 在你自己的应用代码中请求一个短期的服务账号令牌。此令牌会自动过期,并可在过期时被轮换。 如果你有一个旧的、对 Kubernetes 无感知能力的应用,你可以在同一个 Pod 内使用边车容器来获取这些令牌,并将其提供给应用工作负载。

  • 令牌卷投射(同样推荐): 在 Kubernetes v1.20 及更高版本中,使用 Pod 规约告知 kubelet 将服务账号令牌作为投射卷添加到 Pod 中。 所投射的令牌会自动过期,在过期之前 kubelet 会自动轮换此令牌。

  • 服务账号令牌 Secret(不推荐): 你可以将服务账号令牌以 Kubernetes Secret 的形式挂载到 Pod 中。这些令牌不会过期且不会轮换。 不推荐使用此方法,特别是在大规模场景下,这是因为静态、长期有效的凭据存在一定的风险。在 Kubernetes v1.24 及更高版本中, LegacyServiceAccountTokenNoAutoGeneration 特性门控阻止 Kubernetes 自动为 ServiceAccount 创建这些令牌。LegacyServiceAccountTokenNoAutoGeneration 默认被启用, 也就是说,Kubernetes 不会创建这些令牌。

说明:

对于运行在 Kubernetes 集群外的应用,你可能考虑创建一个长期有效的 ServiceAccount 令牌, 并将其存储在 Secret 中。尽管这种方式可以实现身份认证,但 Kubernetes 项目建议你避免使用此方法。 长期有效的持有者令牌(Bearer Token)会带来安全风险,一旦泄露,此令牌就可能被滥用。 为此,你可以考虑使用其他替代方案。例如,你的外部应用可以使用一个保护得很好的私钥和证书进行身份认证, 或者使用你自己实现的身份认证 Webhook 这类自定义机制。

你还可以使用 TokenRequest 为外部应用获取短期的令牌。

限制对 Secret 的访问

Kubernetes 提供了名为 kubernetes.io/enforce-mountable-secrets 的注解, 你可以添加到你的 ServiceAccount 中。当应用了这个注解后, ServiceAccount 的 Secret 只能挂载到特定类型的资源上,从而增强集群的安全性。

你可以使用以下清单将注解添加到一个 ServiceAccount 中:

  1. apiVersion: v1
  2. kind: ServiceAccount
  3. metadata:
  4. annotations:
  5. kubernetes.io/enforce-mountable-secrets: "true"
  6. name: my-serviceaccount
  7. namespace: my-namespace

当此注解设置为 “true” 时,Kubernetes 控制平面确保来自该 ServiceAccount 的 Secret 受到特定挂载限制。

  1. 在 Pod 中作为卷挂载的每个 Secret 的名称必须列在该 Pod 中 ServiceAccount 的 secrets 字段中。

  2. 在 Pod 中使用 envFrom 引用的每个 Secret 的名称也必须列在该 Pod 中 ServiceAccount 的 secrets 字段中。

  3. 在 Pod 中使用 imagePullSecrets 引用的每个 Secret 的名称也必须列在该 Pod 中 ServiceAccount 的 secrets 字段中。

通过理解并执行这些限制,集群管理员可以维护更严格的安全配置,并确保 Secret 仅被适当的资源访问。

对服务账号凭据进行鉴别

ServiceAccount 使用签名的 JSON Web Token (JWT) 来向 Kubernetes API 服务器以及任何其他存在信任关系的系统进行身份认证。根据令牌的签发方式 (使用 TokenRequest 限制时间或使用传统的 Secret 机制),ServiceAccount 令牌也可能有到期时间、受众和令牌开始生效的时间点。 当客户端以 ServiceAccount 的身份尝试与 Kubernetes API 服务器通信时, 客户端会在 HTTP 请求中包含 Authorization: Bearer <token> 标头。 API 服务器按照以下方式检查该持有者令牌的有效性:

  1. 检查令牌签名。
  2. 检查令牌是否已过期。
  3. 检查令牌申明中的对象引用是否当前有效。
  4. 检查令牌是否当前有效。
  5. 检查受众申明。

TokenRequest API 为 ServiceAccount 生成绑定令牌。这种绑定与以该 ServiceAccount 身份运行的 的客户端(如 Pod)的生命期相关联。

对于使用 TokenRequest API 签发的令牌,API 服务器还会检查正在使用 ServiceAccount 的特定对象引用是否仍然存在, 方式是通过该对象的唯一 ID 进行匹配。 对于以 Secret 形式挂载到 Pod 中的旧有令牌,API 服务器会基于 Secret 来检查令牌。

有关身份认证过程的更多信息,参考身份认证

在自己的代码中检查服务账号凭据

如果你的服务需要检查 Kubernetes 服务账号凭据,可以使用以下方法:

Kubernetes 项目建议你使用 TokenReview API,因为当你删除某些 API 对象 (如 Secret、ServiceAccount 和 Pod)的时候,此方法将使绑定到这些 API 对象上的令牌失效。 例如,如果删除包含投射 ServiceAccount 令牌的 Pod,则集群立即使该令牌失效, 并且 TokenReview 操作也会立即失败。 如果你使用的是 OIDC 验证,则客户端将继续将令牌视为有效,直到令牌达到其到期时间戳。

你的应用应始终定义其所接受的受众,并检查令牌的受众是否与应用期望的受众匹配。 这有助于将令牌的作用域最小化,这样它只能在你的应用内部使用,而不能在其他地方使用。

替代方案

接下来