管理服务帐户
一个 ServiceAccount 为 Pod 中运行的进程提供身份。
Pod 内的进程可以使用其关联的服务帐户的身份向集群的 API 服务器进行身份验证。
有关服务帐户的介绍,请阅读 配置服务帐户。
本任务指南解释了 ServiceAccount 背后的一些概念。本指南还解释了如何获取或撤销代表 ServiceAccount 的令牌,以及如何(可选)将 ServiceAccount 的有效期绑定到 API 对象的生命周期。
开始之前
你需要一个 Kubernetes 集群,并且必须配置 kubectl 命令行工具以与你的集群进行通信。建议在至少具有两个不充当控制平面主机的节点的集群上运行本教程。如果你还没有集群,可以使用 minikube 创建一个,也可以使用这些 Kubernetes 游乐场之一
为了能够完全按照这些步骤进行操作,请确保你有一个名为 examplens
的命名空间。如果没有,请运行以下命令创建一个
kubectl create namespace examplens
用户帐户与服务帐户
Kubernetes 出于多种原因区分了用户帐户和服务帐户的概念
- 用户帐户是为人类设计的。服务帐户是为应用程序进程设计的,这些进程(对于 Kubernetes)在作为 Pod 一部分的容器中运行。
- 用户帐户旨在是全局的:名称在集群的所有命名空间中必须是唯一的。无论你查看哪个命名空间,代表用户的特定用户名都代表同一用户。在 Kubernetes 中,服务帐户是命名空间的:两个不同的命名空间可以包含具有相同名称的 ServiceAccount。
- 通常,集群的用户帐户可能与公司数据库同步,其中新用户帐户的创建需要特殊权限,并且与复杂的业务流程相关联。相比之下,服务帐户的创建旨在更加轻量级,允许集群用户按需为特定任务创建服务帐户。将 ServiceAccount 的创建与新用户加入的步骤分开,可以更轻松地使工作负载遵循最小权限原则。
- 人类和服务帐户的审计注意事项可能不同;这种分离使其更容易实现。
- 复杂系统的配置包可能包含该系统组件的各种服务帐户的定义。由于服务帐户可以在没有太多约束的情况下创建,并且具有命名空间的名称,因此这种配置通常是可移植的。
绑定的服务帐户令牌
ServiceAccount 令牌可以绑定到 kube-apiserver 中存在的 API 对象。这可以用于将令牌的有效性与另一个 API 对象的存在联系起来。支持的对象类型如下
- Pod(用于投影卷挂载,请参见下文)
- Secret(可用于通过删除 Secret 来撤销令牌)
- Node(在 v1.32 中,创建新的节点绑定令牌是 beta 版,使用现有的节点绑定令牌是 GA 版)
当令牌绑定到对象时,对象的 metadata.name
和 metadata.uid
将作为额外的“私有声明”存储在颁发的 JWT 中。
当将绑定令牌呈现给 kube-apiserver 时,服务帐户身份验证器将提取并验证这些声明。如果引用的对象或 ServiceAccount 正在等待删除(例如,由于 finalizers),则在 .metadata.deletionTimestamp
日期之后 60 秒(或更长)的任何时刻,使用该令牌进行身份验证都将失败。如果引用的对象不再存在(或其 metadata.uid
不匹配),则请求将不会被身份验证。
Pod 绑定令牌中的其他元数据
Kubernetes v1.32 [稳定]
(默认启用:true)当服务帐户令牌绑定到 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
不是有效的命令。服务帐户私有声明的架构
JWT 令牌中 Kubernetes 特定的声明的架构目前尚未记录,但可以在 Kubernetes 代码库的 serviceaccount 包中找到相关代码区域。
你可以使用标准的 JWT 解码工具来检查 JWT。以下是一个绑定到名为 my-pod
的 Pod 对象,并调度到 my-namespace
命名空间中 my-node
节点的 my-serviceaccount
ServiceAccount 的 JWT 示例
{
"aud": [
"https://my-audience.example.com"
],
"exp": 1729605240,
"iat": 1729601640,
"iss": "https://my-cluster.example.com",
"jti": "aed34954-b33a-4142-b1ec-389d6bbb4936",
"kubernetes.io": {
"namespace": "my-namespace",
"node": {
"name": "my-node",
"uid": "646e7c5e-32d6-4d42-9dbd-e504e6cbe6b1"
},
"pod": {
"name": "my-pod",
"uid": "5e0bd49b-f040-43b0-99b7-22765a53f7f3"
},
"serviceaccount": {
"name": "my-serviceaccount",
"uid": "14ee3fa4-a7e2-420f-9f9a-dbc4507c3798"
}
},
"nbf": 1729601640,
"sub": "system:serviceaccount:my-namespace:my-serviceaccount"
}
注意
此 JWT 中的 aud
和 iss
字段可能因不同的 Kubernetes 集群而异,具体取决于你的配置。
pod
和 node
声明的存在意味着此令牌绑定到 Pod 对象。在验证 Pod 绑定的 ServiceAccount 令牌时,API 服务器 不会 验证引用的 Node 对象是否存在。
在 Kubernetes 外部运行并希望执行 JWT 离线验证的服务可以使用此架构,以及配置了来自 API 服务器的 OpenID Discovery 信息的兼容 JWT 验证器,来验证呈现的 JWT,而无需使用 TokenReview API。
以这种方式验证 JWT 的服务 不验证 JWT 令牌中嵌入的声明是否是当前的且仍然有效。这意味着如果令牌绑定到某个对象,并且该对象不再存在,则该令牌仍将被视为有效(直到配置的令牌过期)。
需要保证令牌的绑定声明仍然有效的客户端 必须 使用 TokenReview API 将令牌呈现给 kube-apiserver
,以便它验证并扩展嵌入的声明,使用类似于上面的 验证和检查私有声明 部分的步骤,但使用 支持的客户端库。有关 JWT 及其结构的更多信息,请参阅 JSON Web 令牌 RFC。
绑定服务帐户令牌卷机制
Kubernetes v1.22 [稳定]
(默认启用:true)默认情况下,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,然后可以将其挂载到正在运行的 Pod 中。
在包括 Kubernetes v1.32 在内的较新版本中,API 凭据是直接获取 使用 TokenRequest API,并使用投影卷挂载到 Pod 中。使用此方法获取的令牌具有有限的生命周期,并且当它们挂载到的 Pod 被删除时会自动失效。
您仍然可以手动创建一个 Secret 来保存服务帐户令牌;例如,如果您需要一个永不过期的令牌。
一旦您手动创建了一个 Secret 并将其链接到 ServiceAccount,Kubernetes 控制平面会自动将令牌填充到该 Secret 中。
注意
虽然存在手动创建长期有效的 ServiceAccount 令牌的机制,但建议使用 TokenRequest 来获取短期的 API 访问令牌。自动生成的传统 ServiceAccount 令牌清理
在 1.24 版本之前,Kubernetes 会自动为 ServiceAccounts 生成基于 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 控制器管理命名空间内的 ServiceAccounts,并确保每个活动命名空间中都存在名为“default”的 ServiceAccount。
令牌控制器
服务帐户令牌控制器作为 kube-controller-manager
的一部分运行。此控制器异步运行。它
- 监视 ServiceAccount 删除并删除所有相应的 ServiceAccount 令牌 Secret。
- 监视 ServiceAccount 令牌 Secret 添加,并确保引用的 ServiceAccount 存在,并在需要时向 Secret 添加令牌。
- 监视 Secret 删除并在需要时从相应的 ServiceAccount 中删除引用。
您必须使用 --service-account-private-key-file
标志将服务帐户私钥文件传递给 kube-controller-manager
中的令牌控制器。私钥用于签署生成的服务帐户令牌。同样,您必须使用 --service-account-key-file
标志将相应的公钥传递给 kube-apiserver
。公钥将在身份验证期间用于验证令牌。
Kubernetes v1.32 [alpha]
(默认禁用:false)设置 --service-account-private-key-file
和 --service-account-key-file
标志的替代设置是为 外部 ServiceAccount 令牌签名和密钥管理 配置外部 JWT 签名器。请注意,这些设置是互斥的,不能一起配置。
ServiceAccount 准入控制器
Pod 的修改是通过名为准入控制器的插件实现的。它是 API 服务器的一部分。此准入控制器同步运行以在创建 Pod 时修改它们。当此插件处于活动状态时(并且在大多数发行版上默认处于活动状态),它会在创建 Pod 时执行以下操作
- 如果 Pod 没有设置
.spec.serviceAccountName
,则准入控制器会将此传入 Pod 的 ServiceAccount 名称设置为default
。 - 准入控制器确保传入 Pod 引用的 ServiceAccount 存在。如果没有具有匹配名称的 ServiceAccount,则准入控制器将拒绝传入的 Pod。该检查甚至适用于
default
ServiceAccount。 - 如果 ServiceAccount 的
automountServiceAccountToken
字段和 Pod 的automountServiceAccountToken
字段均未设置为false
- 准入控制器会更改传入的 Pod,添加一个额外的卷,其中包含用于 API 访问的令牌。
- 准入控制器会将
volumeMount
添加到 Pod 中的每个容器,跳过任何已为路径/var/run/secrets/kubernetes.io/serviceaccount
定义卷挂载的容器。对于 Linux 容器,该卷挂载在/var/run/secrets/kubernetes.io/serviceaccount
;在 Windows 节点上,挂载在等效路径上。
- 如果传入的 Pod 的规范不包含任何
imagePullSecrets
,则准入控制器会添加imagePullSecrets
,并从ServiceAccount
复制它们。
传统 ServiceAccount 令牌跟踪控制器
Kubernetes v1.28 [stable]
(默认启用:true)此控制器在 kube-system
命名空间中生成一个名为 kube-system/kube-apiserver-legacy-service-account-token-tracking
的 ConfigMap。ConfigMap 记录系统开始监视传统服务帐户令牌的时间戳。
传统 ServiceAccount 令牌清理器
Kubernetes v1.30 [stable]
(默认启用:true)传统 ServiceAccount 令牌清理器作为 kube-controller-manager
的一部分运行,并每 24 小时检查一次,以查看是否有任何自动生成的传统 ServiceAccount 令牌在指定的时间量内未使用。如果是,清理器会将这些令牌标记为无效。
清理器的工作原理是首先检查控制平面创建的 ConfigMap(前提是启用了 LegacyServiceAccountTokenTracking
)。如果当前时间在 ConfigMap 中的日期之后指定的时间量,则清理器会遍历集群中的 Secret 列表,并评估类型为 kubernetes.io/service-account-token
的每个 Secret。
如果 Secret 满足以下所有条件,则清理器会将其标记为无效
- 该 Secret 是自动生成的,这意味着它被 ServiceAccount 双向引用。
- 当前没有任何 Pod 挂载该 Secret。
- 自创建或上次使用以来,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 [stable]
您可以使用 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
该清单片段定义了一个 projected 卷,它组合了来自三个来源的信息
- 一个
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: ...
如果您在 examplens
命名空间中启动一个新的 Pod,它可以使用您刚刚创建的 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
外部 ServiceAccount 令牌签名和密钥管理
Kubernetes v1.32 [alpha]
(默认禁用:false)可以配置 kube-apiserver 以使用外部签名器进行令牌签名和令牌验证密钥管理。此功能使 Kubernetes 发行版能够与他们选择的密钥管理解决方案(例如:HSM、云 KMS)集成,以进行服务帐户凭证签名和验证。要配置 kube-apiserver 以使用 external-jwt-signer,请将 --service-account-signing-endpoint
标志设置为文件系统上的 Unix 域套接字 (UDS) 的位置,或以 @ 符号作为前缀并命名抽象套接字命名空间中的 UDS。在配置的 UDS 上,应有一个 RPC 服务器,该服务器实现ExternalJWTSigner。external-jwt-signer 必须是健康的,并且准备好为 kube-apiserver 提供支持的服务帐户密钥才能启动。
有关 ExternalJWTSigner 的更多详细信息,请查看 KEP-740。
注意
除非设置了--service-account-signing-endpoint
,否则 kube-apiserver 标志 --service-account-key-file
和 --service-account-signing-key-file
将继续用于从文件中读取;它们是支持 JWT 签名和身份验证的互斥方式。清理
如果您创建了一个名为 examplens
的命名空间用于实验,则可以将其删除
kubectl delete namespace examplens
下一步
- 阅读有关projected 卷的更多详细信息。