管理服务帐户
ServiceAccount 为在 Pod 中运行的进程提供身份。
Pod 内部的进程可以使用其关联的服务帐户的身份来向集群的 API 服务器进行身份验证。
有关服务帐户的介绍,请阅读 配置服务帐户。
本任务指南解释了 ServiceAccounts 背后的某些概念。本指南还解释了如何获取或撤销表示 ServiceAccounts 的令牌。
开始之前
您需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与您的集群通信。建议在至少有两个节点的集群上运行本教程,这些节点不充当控制平面主机。如果您还没有集群,可以使用 minikube 创建一个,或者您可以使用以下 Kubernetes 游乐场之一
为了能够完全按照这些步骤操作,请确保您有一个名为 examplens
的命名空间。如果没有,请运行以下命令创建一个:
kubectl create namespace examplens
用户帐户与服务帐户
Kubernetes 区分用户帐户和服务帐户的概念,原因如下
- 用户帐户用于人类。服务帐户用于应用程序进程,这些进程(对于 Kubernetes)在作为 Pod 的一部分的容器中运行。
- 用户帐户旨在是全局的:名称在集群的所有命名空间中必须是唯一的。无论您查看哪个命名空间,代表用户的特定用户名都代表同一个用户。在 Kubernetes 中,服务帐户是命名空间的:两个不同的命名空间可以包含具有相同名称的 ServiceAccounts。
- 通常,集群的用户帐户可能会与企业数据库同步,在企业数据库中,创建新用户帐户需要特殊权限,并且与复杂的业务流程相关联。相比之下,服务帐户创建旨在更轻量级,允许集群用户根据需要为特定任务创建服务帐户。将 ServiceAccount 创建与入职人类用户的步骤分开,使工作负载更容易遵循最小权限原则。
- 对人类和服务帐户的审计考虑因素可能会有所不同;这种分离使实现这一点变得更容易。
- 复杂系统的配置包可能包括为该系统组件定义各种服务帐户。由于服务帐户可以在没有太多约束的情况下创建,并且具有命名空间名称,因此此类配置通常是可移植的。
绑定服务帐户令牌
ServiceAccount 令牌可以绑定到 kube-apiserver 中存在的 API 对象。这可用于将令牌的有效性绑定到另一个 API 对象的存在。支持的对象类型如下
- Pod(用于投影卷挂载,见下文)
- Secret(可用于通过删除 Secret 来允许撤销令牌)
- Node(在 v1.30 中,创建新的节点绑定令牌为 alpha,使用现有的节点绑定令牌为 beta)
当令牌绑定到对象时,对象的 metadata.name
和 metadata.uid
将作为额外的“私有声明”存储在发行的 JWT 中。
当绑定令牌被提交给 kube-apiserver 时,服务帐户身份验证器将提取并验证这些声明。如果引用的对象不再存在(或其 metadata.uid
不匹配),则请求将不会被身份验证。
Pod 绑定令牌中的其他元数据
Kubernetes v1.30 [beta]
当服务帐户令牌绑定到 Pod 对象时,其他元数据也会嵌入到令牌中,指示绑定 Pod 的 spec.nodeName
字段的值,以及该节点的 uid(如果可用)。
此节点信息不会在令牌用于身份验证时由 kube-apiserver 验证。它被包含在内,以便集成者在检查 JWT 时不必获取 Pod 或 Node API 对象来检查关联的节点名称和 uid。
验证和检查私有声明
TokenReview
API 可用于验证和提取令牌中的私有声明
- 首先,假设您有一个名为
test-pod
的 Pod 和一个名为my-sa
的服务帐户。 - 创建一个绑定到此 Pod 的令牌
kubectl create token my-sa --bound-object-kind="Pod" --bound-object-name="test-pod"
- 将此令牌复制到名为
tokenreview.yaml
的新文件中
apiVersion: authentication.k8s.io/v1
kind: TokenReview
spec:
token: <token from step 2>
- 将此资源提交给 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
注意
尽管使用kubectl create -f
创建此资源,并将它定义为类似于 Kubernetes 中的其他资源类型,但 TokenReview 是一种特殊类型,kube-apiserver 实际上不会将 TokenReview 对象持久保存到 etcd 中。因此,kubectl get tokenreview
不是有效的命令。绑定服务帐户令牌卷机制
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
该清单片段定义了一个投影卷,该卷由三个来源组成。在本例中,每个来源也代表该卷中的单个路径。三个来源是
- 一个
serviceAccountToken
来源,其中包含 kubelet 从 kube-apiserver 获取的令牌。kubelet 使用 TokenRequest API 获取有时间限制的令牌。为 TokenRequest 提供的服务令牌在 Pod 被删除或在定义的生命周期后过期(默认情况下,为 1 小时)。kubelet 也会在令牌过期之前刷新该令牌。该令牌绑定到特定 Pod,并且其受众是 kube-apiserver。这种机制取代了早期基于 Secret 的机制,其中 Secret 代表 Pod 的 ServiceAccount,但不会过期。 - 一个
configMap
来源。ConfigMap 包含一组证书颁发机构数据。Pod 可以使用这些证书来确保它们连接到集群的 kube-apiserver(而不是中间盒或意外配置错误的同级)。 - 一个
downwardAPI
来源,它查找包含 Pod 的命名空间的名称,并将该名称信息提供给在 Pod 中运行的应用程序代码。
Pod 中的任何容器都可以挂载此特定卷,从而访问上述信息。
注意
没有特定机制来使通过 TokenRequest 发行的令牌失效。如果您不再信任 Pod 的绑定服务帐户令牌,则可以删除该 Pod。删除 Pod 会使它的绑定服务帐户令牌过期。ServiceAccounts 的手动 Secret 管理
v1.22 之前的 Kubernetes 版本会自动创建用于访问 Kubernetes API 的凭据。这种旧的机制是基于创建令牌 Secret,然后可以将这些 Secret 挂载到正在运行的 Pod 中。
在包括 Kubernetes v1.30 在内的更新版本中,API 凭据是 直接获取的,使用 TokenRequest API,并使用投影卷挂载到 Pod 中。使用此方法获取的令牌具有绑定生命周期,并在它们被挂载到的 Pod 被删除时自动失效。
您仍然可以 手动创建 Secret 来保存服务帐户令牌;例如,如果您需要一个永远不会过期的令牌。
手动创建 Secret 并将其链接到 ServiceAccount 后,Kubernetes 控制平面会自动将令牌填充到该 Secret 中。
注意
尽管存在手动创建长期 ServiceAccount 令牌的机制,但建议使用 TokenRequest 来获取短期 API 访问令牌。自动生成的旧版 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 令牌在一段时间内(默认设置为一年)保持未使用,则会被标记为无效。如果令牌在此定义的时间段内(默认情况下为一年)继续保持未使用,则随后会被控制平面清除。
如果用户使用无效的自动生成的令牌,令牌验证器将
- 为键值对
authentication.k8s.io/legacy-token-invalidated: <secret name>/<namespace>
添加审计注释, - 增加
invalid_legacy_auto_token_uses_total
指标计数, - 使用新日期更新 Secret 标签
kubernetes.io/legacy-token-last-used
, - 返回一个错误,指示令牌已被失效。
收到此验证错误后,用户可以更新 Secret 以删除 kubernetes.io/legacy-token-invalid-since
标签,以暂时允许使用此令牌。
以下是一个带有 kubernetes.io/legacy-token-last-used
和 kubernetes.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。
令牌控制器
服务帐户令牌控制器作为 kube-controller-manager
的一部分运行。此控制器异步运行。它
- 监视 ServiceAccount 删除,并删除所有相应的 ServiceAccount 令牌 Secret。
- 监视 ServiceAccount 令牌 Secret 添加,并确保引用的 ServiceAccount 存在,并在需要时向 Secret 添加令牌。
- 监视 Secret 删除,并在需要时从相应的 ServiceAccount 中删除引用。
您必须使用 --service-account-private-key-file
标志将服务帐户私钥文件传递给 kube-controller-manager
中的令牌控制器。私钥用于签署生成的 ServiceAccount 令牌。类似地,您必须使用 --service-account-key-file
标志将相应的公钥传递给 kube-apiserver
。公钥将用于在身份验证期间验证令牌。
ServiceAccount 准入控制器
Pod 的修改是通过一个名为 准入控制器 的插件实现的。它是 API 服务器的一部分。此准入控制器同步运行,以在创建 Pod 时修改它们。当此插件处于活动状态(并且在大多数发行版中默认处于活动状态)时,它会在创建 Pod 时执行以下操作
- 如果 Pod 没有设置
.spec.serviceAccountName
,则准入控制器会将此传入 Pod 的 ServiceAccount 名称设置为default
。 - 准入控制器确保传入 Pod 引用的 ServiceAccount 存在。如果没有与匹配名称的 ServiceAccount,则准入控制器会拒绝传入的 Pod。此检查甚至适用于
default
ServiceAccount。 - 如果 ServiceAccount 的
automountServiceAccountToken
字段或 Pod 的automountServiceAccountToken
字段均未设置为false
- 则准入控制器会修改传入的 Pod,添加一个额外的 卷,其中包含用于 API 访问的令牌。
- 准入控制器会向 Pod 中的每个容器添加一个
volumeMount
,跳过任何已经为路径/var/run/secrets/kubernetes.io/serviceaccount
定义了卷挂载的容器。对于 Linux 容器,该卷挂载在/var/run/secrets/kubernetes.io/serviceaccount
;在 Windows 节点上,挂载位于等效路径。
- 如果传入 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 中的日期晚 指定时间段,则清理器会循环遍历集群中的 Secret 列表,并评估每个类型为 kubernetes.io/service-account-token
的 Secret。
如果 Secret 满足以下所有条件,则清理器会将其标记为无效
- Secret 是自动生成的,这意味着它与 ServiceAccount 双向引用。
- Secret 当前未被任何 Pod 挂载。
- Secret 自创建或上次使用以来,在 指定时间段 内未被使用。
清理器通过向 Secret 添加名为 kubernetes.io/legacy-token-invalid-since
的标签来标记 Secret 无效,并将当前日期作为值。如果无效的 Secret 在 指定时间段 内未被使用,则清理器会将其删除。
注意
以上所有 指定时间段 的默认值为一年。集群管理员可以通过kube-controller-manager
组件的 --legacy-service-account-token-clean-up-period
命令行参数配置此值。TokenRequest API
Kubernetes v1.22 [稳定]
您可以使用 ServiceAccount 的 TokenRequest 子资源来获取该 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
该清单片段定义了一个投影卷,该卷组合了来自三个来源的信息
- 一个
serviceAccountToken
源,其中包含 kubelet 从 kube-apiserver 获取的令牌。kubelet 使用 TokenRequest API 获取时间绑定令牌。为 TokenRequest 提供的服务令牌在 Pod 被删除或在定义的生命周期(默认情况下为 1 小时)后过期。令牌绑定到特定 Pod,并以 kube-apiserver 作为其受众。 - 一个
configMap
来源。ConfigMap 包含一组证书颁发机构数据。Pod 可以使用这些证书来确保它们连接到集群的 kube-apiserver(而不是中间盒或意外配置错误的同级)。 - 一个
downwardAPI
源。此downwardAPI
卷使包含 Pod 的命名空间的名称可供在 Pod 内运行的应用程序代码访问。
Pod 中的任何容器都可以挂载此卷,并访问上述信息。
创建额外的 API 令牌
注意
只有在 令牌请求 机制不适合的情况下,才创建长期有效的 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 的secrets
字段中引用手动创建的 Secret。否则,如果手动创建的 Secret 长时间未使用,它将被清理。请参阅 自动生成的旧版 ServiceAccount 令牌清理。删除/使 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
下一步
- 阅读有关 投影卷 的更多详细信息。