From 87d16b1172f067a4cd718d7269b3d11e08cbe61c Mon Sep 17 00:00:00 2001 From: Tim Bannister Date: Fri, 13 May 2022 19:19:26 +0100 Subject: [PATCH] Update ServiceAccount tasks in light of TokenRequest Now that TokenRequest is the default way to get a service account token for a Pod, update the task pages that relate to this. --- .../service-accounts-admin.md | 147 ++++++++++++------ .../configure-service-account.md | 21 ++- 2 files changed, 113 insertions(+), 55 deletions(-) diff --git a/content/en/docs/reference/access-authn-authz/service-accounts-admin.md b/content/en/docs/reference/access-authn-authz/service-accounts-admin.md index 66cbba953178e..0987c929a3a46 100644 --- a/content/en/docs/reference/access-authn-authz/service-accounts-admin.md +++ b/content/en/docs/reference/access-authn-authz/service-accounts-admin.md @@ -40,52 +40,35 @@ kubectl create namespace examplens Kubernetes distinguishes between the concept of a user account and a service account for a number of reasons: -- User accounts are for humans. Service accounts are for processes, which run - in pods. -- User accounts are intended to be global. Names must be unique across all - namespaces of a cluster. In Kubernetes, service accounts are namespaced. -- Typically, a cluster's user accounts might be synced from a corporate +- User accounts are for humans. Service accounts are for application processes, + which (for Kubernetes) run in containers that are part of pods. +- User accounts are intended to be global: names must be unique across all + namespaces of a cluster. No matter what namespace you look at, a particular + username that represents a user represents the same user. + In Kubernetes, service accounts are namespaced: two different namespaces can + contain ServiceAccounts that have identical names. +- Typically, a cluster's user accounts might be synchronised from a corporate database, where new user account creation requires special privileges and is - tied to complex business processes. Service account creation is intended to be - more lightweight, allowing cluster users to create service accounts for - specific tasks by following the principle of least privilege. -- Auditing considerations for humans and service accounts may differ. -- A config bundle for a complex system may include definition of various service + tied to complex business processes. By contrast, service account creation is + intended to be more lightweight, allowing cluster users to create service accounts + for specific tasks on demand. Separating ServiceAccount creation from the steps to + onboard human users makes it easier for workloads to following the principle of + least privilege. +- Auditing considerations for humans and service accounts may differ; the separation + makes that easier to achieve. +- A configuration bundle for a complex system may include definition of various service accounts for components of that system. Because service accounts can be created - without many constraints and have namespaced names, such config is portable. + without many constraints and have namespaced names, such configuration is + usually portable. -## ServiceAccount admission controller - -The modification of pods is implemented via a plugin -called an [Admission Controller](/docs/reference/access-authn-authz/admission-controllers/). -It is part of the API server. -This admission controller acts synchronously to modify pods as they are created or -updated. When this plugin is active (and it is by default on most distributions), then -it does the following when a Pod is created or modified: - -1. If the pod does not have a `.spec.serviceAccountName` set, the admission controller sets the name of the - ServiceAccount for this incoming Pod to `default`. -1. The admission controller ensures that the ServiceAccount referenced by the incoming Pod exists. If there - is no ServiceAccount with a matching name, the admission controller rejects the incoming Pod. That check - applies even for the `default` ServiceAccount. -1. Provided that neither the ServiceAccount's `automountServiceAccountToken` field nor the - Pod's `automountServiceAccountToken` field is set to `false`: - - the admission controller mutates the incoming Pod, adding an extra - {{< glossary_tooltip text="volume" term_id="volume" >}} that contains - a token for API access. - - the admission controller adds a `volumeSource` to each container in the Pod. - For Linux containers, that `volumeSource` is mounted at `/var/run/secrets/kubernetes.io/serviceaccount` - - If the spec of the incoming Pod does already contain any `imagePullSecrets`, then the - admission controller adds `imagePullSecrets`, copying them from the `ServiceAccount`. - -### Bound service account token volume mechanism {#bound-service-account-token-volume} +## Bound service account token volume mechanism {#bound-service-account-token-volume} {{< feature-state for_k8s_version="v1.22" state="stable" >}} -The Kubernetes control plane (specifically, the ServiceAccount admission controller) ass -adds projected volume to Pods. This mechanism superseded an earlier mechanism that added -a volume based on a Secret, where the Secret represented the ServiceAccount for the Pod -but did not expire. +By default, the Kubernetes control plane (specifically, the +[ServiceAccount admission controller](#service-account-admission-controller)) +adds a [projected volume](/docs/concepts/storage/projected-volumes/) to Pods, +and this volume includes a token for Kubernetes API access. Here's an example of how that looks for a launched Pod: @@ -96,7 +79,8 @@ Here's an example of how that looks for a launched Pod: defaultMode: 420 # decimal equivalent of octal 0644 sources: - serviceAccountToken: - expirationSeconds: 3597 + expirationSeconds: 7200 + audience: api path: token - configMap: items: @@ -111,21 +95,51 @@ Here's an example of how that looks for a launched Pod: path: namespace ``` -That manifest snippet defines a projected volume that consists of three sources: +That manifest snippet defines a projected volume that consists of three sources. In this case, +each source also represents a single path within that volume. The three sources are: 1. A `serviceAccountToken` source, that contains a token that the kubelet acquires from kube-apiserver The kubelet fetches time-bound tokens using the TokenRequest API. A token served for a TokenRequest expires either when the pod is deleted or after a defined lifespan (by default, that is 1 hour). The token is bound to the specific Pod and has the kube-apiserver as its audience. + This mechanism superseded an earlier mechanism that added a volume based on a Secret, + where the Secret represented the ServiceAccount for the Pod, but did not expire. 1. A `configMap` source. The ConfigMap contains a bundle of certificate authority data. Pods can use these certificates to make sure that they are connecting to your cluster's kube-apiserver (and not to middlebox or an accidentally misconfigured peer). -1. A `downwardAPI` source that makes the name ofnamespace of the Pod available to application code - running inside the Pod. +1. A `downwardAPI` source that looks up the name of thhe namespace containing the Pod, and makes + that name information available to application code running inside the Pod. Any container within the Pod that mounts this particular volume can access the above information. -See more details about [projected volumes](/docs/tasks/configure-pod-container/configure-projected-volume-storage/). +{{< note >}} +There is no specific mechanism to invalidate a token issued via TokenRequest. If you no longer +trust a bound service account token for a Pod, you can delete that Pod. Deleting a Pod expires +its bound service account tokens. +{{< /note >}} + +## Manual Secret management for ServiceAccounts + +Versions of Kubernetes before v1.22 automatically created credentials for accessing +the Kubernetes API. This older mechanism was based on creating token Secrets that +could then be mounted into running Pods. + +In more recent versions, including Kubernetes v{{< skew currentVersion >}}, API credentials +are [obtained directly](#bound-service-account-token-volume) using the +[TokenRequest](/docs/reference/kubernetes-api/authentication-resources/token-request-v1/) API, +and are mounted into Pods using a projected volume. +The tokens obtained using this method have bounded lifetimes, and are automatically +invalidated when the Pod they are mounted into is deleted. + +You can still [manually create](/docs/tasks/configure-pod-container/configure-service-account/#manually-create-an-api-token-for-a-serviceaccount) a Secret to hold a service account token; for example, if you need a token that never expires. + +Once you manually create a Secret and link it to a ServiceAccount, the Kubernetes control plane automatically populates the token into that Secret. + +{{< note >}} +Although the manual mechanism for creating a long-lived ServiceAccount token exists, +using [TokenRequest](/docs/reference/kubernetes-api/authentication-resources/token-request-v1/) +to obtain short-lived API access tokens is recommended instead. +{{< /note >}} ## Control plane details @@ -134,8 +148,6 @@ See more details about [projected volumes](/docs/tasks/configure-pod-container/c The service account token controller runs as part of `kube-controller-manager`. This controller acts asynchronously. It: -- watches for ServiceAccount creation and creates a corresponding - ServiceAccount token Secret to allow API access. - watches for ServiceAccount deletion and deletes all corresponding ServiceAccount token Secrets. - watches for ServiceAccount token Secret addition, and ensures the referenced @@ -150,13 +162,42 @@ Similarly, you must pass the corresponding public key to the `kube-apiserver` using the `--service-account-key-file` flag. The public key will be used to verify the tokens during authentication. +### ServiceAccount admission controller + +The modification of pods is implemented via a plugin +called an [Admission Controller](/docs/reference/access-authn-authz/admission-controllers/). +It is part of the API server. +This admission controller acts synchronously to modify pods as they are created or +updated. When this plugin is active (and it is by default on most distributions), then +it does the following when a Pod is created or modified: + +1. If the pod does not have a `.spec.serviceAccountName` set, the admission controller sets the name of the + ServiceAccount for this incoming Pod to `default`. +1. The admission controller ensures that the ServiceAccount referenced by the incoming Pod exists. If there + is no ServiceAccount with a matching name, the admission controller rejects the incoming Pod. That check + applies even for the `default` ServiceAccount. +1. Provided that neither the ServiceAccount's `automountServiceAccountToken` field nor the + Pod's `automountServiceAccountToken` field is set to `false`: + - the admission controller mutates the incoming Pod, adding an extra + {{< glossary_tooltip text="volume" term_id="volume" >}} that contains + a token for API access. + - the admission controller adds a `volumeSource` to each container in the Pod. + For Linux containers, that `volumeSource` is mounted at `/var/run/secrets/kubernetes.io/serviceaccount` + - If the spec of the incoming Pod does already contain any `imagePullSecrets`, then the + admission controller adds `imagePullSecrets`, copying them from the `ServiceAccount`. + +### TokenRequest API + +You use the [TokenRequest](/docs/reference/kubernetes-api/authentication-resources/token-request-v1/) +subresource of a ServiceAccount to obtain a time-bound token for that ServiceAccount. Usually you don't +need to call this API: the kubelet sets this up for you using a projected volume. + ## Create additional API tokens {#create-token} -The control plane ensures that a Secret with an API token exists for each -ServiceAccount. To create additional API tokens for a ServiceAccount, create a +To create the first, or subsequent, long lived API token for a ServiceAccount, create a Secret of type `kubernetes.io/service-account-token` with an annotation -referencing the ServiceAccount, and the control plane will update that Secret with a -generated token. +referencing the ServiceAccount. The control plane then generates a long-lived token and +updates that Secret with that token data. Here is a sample manifest for such a Secret: @@ -261,3 +302,7 @@ If you created a namespace `examplens` to experiment with, you can remove it: ```shell kubectl delete namespace examplens ``` + +## {{% heading "whatsnext" %}} + +- Read more details about [projected volumes](/docs/concepts/storage/projected-volumes/). diff --git a/content/en/docs/tasks/configure-pod-container/configure-service-account.md b/content/en/docs/tasks/configure-pod-container/configure-service-account.md index 9c467d0a0b4bd..c30787e4b1ec7 100644 --- a/content/en/docs/tasks/configure-pod-container/configure-service-account.md +++ b/content/en/docs/tasks/configure-pod-container/configure-service-account.md @@ -169,6 +169,21 @@ kubectl delete serviceaccount/build-robot ## Manually create an API token for a ServiceAccount +{{< note >}} +Versions of Kubernetes before v1.22 automatically created credentials for accessing +the Kubernetes API. This older mechanism was based on creating token Secrets that +could then be mounted into running Pods. +In more recent versions, including Kubernetes v{{< skew currentVersion >}}, API credentials +are obtained directly by using the [TokenRequest](/docs/reference/kubernetes-api/authentication-resources/token-request-v1/) API, +and are mounted into Pods using a [projected volume](/docs/reference/access-authn-authz/service-accounts-admin/#bound-service-account-token-volume). +The tokens obtained using this method have bounded lifetimes, and are automatically +invalidated when the Pod they are mounted into is deleted. + +You can still manually create a service account token Secret; for example, if you need a token that never expires. +However, using the [TokenRequest](/docs/reference/kubernetes-api/authentication-resources/token-request-v1/) +subresource to obtain a token to access the API is recommended instead. +{{< /note >}} + Suppose you have an existing service account named "build-robot" as mentioned earlier. You then manually create a new Secret: @@ -191,10 +206,6 @@ kubectl get secret/build-robot-secret -o yaml you can see that the Secret now contains an API token for the "build-robot" ServiceAccount. -The control plane automatically generates tokens for ServiceAccounts, and stores -them into the associated Secret. The control plane also cleans up tokens for -deleted ServiceAccounts. - ```shell kubectl describe secrets/build-robot-secret ``` @@ -221,6 +232,8 @@ token: ... By design, `kubectl describe` hides the value of the token. {{< /note >}} +The control plane automatically cleans up tokens for deleted ServiceAccounts. + ## Add ImagePullSecrets to a service account First, [create an imagePullSecret](/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod).