为 Pod 配置服务帐户
Kubernetes 为在您的集群中运行的客户端或以其他方式与您的集群的 控制平面 相关联的客户端提供两种不同的方式来对 API 服务器 进行身份验证。
服务帐户为在 Pod 中运行的进程提供身份,并映射到 ServiceAccount 对象。当您对 API 服务器进行身份验证时,您将自己标识为特定的用户。Kubernetes 认识到用户的概念,但是 Kubernetes 本身不具有用户 API。
本任务指南介绍 ServiceAccounts,它们存在于 Kubernetes API 中。本指南向您展示了一些为 Pod 配置 ServiceAccounts 的方法。
开始之前
您需要有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与您的集群通信。建议在至少有两个节点(不充当控制平面主机)的集群上运行本教程。如果您还没有集群,可以使用 minikube 创建一个,或者可以使用以下 Kubernetes 游乐场之一
使用默认服务帐户访问 API 服务器
当 Pod 联系 API 服务器时,Pod 会以特定 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 的身份进行身份验证时,其访问级别取决于所使用的 授权插件和策略。
选择退出 API 凭据自动挂载
如果您不希望 kubelet 自动挂载 ServiceAccount 的 API 凭据,您可以选择退出默认行为。您可以通过在 ServiceAccount 上设置 automountServiceAccountToken: false
来选择退出为服务帐户自动挂载 /var/run/secrets/kubernetes.io/serviceaccount/token
上的 API 凭据
例如
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
每个命名空间都至少有一个 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 子域名称。
如果您获得了服务帐户对象的完整转储,例如
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
手动为 ServiceAccount 创建 API 令牌
假设您有一个名为“build-robot”的现有服务帐户,如前所述。
您可以使用 kubectl
为该 ServiceAccount 获取一个有限时 API 令牌
kubectl create token build-robot
该命令的输出是一个令牌,您可以使用它以该 ServiceAccount 的身份进行身份验证。您可以使用 kubectl create token
的 --duration
命令行参数请求特定的令牌持续时间(实际颁发的令牌的持续时间可能更短,甚至可能更长)。
当 ServiceAccountTokenNodeBinding
和 ServiceAccountTokenNodeBindingValidation
功能启用且 KUBECTL_NODE_BOUND_TOKENS
环境变量设置为 true
时,可以创建直接绑定到 Node
的服务帐户令牌
KUBECTL_NODE_BOUND_TOKENS=true kubectl create token build-robot --bound-object-kind Node --bound-object-name node-001 --bound-object-uid 123...456
该令牌在过期或关联的 Node
或服务帐户被删除之前一直有效。
注意
v1.22 之前的 Kubernetes 版本会自动为访问 Kubernetes API 创建长期凭据。这种较旧的机制基于创建令牌 Secret,然后可以将其挂载到正在运行的 Pod 中。在较新的版本(包括 Kubernetes v1.30)中,API 凭据是通过使用 TokenRequest API 直接获取的,并使用 投影卷 挂载到 Pod 中。使用此方法获取的令牌具有有限的生命周期,并且在它们被挂载到的 Pod 被删除时会自动失效。
您仍然可以手动创建服务帐户令牌 Secret;例如,如果您需要一个永不过期的令牌。但是,建议改为使用 TokenRequest 子资源来获取访问 API 的令牌。
手动为 ServiceAccount 创建一个长期 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 令牌。
由于您设置了注释,因此控制平面会自动为该 ServiceAccounts 生成令牌,并将它们存储到关联的 Secret 中。控制平面还会清理已删除 ServiceAccounts 的令牌。
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。
将 ImagePullSecrets 添加到服务帐户
首先,创建 imagePullSecret。接下来,验证它是否已创建。例如
创建 imagePullSecret,如 在 Pod 上指定 ImagePullSecrets 中所述。
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 添加到服务帐户
接下来,修改命名空间的默认服务帐户以使用此 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
验证是否为新 Pod 设置了 imagePullSecrets。
现在,当在当前命名空间中创建新的 Pod 并使用默认 ServiceAccount 时,新的 Pod 会自动设置其 spec.imagePullSecrets
字段。
kubectl run nginx --image=<registry name>/nginx --restart=Never
kubectl get pod nginx -o=jsonpath='{.spec.imagePullSecrets[0].name}{"\n"}'
输出为
myregistrykey
ServiceAccount 令牌卷投影
Kubernetes v1.20 [稳定]
注意
要启用和使用令牌请求投影,您必须为 kube-apiserver
指定以下每个命令行参数。
--service-account-issuer
- 定义服务帐户令牌发行者的标识符。您可以多次指定
--service-account-issuer
参数,这对于启用发行者的非破坏性更改很有用。当此标志被多次指定时,第一个用于生成令牌,所有标志都用于确定接受哪些发行者。您必须运行 Kubernetes v1.22 或更高版本才能多次指定--service-account-issuer
。 --service-account-key-file
- 指定包含 PEM 编码的 X.509 私钥或公钥(RSA 或 ECDSA)的文件的路径,用于验证 ServiceAccount 令牌。指定的文件可以包含多个密钥,并且可以多次指定标志,使用不同的文件。如果多次指定,则由任何指定密钥签名的令牌都被 Kubernetes API 服务器视为有效。
--service-account-signing-key-file
- 指定包含服务帐户令牌发行者当前私钥的文件的路径。发行者使用此私钥签署已发行的 ID 令牌。
--api-audiences
(可以省略)- 定义 ServiceAccount 令牌的受众。服务帐户令牌验证器验证用于 API 的令牌是否绑定到这些受众中的至少一个。如果
api-audiences
被多次指定,则针对任何指定受众的令牌都被 Kubernetes API 服务器视为有效。如果您指定--service-account-issuer
命令行参数,但没有设置--api-audiences
,则控制平面将默认使用仅包含发行者 URL 的单元素受众列表。
kubelet 也可以将 ServiceAccount 令牌投影到 Pod 中。您可以指定令牌的所需属性,例如受众和有效期。这些属性不能在默认 ServiceAccount 令牌上配置。当 Pod 或 ServiceAccount 被删除时,令牌也会对 API 变得无效。
您可以使用名为 ServiceAccountToken
的 投影卷 类型为 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", # ServiceAccountTokenJTI feature must be enabled for the claim to be present
"kubernetes.io": {
"namespace": "kube-system",
"node": { # ServiceAccountTokenPodNodeInfo feature must be enabled for the API server to add this node reference claim
"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
要为 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 启用了 令牌投影,那么您也可以使用发现功能。Kubernetes 为客户端提供了一种作为身份提供者联合的方式,以便一个或多个外部系统可以充当信赖方。
注意
发行者 URL 必须符合 OIDC 发现规范。实际上,这意味着它必须使用 https
方案,并且应该在 {service-account-issuer}/.well-known/openid-configuration
上提供 OpenID 提供者配置。
如果 URL 不符合,则不会注册或访问 ServiceAccount 发行者发现端点。
启用后,Kubernetes API 服务器通过 HTTP 发布 OpenID 提供者配置文档。配置文档发布在 /.well-known/openid-configuration
上。OpenID 提供者配置有时被称为发现文档。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 服务器提供缓存响应的公共端点。在这些情况下,可以通过将 --service-account-jwks-uri
标志传递给 API 服务器来覆盖 OpenID 提供者配置中的 jwks_uri
,使其指向公共端点,而不是 API 服务器的地址。与发行者 URL 一样,JWKS URI 要求使用 https
方案。
下一步
另请参阅
- 阅读 集群管理员指南:服务帐户
- 阅读有关 Kubernetes 中的授权
- 阅读有关 Secret
- 或学习 使用 Secret 安全地分发凭据
- 但也要记住,使用 Secret 作为 ServiceAccount 进行身份验证已弃用。推荐的替代方法是 ServiceAccount 令牌卷投影。
- 阅读有关 投影卷。
- 有关 OIDC 发现的背景信息,请阅读 服务帐户签名密钥检索 Kubernetes 增强提案
- 阅读 OIDC 发现规范