为 Pod 配置服务账号
Kubernetes 提供了两种不同的方式,供集群内运行的客户端,或与集群的 控制平面 建立关系的客户端,用于向 API 服务器 进行身份验证。
服务帐户为在 Pod 中运行的进程提供身份,并映射到 ServiceAccount 对象。当您向 API 服务器进行身份验证时,您将标识自己为特定的用户。Kubernetes 识别用户概念,但是 Kubernetes 本身不具有用户 API。
本任务指南是关于 ServiceAccount 的,它们确实存在于 Kubernetes API 中。本指南向您展示了一些为 Pod 配置 ServiceAccount 的方法。
开始之前
您需要一个 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 的身份进行身份验证时,其访问级别取决于所使用的 授权插件和策略。
即使存在 finalizer,API 凭据也会在 Pod 删除时自动撤销。特别是,API 凭据会在 Pod 上设置的 .metadata.deletionTimestamp 之后 60 秒撤销(删除时间戳通常是 delete 请求被接受的时间加上 Pod 的终止宽限期)。
选择退出 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 资源
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
手动为 ServiceAccount 创建 API 令牌
假设您有一个名为“build-robot”的现有服务帐户,如前所述。
您可以使用 kubectl 获取该 ServiceAccount 的有时限 API 令牌
kubectl create token build-robot
该命令的输出是一个可用于以该 ServiceAccount 身份进行身份验证的令牌。您可以使用 kubectl create token 命令的 --duration 命令行参数请求特定的令牌持续时间(实际发出的令牌的持续时间可能会更短,甚至更长)。
Kubernetes v1.33 [稳定](默认启用)使用 kubectl v1.31 或更高版本,可以创建直接绑定到节点的 ServiceAccount 令牌
kubectl create token build-robot --bound-object-kind Node --bound-object-name node-001 --bound-object-uid 123...456
该令牌在到期或关联的节点或服务帐户被删除之前有效。
说明
Kubernetes 1.22 之前的版本会自动为访问 Kubernetes API 创建长期凭据。这种较旧的机制基于创建令牌 Secret,然后可以将其挂载到正在运行的 Pod 中。在较新版本中,包括 Kubernetes v1.35,API 凭据是通过使用 TokenRequest API 获取的,并使用 projected volume 挂载到 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
如果您使用
kubectl get secret/build-robot-secret -o yaml
查看 Secret,您会看到该 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 中清理长期令牌。
说明
如果您使用
kubectl get serviceaccount build-robot -o yaml
你无法在 ServiceAccount API 对象中的 build-robot-secret Secret 的 .secrets 字段中看到它,因为该字段仅填充自动生成的 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
将 image pull secret 添加到 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
现在,当在当前命名空间中创建新的 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 token 卷投影
Kubernetes v1.20 [稳定]说明
要启用和使用 token 请求投影,你必须将以下命令行参数指定给 kube-apiserver
--service-account-issuer- 定义 ServiceAccount token issuer 的标识符。 你可以多次指定
--service-account-issuer参数,这对于启用 issuer 的非破坏性更改很有用。 如果多次指定此标志,则第一个用于生成 token,所有标志都用于确定接受哪些 issuer。 你必须运行 Kubernetes v1.22 或更高版本才能多次指定--service-account-issuer。 --service-account-key-file- 指定指向包含 PEM 编码 X.509 私钥或公钥(RSA 或 ECDSA)的文件路径,用于验证 ServiceAccount token。 指定的文件可以包含多个密钥,并且可以使用不同的文件多次指定该标志。 如果多次指定,则 Kubernetes API 服务器将认为由任何指定的密钥签名的 token 有效。
--service-account-signing-key-file- 指定指向包含 ServiceAccount token issuer 当前私钥的文件路径。 issuer 使用此私钥对发出的 ID token 进行签名。
--api-audiences(可以省略)- 定义 ServiceAccount token 的受众。 ServiceAccount token 身份验证器会验证用于访问 API 的 token 是否绑定到这些受众中的至少一个。 如果多次指定
api-audiences,则 Kubernetes API 服务器将认为任何指定受众的 token 有效。 如果你指定了--service-account-issuer命令行参数,但未设置--api-audiences,则控制平面默认设置为仅包含 issuer URL 的单个元素受众列表。
kubelet 也可以将 ServiceAccount token 投影到 Pod 中。 你可以指定 token 的所需属性,例如受众和有效持续时间。 这些属性不能在默认 ServiceAccount token 上进行配置。 当 Pod 或 ServiceAccount 被删除时,token 也会对 API 无效。
你可以使用 projected volume 类型,名为 ServiceAccountToken,来配置 Pod 的 spec 中的此行为。
此 projected volume 中的 token 是 JSON Web Token (JWT)。 此 token 的 JSON payload 遵循定义良好的模式 - 一个绑定 token 的示例 payload
{
"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;在可配置的文件路径上向 Pod 提供 token;并在 token 接近到期时刷新 token。 如果 token 的总生存时间 (TTL) 超过 80%,或者 token 的年龄超过 24 小时,kubelet 会主动请求 token 轮换。
应用程序负责在 token 轮换时重新加载 token。 通常,应用程序只需按计划加载 token(例如:每 5 分钟一次),而无需跟踪实际的到期时间即可。
ServiceAccount issuer 发现
Kubernetes v1.21 [稳定]如果你已为集群中的 ServiceAccount 启用 token 投影,则还可以利用发现功能。 Kubernetes 提供了一种方法,让客户端可以联合作为身份提供者,以便一个或多个外部系统可以充当依赖方。
说明
issuer URL 必须符合 OIDC 发现规范。 在实践中,这意味着它必须使用 https 方案,并且应在 {service-account-issuer}/.well-known/openid-configuration 处提供 OpenID 提供程序配置。
如果 URL 不符合要求,则 ServiceAccount issuer 发现端点将不会被注册或访问。
启用后,Kubernetes API 服务器将通过 HTTP 发布 OpenID Provider Configuration 文档。 该配置文档发布在 /.well-known/openid-configuration。 OpenID Provider Configuration 有时被称为发现文档。 Kubernetes API 服务器还会通过 HTTP 在 /openid/v1/jwks 发布相关的 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 Provider Configuration,并使用响应中的 jwks_uri 字段找到 JWKS。
在许多情况下,Kubernetes API 服务器不可在公共互联网上访问,但用户或服务提供商可以提供提供来自 API 服务器的缓存响应的公共端点。 在这些情况下,可以通过将 --service-account-jwks-uri 标志传递给 API 服务器来覆盖 OpenID Provider Configuration 中的 jwks_uri,以便它指向公共端点,而不是 API 服务器的地址。 与 issuer URL 一样,JWKS URI 需要使用 https 方案。
接下来
参见
- 阅读 ServiceAccount 的集群管理员指南
- 阅读关于 Kubernetes 中的授权
- 阅读关于 Secrets
- 或者学习如何 使用 Secrets 安全地分发凭据
- 但请记住,使用 Secrets 进行 ServiceAccount 身份验证已被弃用。 推荐的替代方案是 ServiceAccount token 卷投影。
- 阅读关于 projected volume。
- 有关 OIDC 发现的背景信息,请阅读 ServiceAccount 签名密钥检索 Kubernetes 增强提案
- 阅读 OIDC 发现规范