为 Pod 配置服务账号

    说明: 本文是服务账号的用户使用介绍,描述服务账号在集群中如何起作用。 你的集群管理员可能已经对你的集群做了定制,因此导致本文中所讲述的内容并不适用。

    当你(自然人)访问集群时(例如,使用 ),API 服务器将你的身份验证为 特定的用户账号(当前这通常是 admin,除非你的集群管理员已经定制了你的集群配置)。 Pod 内的容器中的进程也可以与 API 服务器接触。 当它们进行身份验证时,它们被验证为特定的服务账号(例如,default)。

    你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:

    要获知版本信息,请输入 kubectl version.

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

    当你创建 Pod 时,如果没有指定服务账号,Pod 会被指定给命名空间中的 default 服务账号。 如果你查看 Pod 的原始 JSON 或 YAML(例如:kubectl get pods/<podname> -o yaml), 你可以看到 spec.serviceAccountName 字段已经被了。

    你可以使用自动挂载给 Pod 的服务账号凭据访问 API, 访问集群页面中有相关描述。 服务账号的 API 许可取决于你所使用的。

    你可以通过在 ServiceAccount 上设置 automountServiceAccountToken: false 来实现不给服务账号自动挂载 API 凭据到 /var/run/secrets/kubernetes.io/serviceaccount/token 的目的:

    在 1.6 以上版本中,你也可以选择不给特定 Pod 自动挂载 API 凭据:

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: my-pod
    5. spec:
    6. serviceAccountName: build-robot
    7. automountServiceAccountToken: false
    8. ...

    如果 Pod 和服务账号都指定了 automountServiceAccountToken 值,则 Pod 的 spec 优先于服务账号。

    每个命名空间都有一个名为 default 的服务账号资源。 你可以用下面的命令查询这个服务账号以及命名空间中的其他 ServiceAccount 资源:

    1. kubectl get serviceaccounts

    输出类似于:

    1. NAME SECRETS AGE
    2. default 1 1d

    你可以像这样来创建额外的 ServiceAccount 对象:

    1. kubectl apply -f - <<EOF
    2. apiVersion: v1
    3. kind: ServiceAccount
    4. metadata:
    5. name: build-robot
    6. EOF

    ServiceAccount 对象的名字必须是一个有效的 .

    如果你查询服务账号对象的完整信息,如下所示:

    1. kubectl get serviceaccounts/build-robot -o yaml

    输出类似于:

    1. apiVersion: v1
    2. metadata:
    3. creationTimestamp: 2015-06-16T00:12:59Z
    4. name: build-robot
    5. namespace: default
    6. resourceVersion: "272500"
    7. uid: 721ab723-13bc-11e5-aec2-42010af0021e

    你可以使用授权插件来设置服务账号的访问许可

    要使用非默认的服务账号,将 Pod 的 spec.serviceAccountName 字段设置为你想用的服务账号名称。

    Pod 被创建时服务账号必须存在,否则会被拒绝。

    你不能更新已经创建好的 Pod 的服务账号。

    说明:

    spec.serviceAccount 字段是 spec.serviceAccountName 的已弃用别名。 如果要从工作负载资源中删除这些字段,请在 上将这两个字段显式设置为空。

    手动创建服务账号 API 令牌

    假设我们有一个上面提到的名为 “build-robot” 的服务账号,现在我们手动创建一个新的 Secret。

    1. kubectl apply -f - <<EOF
    2. apiVersion: v1
    3. kind: Secret
    4. name: build-robot-secret
    5. annotations:
    6. kubernetes.io/service-account.name: build-robot
    7. type: kubernetes.io/service-account-token
    8. EOF

    现在,你可以确认新构建的 Secret 中填充了 “build-robot” 服务账号的 API 令牌。 令牌控制器将清理不存在的服务账号的所有令牌。

    1. kubectl describe secrets/build-robot-secret

    输出类似于:

    1. Name: build-robot-secret
    2. Namespace: default
    3. Labels: <none>
    4. Annotations: kubernetes.io/service-account.name: build-robot
    5. kubernetes.io/service-account.uid: da68f9c6-9d26-11e7-b84e-002dc52800da
    6. Type: kubernetes.io/service-account-token
    7. Data
    8. ====
    9. ca.crt: 1338 bytes
    10. namespace: 7 bytes
    11. token: ...

    说明:

    这里省略了 token 的内容。

    • 创建一个 ImagePullSecret,如 所述。

      1. kubectl create secret docker-registry myregistrykey --docker-server=DUMMY_SERVER \
      2. --docker-username=DUMMY_USERNAME --docker-password=DUMMY_DOCKER_PASSWORD \
    • 确认创建成功:

      1. kubectl get secrets myregistrykey

      输出类似于:

      1. NAME TYPE DATA AGE
      2. myregistrykey kubernetes.io/.dockerconfigjson 1 1d

    接着修改命名空间的 default 服务账号,令其使用该 Secret 用作 imagePullSecret

    你也可以使用 kubectl edit,或者如下所示手动编辑 YAML 清单:

    1. kubectl get serviceaccounts default -o yaml > ./sa.yaml

    sa.yaml 文件的输出类似这样:

    1. apiVersion: v1
    2. kind: ServiceAccount
    3. metadata:
    4. creationTimestamp: 2015-08-07T22:02:39Z
    5. name: default
    6. namespace: default
    7. resourceVersion: "243024"
    8. uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6

    使用你常用的编辑器(例如 vi),打开 sa.yaml 文件,删除带有键名 resourceVersion 的行,添加带有 的行,最后保存文件。

    所得到的 sa.yaml 文件类似于:

    1. apiVersion: v1
    2. kind: ServiceAccount
    3. metadata:
    4. creationTimestamp: 2015-08-07T22:02:39Z
    5. name: default
    6. namespace: default
    7. uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6
    8. imagePullSecrets:
    9. - name: myregistrykey

    最后,使用新更新的 sa.yaml 文件替换服务账号。

    1. kubectl replace serviceaccount default -f ./sa.yaml

    现在,在当前命名空间中创建使用默认服务账号的新 Pod 时,新 Pod 会自动设置其 .spec.imagePullSecrets 字段:

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

    输出为:

    1. myregistrykey

    服务账号令牌卷投射

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

    为了启用令牌请求投射,你必须为 kube-apiserver 设置以下命令行参数:

    • --service-account-issuer

      此参数可作为服务账号令牌发放者的身份标识(Identifier)。你可以多次指定 --service-account-issuer 参数,对于要变更发放者而又不想带来业务中断的场景, 这样做是有用的。如果这个参数被多次指定,则第一个参数值会被用来生成令牌, 而所有参数值都会被用来确定哪些发放者是可接受的。你所运行的 Kubernetes 集群必须是 v1.22 或更高版本,才能多次指定 --service-account-issuer

    • --service-account-key-file

    • --service-account-signing-key-file

      指向包含当前服务账号令牌发放者的私钥的文件路径。 此发放者使用此私钥来签署所发放的 ID 令牌。

    • --api-audiences (可以省略)

      服务账号令牌身份检查组件会检查针对 API 访问所使用的令牌, 确认令牌至少是被绑定到这里所给的受众(audiences)之一。 如果此参数被多次指定,则针对所给的多个受众中任何目标的令牌都会被 Kubernetes API 服务器当做合法的令牌。如果 --service-account-issuer 参数被设置,而这个参数未指定,则这个参数的默认值为一个只有一个元素的列表, 且该元素为令牌发放者的 URL。

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

    使用名为 的 ProjectedVolume 类型在 PodSpec 上配置此功能。 要向 Pod 提供具有 “vault” 用户以及两个小时有效期的令牌,可以在 PodSpec 中配置以下内容:

    pods/pod-projected-svc-token.yaml

    创建 Pod:

    1. 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]

    当启用服务账号令牌投射时启用发现服务账号分发者(Service Account Issuer Discovery) 这一功能特性,如上文所述

    说明:

    分发者的 URL 必须遵从 。 这意味着 URL 必须使用 https 模式,并且必须在 {service-account-issuer}/.well-known/openid-configuration 路径给出 OpenID 提供者(Provider)配置。

    如果 URL 没有遵从这一规范,ServiceAccountIssuerDiscovery 末端就不会被注册, 即使该特性已经被启用。

    发现服务账号分发者这一功能使得用户能够用联邦的方式结合使用 Kubernetes 集群(“Identity Provider”,标识提供者)与外部系统(“Relying Parties”, 依赖方)所分发的服务账号令牌。

    当此功能被启用时,Kubernetes API 服务器会在 /.well-known/openid-configuration 提供一个 OpenID 提供者配置文档,并在 /openid/v1/jwks 处提供与之关联的 JSON Web Key Set(JWKS)。 这里的 OpenID 提供者配置有时候也被称作“发现文档(Discovery Document)”。

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

    说明:/.well-known/openid-configuration/openid/v1/jwks 路径请求的响应被设计为与 OIDC 兼容,但不是与其完全一致。 返回的文档仅包含对 Kubernetes 服务账号令牌进行验证所必须的参数。

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

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

    接下来