为 Pod 配置服务账号

Kubernetes 为在集群内部运行的客户端,或者与集群的 控制平面 有其他关系的客户端提供了两种不同的方式来向 API 服务器 进行身份验证。

_服务帐户_ 为在 Pod 中运行的进程提供身份,并映射到 ServiceAccount 对象。当你向 API 服务器进行身份验证时,你将自己标识为特定的_用户_。Kubernetes 识别用户的概念,但是,Kubernetes 本身 **没有** User 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 进行身份验证时,其访问级别取决于正在使用的 授权插件和策略

即使 finalizer 存在,API 凭据也会在 Pod 删除时自动吊销。特别是,API 凭据将在 Pod 上设置的 .metadata.deletionTimestamp 之后 60 秒吊销(删除时间戳通常是接受 **删除** 请求的时间加上 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 子域名

如果你获得服务帐户对象的完整转储,如下所示

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 字段。

清理

如果你尝试从上面的示例中创建 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.31 [beta](默认启用:true)

使用 kubectl v1.31 或更高版本,可以创建直接绑定到节点的 ServiceAccount 令牌

kubectl create token build-robot --bound-object-kind Node --bound-object-name node-001 --bound-object-uid 123...456

该令牌将一直有效,直到它过期或关联的节点或服务帐户被删除。

手动为 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:          ...

当你删除具有关联 Secret 的 ServiceAccount 时,Kubernetes 控制平面会自动清理该 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 作为 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 [stable]

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", 
  "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

要为 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 [stable]

如果您已在集群中为 ServiceAccount 启用了令牌投影,那么您还可以使用发现功能。Kubernetes 提供了一种让客户端联合为身份提供者的方式,以便一个或多个外部系统可以充当依赖方

启用后,Kubernetes API 服务器通过 HTTP 发布 OpenID 提供程序配置文档。配置文档发布在 /.well-known/openid-configuration。OpenID 提供程序配置有时被称为发现文档。Kubernetes API 服务器还在 /openid/v1/jwks 通过 HTTP 发布相关的 JSON Web 密钥集 (JWKS)。

使用RBAC 的集群包含一个名为 system:service-account-issuer-discovery 的默认 ClusterRole。默认的 ClusterRoleBinding 将此角色分配给 system:serviceaccounts 组,所有 ServiceAccount 都隐式属于该组。这允许集群上运行的 Pod 通过其已挂载的服务帐户令牌访问服务帐户发现文档。管理员还可以选择将角色绑定到 system:authenticatedsystem:unauthenticated,具体取决于他们的安全要求以及他们打算与之联合的外部系统。

JWKS 响应包含依赖方可用于验证 Kubernetes 服务帐户令牌的公钥。依赖方首先查询 OpenID 提供程序配置,并使用响应中的 jwks_uri 字段查找 JWKS。

在许多情况下,Kubernetes API 服务器在公共互联网上不可用,但是用户或服务提供商可以提供公共端点,这些端点提供来自 API 服务器的缓存响应。在这些情况下,可以通过将 --service-account-jwks-uri 标志传递给 API 服务器,来覆盖 OpenID 提供程序配置中的 jwks_uri,使其指向公共端点而不是 API 服务器的地址。与颁发者 URL 一样,JWKS URI 也需要使用 https 方案。

下一步

另请参阅