为 Pod 配置 ServiceAccount
Kubernetes 为在集群内运行的客户端,或者与集群的 控制平面 有关联的客户端,提供了两种不同的向 API 服务器 进行身份认证的方式。
ServiceAccount 为在 Pod 中运行的进程提供了一个身份,并映射到一个 ServiceAccount 对象。当你对 API 服务器进行身份认证时,你将自己标识为一个特定的用户。Kubernetes 承认用户的概念,但是,Kubernetes 本身没有 User API。
本任务指南介绍 ServiceAccount,ServiceAccount 确实存在于 Kubernetes API 中。本指南向你展示了为 Pod 配置 ServiceAccount 的一些方法。
准备工作
你需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具已配置为与你的集群通信。建议你在至少有两个非控制平面主机的节点上运行本教程。如果你还没有集群,你可以使用 minikube 创建一个,或者使用以下任一 Kubernetes 实验环境
使用默认 ServiceAccount 访问 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 内的应用可以使用自动挂载的 ServiceAccount 凭据访问 Kubernetes API。参阅访问集群以了解更多信息。
当 Pod 以 ServiceAccount 身份进行身份认证时,其访问级别取决于所使用的授权插件和策略。
API 凭据在 Pod 被删除时会自动撤销,即使存在 Finalizer 也是如此。特别是,API 凭据将在 Pod 上设置的 .metadata.deletionTimestamp
之后 60 秒撤销(删除时间戳通常是接受删除请求的时间加上 Pod 的终止宽限期)。
选择禁用 API 凭据自动挂载
如果你不希望 kubelet 自动挂载 ServiceAccount 的 API 凭据,你可以选择禁用此默认行为。你可以通过在 ServiceAccount 上设置 automountServiceAccountToken: false
来选择禁用为 ServiceAccount 在 /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:默认的 ServiceAccount 资源,称为 default
。你可以使用以下命令列出当前命名空间中的所有 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
你可以使用授权插件来设置 ServiceAccount 的权限。
要使用非默认的 ServiceAccount,请将 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 Token
假设你有一个如前所述名为 "build-robot" 的现有 ServiceAccount。
你可以使用 kubectl
为该 ServiceAccount 获取有时限的 API Token
kubectl create token build-robot
该命令的输出是一个 Token,你可以使用它作为该 ServiceAccount 进行身份认证。你可以使用 kubectl create token
命令的 --duration
命令行参数请求一个特定的 Token 有效期(实际颁发的 Token 有效期可能更短,甚至更长)。
Kubernetes v1.33 [stable]
(默认为启用:true)使用 kubectl
v1.31 或更高版本,可以创建一个直接绑定到 Node 的 ServiceAccount Token
kubectl create token build-robot --bound-object-kind Node --bound-object-name node-001 --bound-object-uid 123...456
该 Token 将一直有效,直到过期或关联的 Node 或 ServiceAccount 被删除为止。
注意
Kubernetes v1.22 之前的版本会自动创建用于访问 Kubernetes API 的长期凭据。这个旧机制是基于创建可挂载到运行中的 Pod 中的 Token Secret。在包括 Kubernetes v1.33 在内的更新版本中,API 凭据是直接使用 TokenRequest API 获取的,并使用投射卷挂载到 Pod 中。使用此方法获取的 Token 具有有限的生命周期,并在挂载它们的 Pod 被删除时自动失效。
你仍然可以手动创建一个 ServiceAccount Token Secret;例如,如果你需要一个永不过期的 Token。然而,建议改用 TokenRequest 子资源来获取访问 API 的 Token。
手动为 ServiceAccount 创建一个长期有效的 API Token
如果你想获取一个 ServiceAccount 的 API Token,你可以创建一个带有特殊注解 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 Token。
由于你设置的注解,控制平面会自动为该 ServiceAccount 生成一个 Token,并将其存储到相关的 Secret 中。控制平面还会清理已删除 ServiceAccount 的 Token。
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 中的长期有效 Token。
注意
如果你使用以下命令查看 ServiceAccount
kubectl get serviceaccount build-robot -o yaml
你无法在 ServiceAccount API 对象的 .secrets
字段中看到 build-robot-secret
Secret,因为该字段仅填充自动生成的 Secret。
将 ImagePullSecrets 添加到 ServiceAccount
首先,创建一个 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
将 ImagePullSecret 添加到 ServiceAccount
接下来,修改该命名空间的默认 ServiceAccount,使其使用此 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
现在,当在当前命名空间中使用默认 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
ServiceAccount Token 卷投射
Kubernetes v1.20 [stable]
注意
要启用和使用 Token 请求投射,你必须为 kube-apiserver
指定以下每个命令行参数
--service-account-issuer
- 定义 ServiceAccount Token 发行者的标识符。你可以多次指定
--service-account-issuer
参数,这对于启用非中断的发起者更改非常有用。当此参数被指定多次时,第一个用于生成 Token,所有参数用于确定哪些发行者被接受。你必须运行 Kubernetes v1.22 或更高版本才能多次指定--service-account-issuer
。 --service-account-key-file
- 指定包含 PEM 编码的 X.509 私钥或公钥(RSA 或 ECDSA)的文件路径,用于验证 ServiceAccount Token。指定的文件可以包含多个密钥,并且该参数可以多次指定不同的文件。如果多次指定,由任何指定的密钥签名的 Token 都被 Kubernetes API 服务器视为有效。
--service-account-signing-key-file
- 指定包含 ServiceAccount Token 发行者当前私钥的文件路径。发行者使用此私钥签署颁发的 ID Token。
--api-audiences
(可省略)- 定义 ServiceAccount Token 的受众。ServiceAccount Token 认证器验证用于访问 API 的 Token 是否绑定到这些受众中的至少一个。如果
api-audiences
被多次指定,则任何指定的受众的 Token 都被 Kubernetes API 服务器视为有效。如果你指定了--service-account-issuer
命令行参数但未设置--api-audiences
,则控制平面默认为一个仅包含发行者 URL 的单元素受众列表。
Kubelet 也可以将 ServiceAccount Token 投射到 Pod 中。你可以指定 Token 的所需属性,例如受众和有效期。这些属性不能在默认 ServiceAccount Token 上配置。当 Pod 或 ServiceAccount 被删除时,该 Token 也将对 API 无效。
你可以使用一个名为 ServiceAccountToken
的投射卷类型来为 Pod 的 spec
配置此行为。
此投射卷中的 Token 是一个 JSON Web Token (JWT)。此 Token 的 JSON 有效载荷遵循一个明确定义的模式 - Pod 绑定的 Token 的示例有效载荷如下
{
"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"
}
使用 ServiceAccount Token 投射启动 Pod
要为 Pod 提供一个受众为 vault
、有效期为两小时的 Token,你可以定义一个类似于以下的 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 请求并存储 Token;在可配置的文件路径上使 Token 对 Pod 可用;并在 Token 即将到期时刷新 Token。如果 Token 超过其总生存时间 (TTL) 的 80%,或者如果 Token 已超过 24 小时,Kubelet 会主动请求轮换 Token。
应用负责在 Token 轮换时重新加载 Token。对于应用来说,按计划加载 Token(例如:每 5 分钟一次)通常就足够了,无需跟踪实际的过期时间。
ServiceAccount 发行者发现
Kubernetes v1.21 [stable]
如果在你的集群中为 ServiceAccount 启用了Token 投射,那么你也可以利用发现特性。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 服务器也在 /openid/v1/jwks
通过 HTTP 发布相关的 JSON Web Key Set (JWKS)。
注意
在/.well-known/openid-configuration
和 /openid/v1/jwks
提供的响应被设计为与 OIDC 兼容,但并非严格符合 OIDC 规范。这些文档仅包含执行 Kubernetes ServiceAccount Token 验证所需的参数。使用RBAC 的集群包含一个名为 system:service-account-issuer-discovery
的默认 ClusterRole。默认的 ClusterRoleBinding 将此角色分配给 system:serviceaccounts
组,所有 ServiceAccount 都隐式属于此组。这允许在集群上运行的 Pod 通过其挂载的 ServiceAccount Token 访问 ServiceAccount 发现文档。此外,管理员可以根据其安全要求以及打算与哪些外部系统联合,选择将该角色绑定到 system:authenticated
或 system:unauthenticated
。
JWKS 响应包含信赖方可用于验证 Kubernetes ServiceAccount Token 的公钥。信赖方首先查询 OpenID 提供者配置,并使用响应中的 jwks_uri
字段查找 JWKS。
在许多情况下,Kubernetes API 服务器无法在公共互联网上访问,但用户或服务提供商可以提供从 API 服务器提供缓存响应的公共端点。在这种情况下,可以通过向 API 服务器传递 --service-account-jwks-uri
参数来覆盖 OpenID 提供者配置中的 jwks_uri
,使其指向公共端点而不是 API 服务器的地址。与发行者 URL 一样,JWKS URI 也必须使用 https
方案。
下一步
另请参见
- 阅读面向集群管理员的 Service Account 指南
- 阅读关于Kubernetes 中的授权
- 阅读关于Secret
- 或学习如何使用 Secret 安全地分发凭据
- 但也要注意,使用 Secret 作为 ServiceAccount 进行身份认证的方式已被弃用。推荐的替代方案是ServiceAccount Token 卷投射。
- 阅读关于投射卷。
- 有关 OIDC 发现的背景信息,请阅读 ServiceAccount 签名密钥获取 的 Kubernetes 增强提案
- 阅读 OIDC 发现规范