Information in this document may be out of date

This document has an older update date than the original, so the information it contains may be out of date. If you're able to read English, see the English version for the most up-to-date information: Configure Service Accounts for Pods

为 Pod 配置服务账号

Kubernetes 提供两种完全不同的方式来为客户端提供支持,这些客户端可能运行在你的集群中, 也可能与你的集群的控制面相关, 需要向 API 服务器完成身份认证。

服务账号(Service Account)为 Pod 中运行的进程提供身份标识, 并映射到 ServiceAccount 对象。当你向 API 服务器执行身份认证时, 你会将自己标识为某个用户(User)。Kubernetes 能够识别用户的概念, 但是 Kubernetes 自身并不提供 User API。

本服务是关于 ServiceAccount 的,而 ServiceAccount 则确实存在于 Kubernetes 的 API 中。 本指南为你展示为 Pod 配置 ServiceAccount 的一些方法。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

使用默认的服务账号访问 API 服务器

当 Pod 与 API 服务器联系时,Pod 会被认证为某个特定的 ServiceAccount(例如:default)。 在每个名字空间中,至少存在一个 ServiceAccount。

每个 Kubernetes 名字空间至少包含一个 ServiceAccount:也就是该名字空间的默认服务账号, 名为 default。如果你在创建 Pod 时没有指定 ServiceAccount,Kubernetes 会自动将该名字空间中 名为 default 的 ServiceAccount 分配给该 Pod。

你可以检视你刚刚创建的 Pod 的细节。例如:

kubectl get pods/<podname> -o yaml

在输出中,你可以看到字段 spec.serviceAccountName。当你在创建 Pod 时未设置该字段时, Kubernetes 自动为 Pod 设置这一属性的取值。

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 上 spec 的设置优先于服务账号的设置。

使用多个服务账号

每个名字空间都至少有一个 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 字段设置为你想用的服务账号名称。

只能在创建 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 参数来请求特定的令牌有效期 (实际签发的令牌的有效期可能会稍短一些,也可能会稍长一些)。

手动为 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 令牌。

鉴于你所设置的注解,控制面会自动为该 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:          ...

当你删除一个与某 Secret 相关联的 ServiceAccount 时,Kubernetes 的控制面会自动清理该 Secret 中长期有效的令牌。

为服务账号添加 ImagePullSecrets

首先,生成一个 imagePullSecret; 接下来,验证该 Secret 已被创建。例如:

  • 为 Pod 设置 imagePullSecret 所描述的,生成一个镜像拉取 Secret:

    kubectl create secret docker-registry myregistrykey --docker-server=DUMMY_SERVER \
            --docker-username=DUMMY_USERNAME --docker-password=DUMMY_DOCKER_PASSWORD \
            --docker-email=DUMMY_DOCKER_EMAIL
    
  • 检查该 Secret 已经被创建。

    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

检查 imagePullSecrets 已经被设置到新 Pod 上

现在,在当前名字空间中创建新 Pod 并使用默认 ServiceAccount 时, 新 Pod 的 spec.imagePullSecrets 会被自动设置。

kubectl run nginx --image=nginx --restart=Never
kubectl get pod nginx -o=jsonpath='{.spec.imagePullSecrets[0].name}{"\n"}'

输出为:

myregistrykey

服务账号令牌卷投射

特性状态: Kubernetes v1.20 [stable]

kubelet 还可以将 ServiceAccount 令牌投射到 Pod 中。你可以指定令牌的期望属性, 例如受众和有效期限。这些属性在 default ServiceAccount 令牌上无法配置。 当 Pod 或 ServiceAccount 被删除时,该令牌也将对 API 无效。

你可以使用类型为 ServiceAccountToken投射卷 来为 Pod 的 spec 配置此行为。

启动使用服务账号令牌投射的 Pod

要为某 Pod 提供一个受众为 vault 并且有效期限为 2 小时的令牌,你可以定义一个与下面类似的 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 内可用;在令牌快要到期的时候刷新它。kubelet 会在令牌存在期达到其 TTL 的 80% 的时候或者令牌生命期超过 24 小时的时候主动请求将其轮换掉。

应用负责在令牌被轮换时重新加载其内容。通常而言,周期性地(例如,每隔 5 分钟) 重新加载就足够了,不必跟踪令牌的实际过期时间。

发现服务账号分发者

特性状态: Kubernetes v1.21 [stable]

如果你在你的集群中已经为 ServiceAccount 启用了令牌投射, 那么你也可以利用其发现能力。Kubernetes 提供一种方式来让客户端将一个或多个外部系统进行联邦, 作为标识提供者(Identity Provider),而这些外部系统的角色是依赖方(Relying Party)

当此特性被启用时,Kubernetes API 服务器会通过 HTTP 发布一个 OpenID 提供者配置文档。 该配置文档发布在 /.well-known/openid-configuration 路径。 这里的 OpenID 提供者配置(OpenID Provider Configuration)有时候也被称作 “发现文档(Discovery Document)”。 Kubernetes API 服务器也通过 HTTP 在 /openid/v1/jwks 处发布相关的 JSON Web Key Set(JWKS)。

使用 RBAC 的集群都包含一个的默认 RBAC ClusterRole, 名为 system:service-account-issuer-discovery。 默认的 RBAC ClusterRoleBinding 将此角色分配给 system:serviceaccounts 组, 所有 ServiceAccount 隐式属于该组。这使得集群上运行的 Pod 能够通过它们所挂载的服务账号令牌访问服务账号发现文档。 此外,管理员可以根据其安全性需要以及期望集成的外部系统,选择是否将该角色绑定到 system:authenticatedsystem:unauthenticated

JWKS 响应包含依赖方可以用来验证 Kubernetes 服务账号令牌的公钥数据。 依赖方先会查询 OpenID 提供者配置,之后使用返回响应中的 jwks_uri 来查找 JWKS。

在很多场合,Kubernetes API 服务器都不会暴露在公网上,不过对于缓存并向外提供 API 服务器响应数据的公开末端而言,用户或者服务提供商可以选择将其暴露在公网上。 在这种环境中,可能会重载 OpenID 提供者配置中的 jwks_uri,使之指向公网上可用的末端地址,而不是 API 服务器的地址。 这时需要向 API 服务器传递 --service-account-jwks-uri 参数。 与分发者 URL 类似,此 JWKS URI 也需要使用 https 模式。

接下来

另请参见: