Kubernetes 为在集群内运行的客户端,或与集群 控制平面 有某种关联的客户端,提供了两种截然不同的方式来验证其对 API 服务器 的访问权限。
服务账户 (ServiceAccount) 为 Pod 中运行的进程提供身份,并映射到 ServiceAccount 对象。当你向 API 服务器进行身份验证时,你将自己标识为一个特定的 用户。Kubernetes 承认用户的概念,然而,Kubernetes 本身并**没有**用户 API。
本任务指南介绍了 ServiceAccount,它们存在于 Kubernetes API 中。本指南将向你展示为 Pod 配置 ServiceAccount 的一些方法。
你需要有一个 Kubernetes 集群,并且必须配置 kubectl 命令行工具以与你的集群通信。建议在至少有两个节点的集群上运行本教程,且这些节点不能作为控制平面主机。如果你还没有集群,可以通过 minikube 创建一个,或者使用以下 Kubernetes 演练场之一。
当 Pod 联系 API 服务器时,它们会以特定的 ServiceAccount(例如 default)进行身份验证。每个 命名空间 中始终至少有一个 ServiceAccount。
每个 Kubernetes 命名空间都包含至少一个 ServiceAccount:该命名空间的默认 ServiceAccount,名为 default。如果你在创建 Pod 时没有指定 ServiceAccount,Kubernetes 会自动将该命名空间中名为 default 的 ServiceAccount 分配给它。
你可以获取已创建 Pod 的详细信息。例如:
kubectl get pods/<podname> -o yaml
在输出中,你会看到一个 spec.serviceAccountName 字段。如果你在创建 Pod 时没有指定该值,Kubernetes 会自动设置它。
在 Pod 内运行的应用程序可以使用自动挂载的服务账户凭据访问 Kubernetes API。请参阅 访问集群 以了解更多信息。
当 Pod 以 ServiceAccount 身份进行验证时,其访问级别取决于所使用的 授权插件和策略。
即使存在终结器 (finalizers),API 凭据也会在 Pod 被删除时自动撤销。具体来说,API 凭据会在 Pod 上设置的 .metadata.deletionTimestamp 之后 60 秒撤销(删除时间戳通常是 删除 请求被接受的时间加上 Pod 的终止宽限期)。
如果你不希望 kubelet 自动挂载 ServiceAccount 的 API 凭据,你可以选择退出默认行为。你可以通过在 ServiceAccount 上设置 automountServiceAccountToken: false,选择不将 API 凭据自动挂载到 /var/run/secrets/kubernetes.io/serviceaccount/token。
例如
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 的规格为准。
每个命名空间至少有一个 ServiceAccount:名为 default 的默认 ServiceAccount 资源。你可以通过以下命令列出 当前命名空间 中的所有 ServiceAccount 资源:
kubectl get serviceaccounts
输出类似于此
NAME SECRETS AGE
default 1 1d
你可以像这样创建额外的 ServiceAccount 对象:
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: build-robot
EOF
ServiceAccount 对象的名称必须是有效的 DNS 子域名。
如果你获取了 ServiceAccount 对象的完整转储,如下所示:
kubectl get serviceaccounts/build-robot -o yaml
输出类似于此
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: 2019-06-16T00:12:34Z
name: build-robot
namespace: default
resourceVersion: "272500"
uid: 721ab723-13bc-11e5-aec2-42010af0021e
你可以使用授权插件来 设置服务账户的权限。
要使用非默认的服务账户,请将 Pod 的 spec.serviceAccountName 字段设置为你希望使用的 ServiceAccount 名称。
你只能在创建 Pod 时,或在新 Pod 的模板中设置 serviceAccountName 字段。你不能更新已存在 Pod 的 .spec.serviceAccountName 字段。
.spec.serviceAccount 字段是 .spec.serviceAccountName 的已弃用别名。如果你想从工作负载资源中移除这些字段,请在 Pod 模板 上显式地将这两个字段都设置为空。如果你尝试创建了上述示例中的 build-robot ServiceAccount,你可以通过运行以下命令来清理它:
kubectl delete serviceaccount/build-robot
假设你有一个如前所述名为 "build-robot" 的现有服务账户。
你可以使用 kubectl 获取该 ServiceAccount 的限时 API 令牌:
kubectl create token build-robot
该命令的输出是一个可用于以该 ServiceAccount 身份进行验证的令牌。你可以使用 kubectl create token 命令的 --duration 命令行参数请求特定的令牌持续时间(实际颁发的令牌持续时间可能会更短,甚至可能更长)。
Kubernetes v1.33 [稳定](默认启用)使用 kubectl v1.31 或更高版本,可以创建直接绑定到节点的服务账户令牌:
kubectl create token build-robot --bound-object-kind Node --bound-object-name node-001 --bound-object-uid 123...456
该令牌将在过期、关联的节点或服务账户被删除之前有效。
Kubernetes v1.22 之前的版本会自动创建用于访问 Kubernetes API 的长期凭据。这种较旧的机制基于创建可以挂载到运行中 Pod 的令牌 Secret。在包括 Kubernetes v1.36 在内的较新版本中,API 凭据是直接通过使用 TokenRequest API 获取的,并使用 投影卷 (projected volume) 挂载到 Pod 中。使用此方法获取的令牌具有有限的生命周期,并且在挂载它们的 Pod 被删除时会自动失效。
你仍然可以手动创建服务账户令牌 Secret;例如,如果你需要一个永不过期的令牌。但是,建议改用 TokenRequest 子资源来获取访问 API 的令牌。
如果你想获取 ServiceAccount 的 API 令牌,可以创建一个带有特殊注解 kubernetes.io/service-account.name 的新 Secret。
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: build-robot-secret
annotations:
kubernetes.io/service-account.name: build-robot
type: kubernetes.io/service-account-token
EOF
如果你使用以下命令查看 Secret:
kubectl get secret/build-robot-secret -o yaml
你可以看到该 Secret 现在包含了 "build-robot" ServiceAccount 的 API 令牌。
由于你设置了该注解,控制平面会自动为该 ServiceAccount 生成一个令牌,并将其存储到关联的 Secret 中。控制平面还会清理已删除 ServiceAccount 的令牌。
kubectl describe secrets/build-robot-secret
输出类似于此
Name: build-robot-secret
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: build-robot
kubernetes.io/service-account.uid: da68f9c6-9d26-11e7-b84e-002dc52800da
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1338 bytes
namespace: 7 bytes
token: ...
此处省略了 token 的内容。
请注意,不要将 kubernetes.io/service-account-token Secret 的内容显示在旁观者可以看到你的终端/电脑屏幕的地方。
当你删除具有关联 Secret 的 ServiceAccount 时,Kubernetes 控制平面会自动清理该 Secret 中的长期令牌。
如果你使用以下命令查看 ServiceAccount:
kubectl get serviceaccount build-robot -o yaml
你无法在 ServiceAccount API 对象的 .secrets 字段中看到 build-robot-secret Secret,因为该字段仅填充自动生成的 Secret。
首先,创建一个 imagePullSecret。接下来,验证它是否已创建。例如:
按照 在 Pod 上指定 ImagePullSecrets 中所述,创建一个 imagePullSecret。
kubectl create secret docker-registry myregistrykey --docker-server=<registry name> \
--docker-username=DUMMY_USERNAME --docker-password=DUMMY_DOCKER_PASSWORD \
--docker-email=DUMMY_DOCKER_EMAIL
验证它是否已创建。
kubectl get secrets myregistrykey
输出类似于此
NAME TYPE DATA AGE
myregistrykey kubernetes.io/.dockerconfigjson 1 1d
接下来,修改命名空间的默认服务账户,以使用此 Secret 作为 imagePullSecret。
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'
你可以通过手动编辑对象来实现相同的结果:
kubectl edit serviceaccount/default
sa.yaml 文件的输出类似于:
你选择的文本编辑器将打开,其中包含类似这样的配置:
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: 2021-07-07T22:02:39Z
name: default
namespace: default
resourceVersion: "243024"
uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6
使用编辑器,删除 resourceVersion 键所在的行,添加 imagePullSecrets: 行,然后保存。保留 uid 值与你发现的一致。
进行这些更改后,编辑后的 ServiceAccount 看起来类似于:
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: 2021-07-07T22:02:39Z
name: default
namespace: default
uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6
imagePullSecrets:
- name: myregistrykey
现在,当在当前命名空间中创建使用默认 ServiceAccount 的新 Pod 时,新 Pod 会自动设置其 spec.imagePullSecrets 字段。
kubectl run nginx --image=<registry name>/nginx --restart=Never
kubectl get pod nginx -o=jsonpath='{.spec.imagePullSecrets[0].name}{"\n"}'
输出如下:
myregistrykey
Kubernetes v1.20 [稳定]要启用并使用令牌请求投影,你必须向 kube-apiserver 指定以下每个命令行参数:
--service-account-issuer--service-account-issuer 参数,这对于实现无中断的颁发者更改非常有用。当多次指定此标志时,第一个标志用于生成令牌,所有标志都用于确定哪些颁发者是被接受的。你必须运行 Kubernetes v1.22 或更高版本才能多次指定 --service-account-issuer。--service-account-key-file--service-account-signing-key-file--api-audiences(可省略)api-audiences,Kubernetes API 服务器会将任何指定受众的令牌视为有效。如果你指定了 --service-account-issuer 命令行参数但没有设置 --api-audiences,控制平面将默认使用仅包含颁发者 URL 的单元素受众列表。kubelet 也可以将 ServiceAccount 令牌投影到 Pod 中。你可以指定令牌的所需属性,例如受众和有效期。这些属性在默认的 ServiceAccount 令牌上是不可配置的。当 Pod 或 ServiceAccount 被删除时,该令牌在 API 上也会失效。
你可以使用称为 ServiceAccountToken 的 投影卷 (projected volume) 类型,为 Pod 的 spec 配置此行为。
来自此投影卷的令牌是一个 JSON Web 令牌 (JWT)。该令牌的 JSON 有效负载遵循定义明确的模式——Pod 绑定令牌的有效负载示例:
{
"aud": [ # matches the requested audiences, or the API server's default audiences when none are explicitly requested
"https://kubernetes.default.svc"
],
"exp": 1731613413,
"iat": 1700077413,
"iss": "https://kubernetes.default.svc", # matches the first value passed to the --service-account-issuer flag
"jti": "ea28ed49-2e11-4280-9ec5-bc3d1d84661a",
"kubernetes.io": {
"namespace": "kube-system",
"node": {
"name": "127.0.0.1",
"uid": "58456cb0-dd00-45ed-b797-5578fdceaced"
},
"pod": {
"name": "coredns-69cbfb9798-jv9gn",
"uid": "778a530c-b3f4-47c0-9cd5-ab018fb64f33"
},
"serviceaccount": {
"name": "coredns",
"uid": "a087d5a0-e1dd-43ec-93ac-f13d89cd13af"
},
"warnafter": 1700081020
},
"nbf": 1700077413,
"sub": "system:serviceaccount:kube-system:coredns"
}
要为 Pod 提供受众为 vault 且有效期为两小时的令牌,你可以定义一个类似以下的 Pod 清单:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- mountPath: /var/run/secrets/tokens
name: vault-token
serviceAccountName: build-robot
volumes:
- name: vault-token
projected:
sources:
- serviceAccountToken:
path: vault-token
expirationSeconds: 7200
audience: vault
创建 Pod
kubectl create -f https://k8s.io/examples/pods/pod-projected-svc-token.yaml
kubelet 将:代表 Pod 请求并存储令牌;使令牌在可配置的文件路径下对 Pod 可用;并在令牌接近过期时刷新它。如果令牌的生存时间 (TTL) 超过 80%,或者令牌已超过 24 小时,kubelet 会主动请求轮换该令牌。
应用程序负责在令牌轮换时重新加载它。通常,应用程序按计划(例如:每 5 分钟一次)加载令牌,而不跟踪实际过期时间,这就足够了。
Kubernetes v1.21 [稳定]如果你已在集群中为 ServiceAccount 启用了 令牌投影 (token projection),那么你也可以使用发现功能。Kubernetes 提供了一种客户端联合作为 身份提供者 (identity provider) 的方式,以便一个或多个外部系统可以作为 信赖方 (relying party) 行事。
颁发者 URL 必须符合 OIDC 发现规范。实际上,这意味着它必须使用 https 方案,并应在 {service-account-issuer}/.well-known/openid-configuration 处提供 OpenID 提供者配置。
如果 URL 不符合要求,则 ServiceAccount 颁发者发现端点将不会被注册或访问。
启用后,Kubernetes API 服务器会通过 HTTP 发布 OpenID 提供者配置文档。该配置文档发布在 /.well-known/openid-configuration。OpenID 提供者配置有时被称为 发现文档 (discovery document)。Kubernetes API 服务器还会通过 HTTP 在 /openid/v1/jwks 处发布相关的 JSON Web 密钥集 (JWKS)。
/.well-known/openid-configuration 和 /openid/v1/jwks 处提供的响应旨在与 OIDC 兼容,但并不严格符合 OIDC 标准。这些文档仅包含验证 Kubernetes 服务账户令牌所必需的参数。使用 RBAC 的集群包含一个名为 system:service-account-issuer-discovery 的默认 ClusterRole。默认的 ClusterRoleBinding 会将此角色分配给 system:serviceaccounts 组,所有 ServiceAccount 隐式属于该组。这允许在集群上运行的 Pod 通过其挂载的服务账户令牌访问服务账户发现文档。此外,管理员还可以根据其安全要求以及他们打算与之联合的外部系统,选择将该角色绑定到 system:authenticated 或 system:unauthenticated。
JWKS 响应包含信赖方可用于验证 Kubernetes 服务账户令牌的公钥。信赖方首先查询 OpenID 提供者配置,并使用响应中的 jwks_uri 字段找到 JWKS。
在许多情况下,Kubernetes API 服务器在公共互联网上不可用,但用户或服务提供商可以提供从 API 服务器缓存响应的公共端点。在这些情况下,可以通过向 API 服务器传递 --service-account-jwks-uri 标志,覆盖 OpenID 提供者配置中的 jwks_uri,使其指向公共端点而不是 API 服务器的地址。与颁发者 URL 一样,JWKS URI 也要求使用 https 方案。
另请参阅