管理服务帐户

一个 ServiceAccount 为在 Pod 中运行的进程提供身份。

Pod 内部的进程可以使用其关联的 service account 的身份来向集群的 API 服务器进行身份验证。

有关 service account 的介绍,请阅读 配置 service account

本任务指南解释了 ServiceAccount 背后的某些概念。该指南还解释了如何获取或撤销代表 ServiceAccount 的令牌。

开始之前

您需要有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与您的集群通信。建议在至少有两个节点(不充当控制平面主机)的集群上运行本教程。如果您还没有集群,可以使用 minikube 创建一个,或者可以使用以下 Kubernetes 游乐场之一

为了能够准确地按照这些步骤进行操作,请确保您有一个名为 examplens 的命名空间。如果您没有,请通过运行以下命令创建一个

kubectl create namespace examplens

用户帐户与 service account

Kubernetes 区分用户帐户和 service account 的概念,原因有很多

  • 用户帐户是为人类准备的。service account 是为应用程序进程准备的,这些进程(对于 Kubernetes 来说)运行在作为 pod 的一部分的容器中。
  • 用户帐户旨在是全局的:名称在集群的所有命名空间中必须是唯一的。无论您查看哪个命名空间,代表用户的特定用户名都代表同一个用户。在 Kubernetes 中,service account 是有命名空间的:两个不同的命名空间可以包含具有相同名称的 ServiceAccount。
  • 通常,集群的用户帐户可能会与企业数据库同步,在该数据库中,新的用户帐户创建需要特殊权限,并与复杂的业务流程绑定在一起。相比之下,service account 创建旨在更加轻量级,允许集群用户按需为特定任务创建 service account。将 ServiceAccount 创建与入职人类用户步骤分离,使工作负载更容易遵循最小权限原则。
  • 人类和 service account 的审计注意事项可能有所不同;分离使这更容易实现。
  • 复杂系统的配置包可能包括为该系统组件定义各种 service account。因为 service account 可以不受太多约束地创建,并且具有有命名空间的名称,所以这种配置通常是可移植的。

绑定 service account 令牌

ServiceAccount 令牌可以绑定到 kube-apiserver 中存在的 API 对象。这可以用来将令牌的有效性与另一个 API 对象的存在联系起来。支持的对象类型如下

  • Pod(用于投影卷挂载,见下文)
  • Secret(可用于通过删除 Secret 来允许撤销令牌)
  • Node(在 v1.30 中,创建新的节点绑定令牌处于 Alpha 阶段,使用现有的节点绑定令牌处于 Beta 阶段)

当令牌绑定到对象时,对象的 metadata.namemetadata.uid 会作为额外的“私有声明”存储在颁发的 JWT 中。

当向 kube-apiserver 展示绑定令牌时,service account 身份验证器将提取并验证这些声明。如果引用的对象或 ServiceAccount 处于待删除状态(例如,由于终结器),那么对于 .metadata.deletionTimestamp 日期后 60 秒(或更长时间)的任何时间点,使用该令牌进行身份验证都会失败。如果引用的对象不再存在(或其 metadata.uid 不匹配),则请求将不会被身份验证。

Pod 绑定令牌中的其他元数据

功能状态: Kubernetes v1.30 [beta]

当 service account 令牌绑定到 Pod 对象时,其他元数据也会嵌入到令牌中,指示绑定 pod 的 spec.nodeName 字段的值以及该节点的 uid(如果可用)。

此节点信息在使用令牌进行身份验证时不会被 kube-apiserver 验证。它被包含在内,因此集成器在检查 JWT 时不必获取 Pod 或 Node API 对象来检查关联的节点名称和 uid。

验证和检查私有声明

TokenReview API 可用于验证和提取令牌的私有声明

  1. 首先,假设您有一个名为 test-pod 的 pod 和一个名为 my-sa 的 service account。
  2. 创建一个绑定到此 Pod 的令牌
kubectl create token my-sa --bound-object-kind="Pod" --bound-object-name="test-pod"
  1. 将此令牌复制到一个名为 tokenreview.yaml 的新文件中
apiVersion: authentication.k8s.io/v1
kind: TokenReview
spec:
  token: <token from step 2>
  1. 将此资源提交到 apiserver 以供审核
kubectl create -o yaml -f tokenreview.yaml # we use '-o yaml' so we can inspect the output

您应该会看到类似于以下的输出

apiVersion: authentication.k8s.io/v1
kind: TokenReview
metadata:
  creationTimestamp: null
spec:
  token: <token>
status:
  audiences:
  - https://kubernetes.default.svc.cluster.local
  authenticated: true
  user:
    extra:
      authentication.kubernetes.io/credential-id:
      - JTI=7ee52be0-9045-4653-aa5e-0da57b8dccdc
      authentication.kubernetes.io/node-name:
      - kind-control-plane
      authentication.kubernetes.io/node-uid:
      - 497e9d9a-47aa-4930-b0f6-9f2fb574c8c6
      authentication.kubernetes.io/pod-name:
      - test-pod
      authentication.kubernetes.io/pod-uid:
      - e87dbbd6-3d7e-45db-aafb-72b24627dff5
    groups:
    - system:serviceaccounts
    - system:serviceaccounts:default
    - system:authenticated
    uid: f8b4161b-2e2b-11e9-86b7-2afc33b31a7e
    username: system:serviceaccount:default:my-sa

绑定 service account 令牌卷机制

功能状态: Kubernetes v1.22 [稳定]

默认情况下,Kubernetes 控制平面(具体来说,是 ServiceAccount 准入控制器)会将一个 投影卷 添加到 Pod 中,并且此卷包含一个用于访问 Kubernetes API 的令牌。

以下是如何在启动的 Pod 中看到它工作的示例

...
  - name: kube-api-access-<random-suffix>
    projected:
      sources:
        - serviceAccountToken:
            path: token # must match the path the app expects
        - 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 小时)到期。kubelet 还会在令牌到期之前刷新该令牌。该令牌绑定到特定 Pod,并且其受众是 kube-apiserver。此机制取代了早期基于 Secret 的机制,其中 Secret 代表 Pod 的 ServiceAccount,但不会到期。
  2. 一个 configMap 来源。ConfigMap 包含一组证书颁发机构数据。Pod 可以使用这些证书来确保它们连接到您的集群的 kube-apiserver(而不是到中间盒或意外配置错误的对等方)。
  3. 一个 downwardAPI 来源,它查找包含 Pod 的命名空间的名称,并将该名称信息提供给 Pod 中运行的应用程序代码。

Pod 中的任何容器都可以挂载此特定卷,并访问上述信息。

手动管理 ServiceAccount 的 Secret

v1.22 之前的 Kubernetes 版本会自动创建用于访问 Kubernetes API 的凭据。此旧机制基于创建令牌 Secret,然后可以将其挂载到正在运行的 Pod 中。

在更新的版本(包括 Kubernetes v1.31)中,API 凭据 直接获取,使用 TokenRequest API,并使用投影卷挂载到 Pod 中。使用此方法获得的令牌具有有限的寿命,并且在挂载到其中的 Pod 被删除时会自动失效。

您仍然可以 手动创建 Secret 来保存 service account 令牌;例如,如果您需要一个永不到期的令牌。

一旦您手动创建 Secret 并将其链接到 ServiceAccount,Kubernetes 控制平面就会自动将令牌填充到该 Secret 中。

自动生成的传统 ServiceAccount 令牌清理

在 1.24 版本之前,Kubernetes 会自动为 ServiceAccount 生成基于 Secret 的令牌。为了区分自动生成的令牌和手动创建的令牌,Kubernetes 会检查 ServiceAccount 的 secrets 字段中的引用。如果 Secret 在 secrets 字段中被引用,则它被认为是自动生成的传统令牌。否则,它被认为是手动创建的传统令牌。例如

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
  namespace: default
secrets:
  - name: build-robot-secret # usually NOT present for a manually generated token                         

从 1.29 版本开始,如果自动生成的传统 ServiceAccount 令牌在一段时间内(默认为一年)保持未使用状态,则它们将被标记为无效。在定义的时间段内(同样,默认情况下为一年)继续保持未使用的令牌将随后被控制平面清除。

如果用户使用无效的自动生成的令牌,令牌验证器将

  1. 为键值对 authentication.k8s.io/legacy-token-invalidated: <secret name>/<namespace> 添加审计注释,
  2. 增加 invalid_legacy_auto_token_uses_total 指标计数,
  3. 使用新日期更新 Secret 标签 kubernetes.io/legacy-token-last-used
  4. 返回一个错误,指示令牌已失效。

在收到此验证错误时,用户可以更新 Secret 以删除 kubernetes.io/legacy-token-invalid-since 标签,以暂时允许使用此令牌。

以下是一个带有 kubernetes.io/legacy-token-last-usedkubernetes.io/legacy-token-invalid-since 标签的自动生成的传统令牌的示例

apiVersion: v1
kind: Secret
metadata:
  name: build-robot-secret
  namespace: default
  labels:
    kubernetes.io/legacy-token-last-used: 2022-10-24
    kubernetes.io/legacy-token-invalid-since: 2023-10-25
  annotations:
    kubernetes.io/service-account.name: build-robot
type: kubernetes.io/service-account-token

控制平面详细信息

ServiceAccount 控制器

ServiceAccount 控制器管理命名空间中的 ServiceAccount,并确保每个活动命名空间中都存在名为“default”的 ServiceAccount。

令牌控制器

service account 令牌控制器作为 kube-controller-manager 的一部分运行。此控制器异步运行。它

  • 监视 ServiceAccount 删除并删除所有相应的 ServiceAccount 令牌 Secret。
  • 监视 ServiceAccount 令牌 Secret 添加,并确保引用的 ServiceAccount 存在,并在需要时将令牌添加到 Secret 中。
  • 监视 Secret 删除,并在需要时从相应的 ServiceAccount 中删除引用。

您必须使用 --service-account-private-key-file 标志将 service account 私钥文件传递给 kube-controller-manager 中的令牌控制器。私钥用于对生成的 service account 令牌进行签名。类似地,您必须使用 --service-account-key-file 标志将相应的公钥传递给 kube-apiserver。公钥将用于在身份验证期间验证令牌。

ServiceAccount 准入控制器

Pod 的修改是通过一个名为 准入控制器 的插件实现的。它是 API 服务器的一部分。这个准入控制器同步地修改 Pod,以便在创建时修改它们。当这个插件处于活动状态(在大多数发行版中默认情况下是活动状态)时,它在创建 Pod 时会执行以下操作

  1. 如果 Pod 没有设置 .spec.serviceAccountName,准入控制器将此传入 Pod 的 ServiceAccount 名称设置为 default
  2. 准入控制器确保传入 Pod 所引用的 ServiceAccount 存在。如果没有具有匹配名称的 ServiceAccount,准入控制器将拒绝传入的 Pod。即使是 default ServiceAccount,该检查也适用。
  3. 前提是 ServiceAccount 的 automountServiceAccountToken 字段和 Pod 的 automountServiceAccountToken 字段都没有设置为 false
    • 准入控制器将改变传入的 Pod,添加一个额外的 ,其中包含用于 API 访问的令牌。
    • 准入控制器将向 Pod 中的每个容器添加一个 volumeMount,跳过任何已经为路径 /var/run/secrets/kubernetes.io/serviceaccount 定义了卷挂载的容器。对于 Linux 容器,该卷挂载在 /var/run/secrets/kubernetes.io/serviceaccount;在 Windows 节点上,挂载在等效路径。
  4. 如果传入 Pod 的规范中还没有包含任何 imagePullSecrets,那么准入控制器会添加 imagePullSecrets,将它们从 ServiceAccount 中复制过来。

旧版 ServiceAccount 令牌跟踪控制器

功能状态: Kubernetes v1.28 [稳定]

这个控制器在 kube-system 命名空间中生成一个名为 kube-system/kube-apiserver-legacy-service-account-token-tracking 的 ConfigMap。ConfigMap 记录了系统开始监控旧版 ServiceAccount 令牌的时间戳。

旧版 ServiceAccount 令牌清理器

功能状态: Kubernetes v1.30 [稳定]

旧版 ServiceAccount 令牌清理器作为 kube-controller-manager 的一部分运行,每 24 小时检查一次,查看是否有任何自动生成的旧版 ServiceAccount 令牌在 指定时间段 内没有使用。如果是,清理器会将这些令牌标记为无效。

清理器的工作原理是首先检查控制平面创建的 ConfigMap(前提是 LegacyServiceAccountTokenTracking 已启用)。如果当前时间比 ConfigMap 中的日期晚 指定时间段,清理器将循环遍历集群中的 Secrets 列表,并评估每个类型为 kubernetes.io/service-account-token 的 Secret。

如果一个 Secret 满足以下所有条件,清理器会将其标记为无效

  • Secret 是自动生成的,这意味着它与 ServiceAccount 双向引用。
  • Secret 目前没有被任何 Pod 挂载。
  • 自 Secret 创建或上次使用以来,Secret 在 指定时间段 内没有使用。

清理器通过在 Secret 上添加一个名为 kubernetes.io/legacy-token-invalid-since 的标签,将 Secret 标记为无效,并将当前日期作为值。如果一个无效的 Secret 在 指定时间段 内没有使用,清理器将删除它。

TokenRequest API

功能状态: Kubernetes v1.22 [稳定]

使用 TokenRequest,这是 ServiceAccount 的子资源,可以获取 ServiceAccount 的时间绑定令牌。你无需调用它来获取容器内使用的 API 令牌,因为 kubelet 使用 投影卷 为你设置了它。

如果你想从 kubectl 中使用 TokenRequest API,请参阅 手动为 ServiceAccount 创建 API 令牌

Kubernetes 控制平面(具体来说,是 ServiceAccount 准入控制器)向 Pod 添加一个投影卷,kubelet 确保该卷包含一个令牌,该令牌允许容器以正确的 ServiceAccount 进行身份验证。

(这种机制取代了早期的一种机制,该机制添加了基于 Secret 的卷,其中 Secret 代表 Pod 的 ServiceAccount,但不会过期。)

以下是如何在启动的 Pod 中看到它工作的示例

...
  - name: kube-api-access-<random-suffix>
    projected:
      defaultMode: 420 # decimal equivalent of octal 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 内的任何容器都可以挂载此卷,并访问上述信息。

创建其他 API 令牌

要为 ServiceAccount 创建一个不会过期的持久化 API 令牌,请创建一个类型为 kubernetes.io/service-account-token 的 Secret,并添加一个引用 ServiceAccount 的注释。然后,控制平面会生成一个长期有效的令牌,并使用生成的令牌数据更新该 Secret。

这是一个用于此类 Secret 的示例清单

apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
  name: mysecretname
  annotations:
    kubernetes.io/service-account.name: myserviceaccount

要基于此示例创建 Secret,请运行

kubectl -n examplens create -f https://k8s.io/examples/secret/serviceaccount/mysecretname.yaml

要查看该 Secret 的详细信息,请运行

kubectl -n examplens describe secret mysecretname

输出类似于

Name:           mysecretname
Namespace:      examplens
Labels:         <none>
Annotations:    kubernetes.io/service-account.name=myserviceaccount
                kubernetes.io/service-account.uid=8a85c4c4-8483-11e9-bc42-526af7764f64

Type:   kubernetes.io/service-account-token

Data
====
ca.crt:         1362 bytes
namespace:      9 bytes
token:          ...

如果你将一个新的 Pod 启动到 examplens 命名空间,它可以使用你刚刚创建的 myserviceaccount service-account-token Secret。

删除/使 ServiceAccount 令牌无效

如果你知道包含要删除的令牌的 Secret 的名称

kubectl delete secret name-of-secret

否则,首先找到 ServiceAccount 的 Secret。

# This assumes that you already have a namespace named 'examplens'
kubectl -n examplens get serviceaccount/example-automated-thing -o yaml

输出类似于

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"example-automated-thing","namespace":"examplens"}}      
  creationTimestamp: "2019-07-21T07:07:07Z"
  name: example-automated-thing
  namespace: examplens
  resourceVersion: "777"
  selfLink: /api/v1/namespaces/examplens/serviceaccounts/example-automated-thing
  uid: f23fd170-66f2-4697-b049-e1e266b7f835
secrets:
  - name: example-automated-thing-token-zyxwv

然后,删除你现在知道的名称的 Secret

kubectl -n examplens delete secret/example-automated-thing-token-zyxwv

清理

如果你创建了一个 examplens 命名空间来进行实验,你可以将其删除

kubectl delete namespace examplens

下一步

  • 阅读有关 投影卷 的更多详细信息。
上次修改时间:2024 年 8 月 9 日上午 10:06 PST:应用代码审查的建议 (300519c850)