From 0fda59f8df0017f4b8f67047423ebb74dcf9b5d9 Mon Sep 17 00:00:00 2001 From: Tim Bannister Date: Fri, 13 May 2022 19:25:31 +0100 Subject: [PATCH 1/3] =?UTF-8?q?Reword=20=E2=80=9CConfigure=20Service=20Acc?= =?UTF-8?q?ounts=20for=20Pods=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configure-service-account.md | 271 ++++++++++-------- 1 file changed, 157 insertions(+), 114 deletions(-) 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 ae366193cce28..1dc977d971bca 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 @@ -8,42 +8,63 @@ content_type: task weight: 90 --- - -A service account provides an identity for processes that run in a Pod. +Kubernetes offers two distinct ways for clients that run within your +cluster, or that otherwise have a relationship to your cluster's +{{< glossary_tooltip text="control plane" term_id="control-plane" >}} +to authenticate to the +{{< glossary_tooltip text="API server" term_id="kube-apiserver" >}}. -{{< note >}} -This document is a user introduction to Service Accounts and describes how service accounts behave in a cluster set up -as recommended by the Kubernetes project. Your cluster administrator may have -customized the behavior in your cluster, in which case this documentation may -not apply. -{{< /note >}} +A _service account_ provides an identity for processes that run in a Pod, +and maps to a ServiceAccount object. When you authenticate to the API +server, you identify yourself as a particular _user_. Kubernetes recognises +the concept of a user, however, Kubernetes itself does **not** have a User +API. -When you (a human) access the cluster (for example, using `kubectl`), you are -authenticated by the apiserver as a particular User Account (currently this is -usually `admin`, unless your cluster administrator has customized your cluster). Processes in containers inside pods can also contact the apiserver. -When they do, they are authenticated as a particular Service Account (for example, `default`). +This task guide is about ServiceAccounts, which do exist in the Kubernetes +API. The guide shows you some ways to configure ServiceAccounts for Pods. ## {{% heading "prerequisites" %}} -{{< include "task-tutorial-prereqs.md" >}} {{< version-check >}} +{{< include "task-tutorial-prereqs.md" >}} -## Use the Default Service Account to access the API server +## Use the default service account to access the API server + +When Pods contact the API server, Pods authenticate as a particular +ServiceAccount (for example, `default`). There is always at least one +ServiceAccount in each {{< glossary_tooltip text="namespace" term_id="namespace" >}}. + +Every Kubernetes namespace contains at least one ServiceAccount: the default +ServiceAccount for that namespace, named `default`. +If you do not specify a ServiceAccount when you create a Pod, Kubernetes +automatically assigns the ServiceAccount named `default` in that namespace. + +You can fetch the details for a Pod you have created. For example: +```shell +kubectl get pods/ -o yaml +``` + +In the output, you see a field `spec.serviceAccountName`. +Kubernetes [automatically](/docs/user-guide/working-with-resources/#resources-are-automatically-modified) +sets that value if you don't specify it when you create a Pod. + +An application running inside a Pod can access the Kubernetes API using +automatically mounted service account credentials. See [accessing the Cluster](/docs/user-guide/accessing-the-cluster/#accessing-the-api-from-a-pod) to learn more. -When you create a pod, if you do not specify a service account, it is -automatically assigned the `default` service account in the same namespace. -If you get the raw json or yaml for a pod you have created (for example, `kubectl get pods/ -o yaml`), -you can see the `spec.serviceAccountName` field has been -[automatically set](/docs/concepts/overview/working-with-objects/object-management/). +When a Pod authenticates as a ServiceAccount, its level of access depends on the +[authorization plugin and policy](/docs/reference/access-authn-authz/authorization/#authorization-modules) +in use. -You can access the API from inside a pod using automatically mounted service account credentials, as described in -[Accessing the Cluster](/docs/tasks/access-application-cluster/access-cluster). -The API permissions of the service account depend on the -[authorization plugin and policy](/docs/reference/access-authn-authz/authorization/#authorization-modules) in use. +### Opt out of API credential automounting +If you don't want the {{< glossary_tooltip text="kubelet" term_id="kubelet" >}} +to automatically mount a ServiceAccount's API credentials, you can opt out of +the default behavior. You can opt out of automounting API credentials on `/var/run/secrets/kubernetes.io/serviceaccount/token` for a service account by setting `automountServiceAccountToken: false` on the ServiceAccount: +For example: + ```yaml apiVersion: v1 kind: ServiceAccount @@ -53,8 +74,7 @@ automountServiceAccountToken: false ... ``` -In version 1.6+, you can also opt out of automounting API credentials for a particular pod: - +You can also opt out of automounting API credentials for a particular Pod: ```yaml apiVersion: v1 kind: Pod @@ -66,12 +86,16 @@ spec: ... ``` -The pod spec takes precedence over the service account if both specify a `automountServiceAccountToken` value. +If both the ServiceAccount and the Pod's `.spec` specify a value for +`automountServiceAccountToken`, the Pod spec takes precedence. -## Use Multiple Service Accounts +## Use more than one ServiceAccount {#use-multiple-service-accounts} -Every namespace has a default service account resource called `default`. -You can list this and any other serviceAccount resources in the namespace with this command: +Every namespace has at least one ServiceAccount: the default ServiceAccount +resource, called `default`. +You can list all ServiceAccount resources in your +[current namespace](/docs/concepts/overview/working-with-objects/namespaces/#setting-the-namespace-preference) +with: ```shell kubectl get serviceaccounts @@ -110,38 +134,43 @@ The output is similar to this: apiVersion: v1 kind: ServiceAccount metadata: - creationTimestamp: 2015-06-16T00:12:59Z + creationTimestamp: 2019-06-16T00:12:34Z name: build-robot namespace: default resourceVersion: "272500" uid: 721ab723-13bc-11e5-aec2-42010af0021e ``` -You may use authorization plugins to [set permissions on service accounts](/docs/reference/access-authn-authz/rbac/#service-account-permissions). +You can use authorization plugins to +[set permissions on service accounts](/docs/reference/access-authn-authz/rbac/#service-account-permissions). To use a non-default service account, set the `spec.serviceAccountName` -field of a pod to the name of the service account you wish to use. - -The service account has to exist at the time the pod is created, or it will be rejected. +field of a Pod to the name of the ServiceAccount you wish to use. -You cannot update the service account of an already created pod. +You can only set the `serviceAccountName` field when creating a Pod, or in a +template for a new Pod. You cannot update the `.spec.serviceAccountName` field +of a Pod that already exists. {{< note >}} -The `spec.serviceAccount` field is a deprecated alias for `spec.serviceAccountName`. +The `.spec.serviceAccount` field is a deprecated alias for `.spec.serviceAccountName`. If you want to remove the fields from a workload resource, set both fields to empty explicitly on the [pod template](/docs/concepts/workloads/pods#pod-templates). {{< /note >}} -You can clean up the service account from this example like this: + +### Cleanup {#cleanup-use-multiple-service-accounts} + +If you tried creating `build-robot` ServiceAccount from the example above, +you can clean it up by running: ```shell kubectl delete serviceaccount/build-robot ``` -## Manually create a service account API token +## Manually create an API token for a ServiceAccount -Suppose we have an existing service account named "build-robot" as mentioned above, and we create -a new secret manually. +If you want to obtain an API token for a ServiceAccount, you create a new Secret +with a special annotation, `kubernetes.io/service-account.name` ```shell kubectl apply -f - <}} The content of `token` is elided here. + +Take care not to display the contents of a `kubernetes.io/service-account-token` +Secret somewhere that your terminal / computer screen could be seen by an +onlooker. {{< /note >}} ## Add ImagePullSecrets to a service account -### Create an imagePullSecret +First, [create an imagePullSecret](/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod). +Next, verify it has been created. For example: - Create an imagePullSecret, as described in [Specifying ImagePullSecrets on a Pod](/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod). @@ -211,41 +252,44 @@ The content of `token` is elided here. ### Add image pull secret to service account -Next, modify the default service account for the namespace to use this secret as an imagePullSecret. +Next, modify the default service account for the namespace to use this Secret as an imagePullSecret. ```shell kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}' ``` -You can instead use `kubectl edit`, or manually edit the YAML manifests as shown below: +You can achieve the same outcome by editing the object manually: ```shell -kubectl get serviceaccounts default -o yaml > ./sa.yaml +kubectl edit serviceaccount/default ``` The output of the `sa.yaml` file is similar to this: +Your selected text editor will open with a configuration looking something like this: + ```yaml apiVersion: v1 kind: ServiceAccount metadata: - creationTimestamp: 2015-08-07T22:02:39Z + creationTimestamp: 2021-07-07T22:02:39Z name: default namespace: default resourceVersion: "243024" uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6 ``` -Using your editor of choice (for example `vi`), open the `sa.yaml` file, delete line with key `resourceVersion`, add lines with `imagePullSecrets:` and save. +Using your editor, delete the line with key `resourceVersion`, add lines for `imagePullSecrets:` and save it. +Leave the `uid` value set the same as you found it. -The output of the `sa.yaml` file is similar to this: +After you made those changes, the edited ServiceAccount looks something like this: ```yaml apiVersion: v1 kind: ServiceAccount metadata: - creationTimestamp: 2015-08-07T22:02:39Z + creationTimestamp: 2021-07-07T22:02:39Z name: default namespace: default uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6 @@ -253,13 +297,7 @@ imagePullSecrets: - name: myregistrykey ``` -Finally replace the serviceaccount with the new updated `sa.yaml` file - -```shell -kubectl replace serviceaccount default -f ./sa.yaml -``` - -### Verify imagePullSecrets was added to pod spec +### Verify that imagePullSecrets are set for new Pods Now, when a new Pod is created in the current namespace and using the default ServiceAccount, the new Pod has its `spec.imagePullSecrets` field set automatically: @@ -274,12 +312,7 @@ The output is: myregistrykey ``` - - -## Service Account Token Volume Projection +## ServiceAccount token volume projection {{< feature-state for_k8s_version="v1.20" state="stable" >}} @@ -287,31 +320,31 @@ TODO: Test and explain how to use additional non-K8s secrets with an existing se To enable and use token request projection, you must specify each of the following command line arguments to `kube-apiserver`: -* `--service-account-issuer` - - It can be used as the Identifier of the service account token issuer. You can specify the `--service-account-issuer` argument multiple times, this can be useful to enable a non-disruptive change of the issuer. When this flag is specified multiple times, the first is used to generate tokens and all are used to determine which issuers are accepted. You must be running Kubernetes v1.22 or later to be able to specify `--service-account-issuer` multiple times. -* `--service-account-key-file` - - File containing PEM-encoded x509 RSA or ECDSA private or public keys, used to verify ServiceAccount tokens. The specified file can contain multiple keys, and the flag can be specified multiple times with different files. If specified multiple times, tokens signed by any of the specified keys are considered valid by the Kubernetes API server. -* `--service-account-signing-key-file` - - Path to the file that contains the current private key of the service account token issuer. The issuer signs issued ID tokens with this private key. -* `--api-audiences` (can be omitted) - - The service account token authenticator validates that tokens used against the API are bound to at least one of these audiences. If `api-audiences` is specified multiple times, tokens for any of the specified audiences are considered valid by the Kubernetes API server. If the `--service-account-issuer` flag is configured and this flag is not, this field defaults to a single element list containing the issuer URL. +`--service-account-issuer` +: defines the Identifier of the service account token issuer. You can specify the `--service-account-issuer` argument multiple times, this can be useful to enable a non-disruptive change of the issuer. When this flag is specified multiple times, the first is used to generate tokens and all are used to determine which issuers are accepted. You must be running Kubernetes v1.22 or later to be able to specify `--service-account-issuer` multiple times. +`--service-account-key-file` +: specifies the path to a file containing PEM-encoded X.509 private or public keys (RSA or ECDSA), used to verify ServiceAccount tokens. The specified file can contain multiple keys, and the flag can be specified multiple times with different files. If specified multiple times, tokens signed by any of the specified keys are considered valid by the Kubernetes API server. +`--service-account-signing-key-file` +: specifies the path to a file that contains the current private key of the service account token issuer. The issuer signs issued ID tokens with this private key. +`--api-audiences` (can be omitted) +: defines audiences for ServiceAccount tokens. The service account token authenticator validates that tokens used against the API are bound to at least one of these audiences. If `api-audiences` is specified multiple times, tokens for any of the specified audiences are considered valid by the Kubernetes API server. If you specify the `--service-account-issuer` command line argument but you don't set `--api-audiences`, the control plane defaults to a single element audience list that contains only the issuer URL. {{< /note >}} -The kubelet can also project a service account token into a Pod. You can +The kubelet can also project a ServiceAccount token into a Pod. You can specify desired properties of the token, such as the audience and the validity -duration. These properties are not configurable on the default service account -token. The service account token will also become invalid against the API when -the Pod or the ServiceAccount is deleted. +duration. These properties are _not_ configurable on the default ServiceAccount +token. The token will also become invalid against the API when either the Pod +or the ServiceAccount is deleted. + +You can configure this behavior for the `spec` of a Pod using a +[projected volume](/docs/concepts/storage/volumes/#projected) type called +`ServiceAccountToken`. -This behavior is configured on a PodSpec using a ProjectedVolume type called -[ServiceAccountToken](/docs/concepts/storage/volumes/#projected). To provide a -pod with a token with an audience of "vault" and a validity duration of two -hours, you would configure the following in your PodSpec: +### Launch a Pod using service account token projection + +To provide a Pod with a token with an audience of `vault` and a validity duration +of two hours, you could define a Pod manifest that is similar to: {{< codenew file="pods/pod-projected-svc-token.yaml" >}} @@ -321,19 +354,24 @@ Create the Pod: kubectl create -f https://k8s.io/examples/pods/pod-projected-svc-token.yaml ``` -The kubelet will request and store the token on behalf of the pod, make the -token available to the pod at a configurable file path, and refresh the token as it approaches expiration. -The kubelet proactively rotates the token if it is older than 80% of its total TTL, or if the token is older than 24 hours. +The kubelet will: request and store the token on behalf of the Pod; make +the token available to the Pod at a configurable file path; and refresh +the token as it approaches expiration. The kubelet proactively requests rotation +for the token if it is older than 80% of its total time-to-live (TTL), +or if the token is older than 24 hours. -The application is responsible for reloading the token when it rotates. Periodic reloading (e.g. once every 5 minutes) is sufficient for most use cases. +The application is responsible for reloading the token when it rotates. It's +often good enough for the application to load the token on a schedule +(for example: once every 5 minutes), without tracking the actual expiry time. -## Service Account Issuer Discovery +### Service account issuer discovery {{< feature-state for_k8s_version="v1.21" state="stable" >}} -The Service Account Issuer Discovery feature is enabled when the Service Account -Token Projection feature is enabled, as described -[above](#service-account-token-volume-projection). +If you have enabled [token projection](#service-account-token-volume-projection) +for ServiceAccounts in your cluster, then you can also make use of the discovery +feature. Kubernetes provides a way for clients to federate as an _identity provider_, +so that one or more external systems can act as a _relying party_. {{< note >}} The issuer URL must comply with the @@ -341,27 +379,16 @@ The issuer URL must comply with the practice, this means it must use the `https` scheme, and should serve an OpenID provider configuration at `{service-account-issuer}/.well-known/openid-configuration`. -If the URL does not comply, the `ServiceAccountIssuerDiscovery` endpoints will -not be registered, even if the feature is enabled. +If the URL does not comply, ServiceAccount issuer discovery endpoints are not +registered or accessible. {{< /note >}} -The Service Account Issuer Discovery feature enables federation of Kubernetes -service account tokens issued by a cluster (the _identity provider_) with -external systems (_relying parties_). - -When enabled, the Kubernetes API server provides an OpenID Provider -Configuration document at `/.well-known/openid-configuration` and the associated -JSON Web Key Set (JWKS) at `/openid/v1/jwks`. The OpenID Provider Configuration -is sometimes referred to as the _discovery document_. - -Clusters include a default RBAC ClusterRole called -`system:service-account-issuer-discovery`. A default RBAC ClusterRoleBinding -assigns this role to the `system:serviceaccounts` group, which all service -accounts implicitly belong to. This allows pods running on the cluster to access -the service account discovery document via their mounted service account token. -Administrators may, additionally, choose to bind the role to -`system:authenticated` or `system:unauthenticated` depending on their security -requirements and which external systems they intend to federate with. +When enabled, the Kubernetes API server publishes an OpenID Provider +Configuration document via HTTP. The configuration document is published at +`/.well-known/openid-configuration`. +The OpenID Provider Configuration is sometimes referred to as the _discovery document_. +The Kubernetes API server publishes the related +JSON Web Key Set (JWKS), also via HTTP, at `/openid/v1/jwks`. {{< note >}} The responses served at `/.well-known/openid-configuration` and @@ -370,6 +397,15 @@ compliant. Those documents contain only the parameters necessary to perform validation of Kubernetes service account tokens. {{< /note >}} +Clusters that use {{< glossary_tooltip text="RBAC" term_id="rbac">}} include a +default ClusterRole called `system:service-account-issuer-discovery`. +A default ClusterRoleBinding assigns this role to the `system:serviceaccounts` group, +which all ServiceAccounts implicitly belong to. +This allows pods running on the cluster to access the service account discovery document +via their mounted service account token. Administrators may, additionally, choose to +bind the role to `system:authenticated` or `system:unauthenticated` depending on their +security requirements and which external systems they intend to federate with. + The JWKS response contains public keys that a relying party can use to validate the Kubernetes service account tokens. Relying parties first query for the OpenID Provider Configuration, and use the `jwks_uri` field in the response to @@ -377,7 +413,7 @@ find the JWKS. In many cases, Kubernetes API servers are not available on the public internet, but public endpoints that serve cached responses from the API server can be made -available by users or service providers. In these cases, it is possible to +available by users or by service providers. In these cases, it is possible to override the `jwks_uri` in the OpenID Provider Configuration so that it points to the public endpoint, rather than the API server's address, by passing the `--service-account-jwks-uri` flag to the API server. Like the issuer URL, the @@ -388,6 +424,13 @@ JWKS URI is required to use the `https` scheme. See also: -- [Cluster Admin Guide to Service Accounts](/docs/reference/access-authn-authz/service-accounts-admin/) -- [Service Account Signing Key Retrieval KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-auth/1393-oidc-discovery) -- [OIDC Discovery Spec](https://openid.net/specs/openid-connect-discovery-1_0.html) +* Read the [Cluster Admin Guide to Service Accounts](/docs/reference/access-authn-authz/service-accounts-admin/) +* Read about [Authorization in Kubernetes](/docs/reference/access-authn-authz/authorization/) +* Read about [Secrets](/docs/concepts/configuration/secret/) + * or learn to [distribute credentials securely using Secrets](/docs/tasks/inject-data-application/distribute-credentials-secure/) + * but also bear in mind that using Secrets for authenticating as a ServiceAccount + is deprecated. The recommended alternative is + [ServiceAccount token volume projection](#service-account-token-volume-projection). +* Read about [projected volumes](/docs/tasks/configure-pod-container/configure-projected-volume-storage/). +* For background on OIDC discovery, read the [ServiceAccount signing key retrieval](https://github.com/kubernetes/enhancements/tree/master/keps/sig-auth/1393-oidc-discovery) Kubernetes Enhancement Proposal +* Read the [OIDC Discovery Spec](https://openid.net/specs/openid-connect-discovery-1_0.html) From f9db6ae93434e93d74f7efccd0132e1dcef66482 Mon Sep 17 00:00:00 2001 From: Tim Bannister Date: Fri, 13 May 2022 19:26:01 +0100 Subject: [PATCH 2/3] =?UTF-8?q?Reword=20=E2=80=9CManaging=20Service=20Acco?= =?UTF-8?q?unts=E2=80=9D=20task?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service-accounts-admin.md | 288 ++++++++++++------ .../secret/serviceaccount/mysecretname.yaml | 7 + 2 files changed, 208 insertions(+), 87 deletions(-) create mode 100644 content/en/examples/secret/serviceaccount/mysecretname.yaml 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 7cd08a8a6cc71..0fad02e87a6f9 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 @@ -11,14 +11,30 @@ weight: 50 -This is a Cluster Administrator guide to service accounts. You should be familiar with -[configuring Kubernetes service accounts](/docs/tasks/configure-pod-container/configure-service-account/). +A _ServiceAccount_ provides an identity for processes that run in a Pod. -Support for authorization and user accounts is planned but incomplete. Sometimes -incomplete features are referred to in order to better describe service accounts. +A process inside a Pod can use the identity of its associated service account to +authenticate to the cluster's API server. + +For an introduction to service accounts, read [configure service accounts](/docs/tasks/configure-pod-container/configure-service-account/). + +This task guide explains some of the concepts behind ServiceAccounts. The +guide also explains how to add and remove tokens from ServiceAccounts. +## {{% heading "prerequisites" %}} + +{{< include "task-tutorial-prereqs.md" >}} + +To be able to follow these steps exactly, ensure you have a namespace named +`examplens`. +If you don't, create one by running: + +```shell +kubectl create namespace examplens +``` + ## User accounts versus service accounts Kubernetes distinguishes between the concept of a user account and a service account @@ -27,7 +43,7 @@ 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. Service accounts are namespaced. + namespaces of a cluster. In Kubernetes, service accounts are namespaced. - Typically, a cluster's user accounts might be synced 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 @@ -38,124 +54,222 @@ for a number of reasons: accounts for components of that system. Because service accounts can be created without many constraints and have namespaced names, such config is portable. -## Service account automation - -Three separate components cooperate to implement the automation around service accounts: - -- A `ServiceAccount` admission controller -- A Token controller -- A `ServiceAccount` controller - -### ServiceAccount Admission Controller +## 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. -It 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 `ServiceAccount` set, it sets the `ServiceAccount` to `default`. -1. It ensures that the `ServiceAccount` referenced by the pod exists, and otherwise rejects it. -1. It adds a `volume` to the pod which contains a token for API access if neither the - ServiceAccount `automountServiceAccountToken` nor the Pod's `automountServiceAccountToken` - is set to `false`. -1. It adds a `volumeSource` to each container of the pod mounted at - `/var/run/secrets/kubernetes.io/serviceaccount`, if the previous step has created a volume - for the ServiceAccount token. -1. If the pod does not contain any `imagePullSecrets`, then `imagePullSecrets` of the - `ServiceAccount` are added to the pod. - -#### Bound Service Account Token Volume +This admission controller acts synchronously to modify pods as they are created. +When this plugin is active (and it is by default on most distributions), then +it does the following when a Pod is created: + +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 `volumeMount` to each container in the Pod, + skipping any containers that already have a volume mount defined for the path + `/var/run/secrets/kubernetes.io/serviceaccount`. + For Linux containers, that volume is mounted at `/var/run/secrets/kubernetes.io/serviceaccount`; + on Windows nodes, the mount is at the equivalent path. +1. 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} {{< feature-state for_k8s_version="v1.22" state="stable" >}} -The ServiceAccount admission controller will add the following projected volume instead of a -Secret-based volume for the non-expiring service account token created by the Token controller. +The Kubernetes control plane (specifically, the ServiceAccount admission controller) +adds a projected volume to Pods, and the kubelet ensures that this volume contains a token +that lets containers authenticate as the right ServiceAccount. + +(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.) + +Here's an example of how that looks for a launched Pod: ```yaml -- name: kube-api-access- - projected: - defaultMode: 420 # 0644 - sources: - - serviceAccountToken: - expirationSeconds: 3607 - path: token - - configMap: - items: - - key: ca.crt - path: ca.crt - name: kube-root-ca.crt - - downwardAPI: - items: - - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - path: namespace +... + - name: kube-api-access- + projected: + defaultMode: 420 # decimal equivalent of octal 0644 + sources: + - serviceAccountToken: + expirationSeconds: 3597 + path: token + - configMap: + items: + - key: ca.crt + path: ca.crt + name: kube-root-ca.crt + - downwardAPI: + items: + - fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + path: namespace ``` -This projected volume consists of three sources: +That manifest snippet defines a projected volume that combines information from three sources: -1. A `serviceAccountToken` acquired from kube-apiserver via TokenRequest API. It will expire - after 1 hour by default or when the pod is deleted. It is bound to the pod and it has - its audience set to match the audience of the `kube-apiserver`. -1. A `configMap` containing a CA bundle used for verifying connections to the kube-apiserver. -1. A `downwardAPI` that references the namespace of the pod. +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. +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. This `downwardAPI` volume makes the name of the namespace container the Pod available + to application code running inside the Pod. -See more details about [projected volumes](/docs/tasks/configure-pod-container/configure-projected-volume-storage/). +Any container within the Pod that mounts this volume can access the above information. -### Token Controller +## Create additional API tokens {#create-token} -TokenController runs as part of `kube-controller-manager`. It acts asynchronously. It: +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 +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. -- watches ServiceAccount creation and creates a corresponding - ServiceAccount token Secret to allow API access. -- watches ServiceAccount deletion and deletes all corresponding ServiceAccount - token Secrets. -- watches ServiceAccount token Secret addition, and ensures the referenced - ServiceAccount exists, and adds a token to the Secret if needed. -- watches Secret deletion and removes a reference from the corresponding - ServiceAccount if needed. +Here is a sample manifest for such a Secret: -You must pass a service account private key file to the token controller in -the `kube-controller-manager` using the `--service-account-private-key-file` -flag. The private key is used to sign generated service account tokens. -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. +{{< codenew file="secret/serviceaccount/mysecretname.yaml" >}} -#### To create additional API tokens +To create a Secret based on this example, run: +```shell +kubectl -n examplens create -f https://k8s.io/examples/secret/serviceaccount/mysecretname.yaml +``` -A controller loop ensures a Secret with an API token exists for each -ServiceAccount. To create additional API tokens for a ServiceAccount, create a -Secret of type `kubernetes.io/service-account-token` with an annotation -referencing the ServiceAccount, and the controller will update it with a -generated token: +To see the details for that Secret, run: + +```shell +kubectl -n examplens describe secret mysecretname +``` + +The output is similar to: +``` +Name: mysecretname +Namespace: examplens +Labels: +Annotations: kubernetes.io/service-account.name=myserviceaccount + kubernetes.io/service-account.uid=8a85c4c4-8483-11e9-bc42-526af7764f64 + +Type: kubernetes.io/service-account-token + +Data +==== +ca.crt: 1362 bytes +namespace: 9 bytes +token: ... +``` + +If you launch a new Pod into the `examplens` namespace, it can use the `myserviceaccount` +service-account-token Secret that you just created. + +## Delete/invalidate a ServiceAccount token {#delete-token} -Below is a sample configuration for such a Secret: +If you know the name of the Secret that contains the token you want to remove: +```shell +kubectl delete secret name-of-secret +``` + +Otherwise, first find the Secret for the ServiceAccount. + +```shell +# This assumes that you already have a namespace named 'examplens' +kubectl -n examplens get serviceaccount/example-automated-thing -o yaml +``` +The output is similar to: ```yaml apiVersion: v1 -kind: Secret +kind: ServiceAccount metadata: - name: mysecretname annotations: - kubernetes.io/service-account.name: myserviceaccount -type: kubernetes.io/service-account-token + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"example-automated-thing","namespace":"examplens"}} + creationTimestamp: "2019-07-21T07:07:07Z" + name: example-automated-thing + namespace: examplens + resourceVersion: "777" + selfLink: /api/v1/namespaces/examplens/serviceaccounts/example-automated-thing + uid: f23fd170-66f2-4697-b049-e1e266b7f835 +secrets: +- name: example-automated-thing-token-zyxwv +``` +Then, delete the Secret you now know the name of: +```shell +kubectl -n examplens delete secret/example-automated-thing-token-zyxwv ``` +The control plane spots that the ServiceAccount is missing its Secret, +and creates a replacement: + ```shell -kubectl create -f ./secret.yaml -kubectl describe secret mysecretname +kubectl -n examplens get serviceaccount/example-automated-thing -o yaml +``` +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"example-automated-thing","namespace":"examplens"}} + creationTimestamp: "2019-07-21T07:07:07Z" + name: example-automated-thing + namespace: examplens + resourceVersion: "1026" + selfLink: /api/v1/namespaces/examplens/serviceaccounts/example-automated-thing + uid: f23fd170-66f2-4697-b049-e1e266b7f835 +secrets: +- name: example-automated-thing-token-4rdrh ``` -#### To delete/invalidate a ServiceAccount token Secret +You can see that there is now a new associated Secret with a different name. The +old Secret is no longer valid. +## Clean up + +If you created a namespace `examplens` to experiment with, you can remove it: ```shell -kubectl delete secret mysecretname +kubectl delete namespace examplens ``` +## Control plane details + ### ServiceAccount controller A ServiceAccount controller manages the ServiceAccounts inside namespaces, and ensures a ServiceAccount named "default" exists in every active namespace. +### Token controller + +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 + ServiceAccount exists, and adds a token to the Secret if needed. +- watches for Secret deletion and removes a reference from the corresponding + ServiceAccount if needed. + +You must pass a service account private key file to the token controller in +the `kube-controller-manager` using the `--service-account-private-key-file` +flag. The private key is used to sign generated service account tokens. +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. + +## {{% heading "whatsnext" %}} + +- Read more details about [projected volumes](/docs/concepts/storage/projected-volumes/). diff --git a/content/en/examples/secret/serviceaccount/mysecretname.yaml b/content/en/examples/secret/serviceaccount/mysecretname.yaml new file mode 100644 index 0000000000000..e50fe72d71cd4 --- /dev/null +++ b/content/en/examples/secret/serviceaccount/mysecretname.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +type: kubernetes.io/service-account-token +metadata: + name: mysecretname + annotations: + - kubernetes.io/service-account.name: myserviceaccount From a4629cd19b60746f750ff4955f1d57da660fc0bd Mon Sep 17 00:00:00 2001 From: Tim Bannister Date: Fri, 13 May 2022 19:19:26 +0100 Subject: [PATCH 3/3] 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 | 160 +++++++++++++++--- .../configure-service-account.md | 35 +++- 2 files changed, 172 insertions(+), 23 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 0fad02e87a6f9..73867e3c73c35 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 @@ -19,7 +19,8 @@ authenticate to the cluster's API server. For an introduction to service accounts, read [configure service accounts](/docs/tasks/configure-pod-container/configure-service-account/). This task guide explains some of the concepts behind ServiceAccounts. The -guide also explains how to add and remove tokens from ServiceAccounts. +guide also explains how to obtain or revoke tokens that represent +ServiceAccounts. @@ -40,21 +41,126 @@ 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 +## Bound service account token volume mechanism {#bound-service-account-token-volume} + +{{< feature-state for_k8s_version="v1.22" state="stable" >}} + +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: + +```yaml +... + - name: kube-api-access- + projected: + sources: + - serviceAccountToken: + path: token # must match the path the app expects + - configMap: + items: + - key: ca.crt + path: ca.crt + name: kube-root-ca.crt + - downwardAPI: + items: + - fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + path: namespace +``` + +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 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. + +{{< 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 + +### Token controller + +The service account token controller runs as part of `kube-controller-manager`. +This controller acts asynchronously. It: + +- watches for ServiceAccount deletion and deletes all corresponding ServiceAccount + token Secrets. +- watches for ServiceAccount token Secret addition, and ensures the referenced + ServiceAccount exists, and adds a token to the Secret if needed. +- watches for Secret deletion and removes a reference from the corresponding + ServiceAccount if needed. + +You must pass a service account private key file to the token controller in +the `kube-controller-manager` using the `--service-account-private-key-file` +flag. The private key is used to sign generated service account tokens. +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/). @@ -81,10 +187,18 @@ it does the following when a Pod is created: 1. 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} +### TokenRequest API {{< feature-state for_k8s_version="v1.22" state="stable" >}} +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. +You don't need to call this to obtain an API token for use within a container, since +the kubelet sets this up for you using a _projected volume_. + +If you want to use the TokenRequest API from `kubectl`, see +[Manually create an API token for a ServiceAccount](/docs/tasks/configure-pod-container/configure-service-account/#manually-create-an-api-token-for-a-serviceaccount). + The Kubernetes control plane (specifically, the ServiceAccount admission controller) adds a projected volume to Pods, and the kubelet ensures that this volume contains a token that lets containers authenticate as the right ServiceAccount. @@ -101,7 +215,7 @@ Here's an example of how that looks for a launched Pod: defaultMode: 420 # decimal equivalent of octal 0644 sources: - serviceAccountToken: - expirationSeconds: 3597 + expirationSeconds: 3607 path: token - configMap: items: @@ -132,11 +246,16 @@ Any container within the Pod that mounts this volume can access the above inform ## 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 +{{< caution >}} +Only create long-lived API tokens if the [token request](#tokenrequest-api) mechanism +is not suitable. The token request mechanism provides time-limited tokens; because these +expire, they represent a lower risk to information security. +{{< /caution >}} + +To create a non-expiring, persisted 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 generated token data. Here is a sample manifest for such a Secret: @@ -232,9 +351,6 @@ secrets: - name: example-automated-thing-token-4rdrh ``` -You can see that there is now a new associated Secret with a different name. The -old Secret is no longer valid. - ## Clean up If you created a namespace `examplens` to experiment with, you can remove it: 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 1dc977d971bca..47955a0ace3be 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,8 +169,38 @@ kubectl delete serviceaccount/build-robot ## Manually create an API token for a ServiceAccount +Suppose you have an existing service account named "build-robot" as mentioned earlier. + +You can get a time-limited API token for that ServiceAccount using `kubectl`: + +```shell +kubectl create token admin-user +``` + +The output from that command is a token that you can use to authenticate as that +ServiceAccount. You can request a specific token duration using the `--duration` +command line argument to `kubectl create token` (the actual duration of the issued +token might be shorter, or could even be longer). + +{{< note >}} +Versions of Kubernetes before v1.22 automatically created long term 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 >}} + +### Manually create a long-lived API token for a ServiceAccount + If you want to obtain an API token for a ServiceAccount, you create a new Secret -with a special annotation, `kubernetes.io/service-account.name` +with a special annotation, `kubernetes.io/service-account.name`. ```shell kubectl apply -f - <}} +When you delete a ServiceAccount that has an associated Secret, the Kubernetes +control plane automatically cleans up the long-lived token from that Secret. + ## Add ImagePullSecrets to a service account First, [create an imagePullSecret](/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod).