Skip to content

Commit

Permalink
Update ServiceAccount tasks in light of TokenRequest
Browse files Browse the repository at this point in the history
Now that TokenRequest is the default way to get a service account token
for a Pod, update the task pages that relate to this.
  • Loading branch information
sftim committed Jun 26, 2022
1 parent 717991d commit 87d16b1
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 55 deletions.
147 changes: 96 additions & 51 deletions content/en/docs/reference/access-authn-authz/service-accounts-admin.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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:
Expand All @@ -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

Expand All @@ -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
Expand All @@ -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:

Expand Down Expand Up @@ -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/).
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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
```
Expand All @@ -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).
Expand Down

0 comments on commit 87d16b1

Please sign in to comment.