为 Pod 配置服务账号
服务账号为 Pod 中运行的进程提供了一个标识。
当你(自然人)访问集群时(例如,使用 kubectl
),API 服务器将你的身份验证为
特定的用户账号(当前这通常是 admin
,除非你的集群管理员已经定制了你的集群配置)。
Pod 内的容器中的进程也可以与 API 服务器接触。
当它们进行身份验证时,它们被验证为特定的服务账号(例如,default
)。
准备开始
你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 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
的目的:
apiVersion: v1
kind: ServiceAccount
metadata:
name: build-robot
automountServiceAccountToken: false
...
在 1.6 以上版本中,你也可以选择不给特定 Pod 自动挂载 API 凭据:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
serviceAccountName: build-robot
automountServiceAccountToken: false
...
如果 Pod 和服务账号都指定了 automountServiceAccountToken
值,则 Pod 的 spec 优先于服务账号。
使用多个服务账号
每个命名空间都有一个名为 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: 2015-06-16T00:12:59Z
name: build-robot
namespace: default
resourceVersion: "272500"
uid: 721ab723-13bc-11e5-aec2-42010af0021e
你可以使用授权插件来设置服务账号的访问许可。
要使用非默认的服务账号,将 Pod 的 spec.serviceAccountName
字段设置为你想用的服务账号名称。
Pod 被创建时服务账号必须存在,否则会被拒绝。
你不能更新已经创建好的 Pod 的服务账号。
spec.serviceAccount
字段是 spec.serviceAccountName
的已弃用别名。
如果要从工作负载资源中删除这些字段,请在
Pod 模板上将这两个字段显式设置为空。
你可以清除服务账号,如下所示:
kubectl delete serviceaccount/build-robot
手动创建服务账号 API 令牌
假设我们有一个上面提到的名为 "build-robot" 的服务账号,现在我们手动创建一个新的 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 中填充了 "build-robot" 服务账号的 API 令牌。 令牌控制器将清理不存在的服务账号的所有令牌。
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
的内容。
为服务账号添加 ImagePullSecrets
创建 ImagePullSecret
创建一个 ImagePullSecret,如为 Pod 设置 ImagePullSecret 所述。
kubectl create secret docker-registry myregistrykey --docker-server=DUMMY_SERVER \ --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 添加到服务账号
接着修改命名空间的 default
服务账号,令其使用该 Secret 用作 imagePullSecret
。
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'
你也可以使用 kubectl edit
,或者如下所示手动编辑 YAML 清单:
kubectl get serviceaccounts default -o yaml > ./sa.yaml
sa.yaml
文件的输出类似这样:
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: 2015-08-07T22:02:39Z
name: default
namespace: default
resourceVersion: "243024"
uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6
使用你常用的编辑器(例如 vi
),打开 sa.yaml
文件,删除带有键名
resourceVersion
的行,添加带有 imagePullSecrets:
的行,最后保存文件。
所得到的 sa.yaml
文件类似于:
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: 2015-08-07T22:02:39Z
name: default
namespace: default
uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6
imagePullSecrets:
- name: myregistrykey
最后,使用新更新的 sa.yaml
文件替换服务账号。
kubectl replace serviceaccount default -f ./sa.yaml
验证镜像拉取 Secret 已经被添加到 Pod 规约
现在,在当前命名空间中创建使用默认服务账号的新 Pod 时,新 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]
为了启用令牌请求投射,你必须为 kube-apiserver
设置以下命令行参数:
--service-account-issuer
此参数可作为服务账号令牌发放者的身份标识(Identifier)。你可以多次指定
--service-account-issuer
参数,对于要变更发放者而又不想带来业务中断的场景, 这样做是有用的。如果这个参数被多次指定,则第一个参数值会被用来生成令牌, 而所有参数值都会被用来确定哪些发放者是可接受的。你所运行的 Kubernetes 集群必须是 v1.22 或更高版本,才能多次指定--service-account-issuer
。
--service-account-key-file
包含 PEM 编码的 x509 RSA 或 ECDSA 私钥或公钥,用来检查 ServiceAccount 的令牌。所指定的文件中可以包含多个秘钥,并且你可以多次使用此参数, 每次参数值为不同的文件。多次使用此参数时,由所给的秘钥之一签名的令牌会被 Kubernetes API 服务器认为是合法令牌。
--service-account-signing-key-file
指向包含当前服务账号令牌发放者的私钥的文件路径。 此发放者使用此私钥来签署所发放的 ID 令牌。
--api-audiences
(可以省略)服务账号令牌身份检查组件会检查针对 API 访问所使用的令牌, 确认令牌至少是被绑定到这里所给的受众(audiences)之一。 如果此参数被多次指定,则针对所给的多个受众中任何目标的令牌都会被 Kubernetes API 服务器当做合法的令牌。如果
--service-account-issuer
参数被设置,而这个参数未指定,则这个参数的默认值为一个只有一个元素的列表, 且该元素为令牌发放者的 URL。
kubelet 还可以将服务账号令牌投射到 Pod 中。 你可以指定令牌的期望属性,例如受众和有效期限。 这些属性在 default 服务账号令牌上无法配置。 当删除 Pod 或 ServiceAccount 时,服务账号令牌也将对 API 无效。
使用名为 ServiceAccountToken 的 ProjectedVolume 类型在 PodSpec 上配置此功能。 要向 Pod 提供具有 "vault" 用户以及两个小时有效期的令牌,可以在 PodSpec 中配置以下内容:
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]
当启用服务账号令牌投射时启用发现服务账号分发者(Service Account Issuer Discovery) 这一功能特性,如上文所述。
分发者的 URL 必须遵从
OIDC 发现规范。
这意味着 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:authenticated
或 system:unauthenticated
。
/.well-known/openid-configuration
和 /openid/v1/jwks
路径请求的响应被设计为与
OIDC 兼容,但不是与其完全一致。
返回的文档仅包含对 Kubernetes 服务账号令牌进行验证所必须的参数。JWKS 响应包含依赖方可以用来验证 Kubernetes 服务账号令牌的公钥数据。
依赖方先会查询 OpenID 提供者配置,之后使用返回响应中的 jwks_uri
来查找 JWKS。
在很多场合,Kubernetes API 服务器都不会暴露在公网上,不过对于缓存并向外提供 API
服务器响应数据的公开末端而言,用户或者服务提供商可以选择将其暴露在公网上。
在这种环境中,可能会重载 OpenID 提供者配置中的
jwks_uri
,使之指向公网上可用的末端地址,而不是 API 服务器的地址。
这时需要向 API 服务器传递 --service-account-jwks-uri
参数。
与分发者 URL 类似,此 JWKS URI 也需要使用 https
模式。
接下来
另请参见: