What does a Kubernetes projected volume do, and why would a pod project a serviceAccountToken with its own audience and expirationSeconds?
answer
- many sources, one read-only directory
- kube-api-access is already one
- aud claim plus short expiry
- kubelet rewrites at 80% TTL
- subPath never sees the new file
basics
~20 sA projected volume merges Secrets, ConfigMaps, downward API fields and service account tokens into one read-only directory. A projected serviceAccountToken is short-lived, scoped to one audience and rotated by the kubelet, so it is safer to hand to an external service.
solid answer
~50 sA `projected` volume takes a list of `sources` (`secret`, `configMap`, `downwardAPI`, `serviceAccountToken`, plus `clusterTrustBundle` and `podCertificate` on current releases) and the kubelet writes them all into **one read-only directory**, with file modes from `defaultMode` (default `0644`) or per-item `mode`. Every pod already uses one: the automatic `kube-api-access-*` volume projects a token, the cluster CA and the namespace. A `serviceAccountToken` source asks the API server for a **bound token**. It carries the `audience` you name (by default the API server's identifier), it expires after `expirationSeconds` (default one hour, minimum ten minutes), and it is invalidated when the pod is deleted. The kubelet rewrites the file once the token is older than 80% of its TTL or older than 24 hours. The app must re-read the file, and the volume must **not** be mounted with `subPath`, because a subPath mount never receives the rotated file.
code
yaml · 32 linesapiVersion: v1
kind: Pod
metadata:
name: ocr-worker-9d1m
spec:
serviceAccountName: ocr-worker
containers:
- name: ocr
image: registry.example.com/ocr/engine:5.3.0
volumeMounts:
- name: ocr-identity
mountPath: /var/run/ocr
readOnly: true
volumes:
- name: ocr-identity
projected:
defaultMode: 0440
sources:
- serviceAccountToken:
audience: ocr-results-store
expirationSeconds: 3600
path: results-token
- configMap:
name: ocr-settings
items:
- key: pipeline.yaml
path: pipeline.yaml
- downwardAPI:
items:
- path: labels
fieldRef:
fieldPath: metadata.labelsgo deeper
Remember that a projected volume puts several inputs into one read-only folder, and that every pod already gets one holding its API token, the cluster CA and its namespace.
Explain audience, expirationSeconds and path, the one-hour default and ten-minute minimum, and that the kubelet rotates the file, so the app has to re-read it.
Show that you know the failure modes: subPath mounts that freeze the token, apps that cache it, and file modes that need fsGroup when the process runs as non-root.
Argue for audience-scoped, short-lived, pod-bound tokens as the platform's standard workload identity, and plan the move of consumers off long-lived token Secrets.
## What a projected volume is A **projected volume** maps several data sources into **one directory** inside a container. The kubelet builds the directory from API objects and pod metadata, writes the files atomically, and mounts the directory **read-only**. The sources are: | Source | What ends up in the directory | |---|---| | `secret` | Keys of a Secret in the pod's namespace | | `configMap` | Keys of a ConfigMap in the pod's namespace | | `downwardAPI` | Pod fields such as labels, annotations, name, and resource limits | | `serviceAccountToken` | A token for the pod's ServiceAccount, requested by the kubelet | | `clusterTrustBundle` | CA certificates from ClusterTrustBundle objects | | `podCertificate` | A key and certificate issued for the pod | The standalone `secret`, `configMap` and `downwardAPI` volume kinds each deliver one source. Projection lets an app find its config file, its credentials and its identity under one path, such as `/var/run/ocr`, without needing three mounts. **File permissions** come from `defaultMode` (default `0644`), and an individual item's `mode` overrides it. `fsGroup` in the pod's `securityContext` can add group bits. When items are listed explicitly, two sources may not write the same path, and the API server rejects the pod with `conflicting duplicate paths`. ## The one every pod already has Unless automounting is turned off, the ServiceAccount admission plugin adds a projected volume named `kube-api-access-<suffix>` to each pod and mounts it at `/var/run/secrets/kubernetes.io/serviceaccount`. It contains: - `token`, a `serviceAccountToken` source with `expirationSeconds: 3607` - `ca.crt`, from the `kube-root-ca.crt` ConfigMap - `namespace`, from a `downwardAPI` item for `metadata.namespace` Client libraries read these three files to talk to the API server. ## Why project your own token In a document-OCR pipeline, the worker has to write results to an internal store that trusts Kubernetes-issued tokens but should accept only tokens meant **for itself**. A dedicated `serviceAccountToken` source sets three things: 1. **`audience`**: the `aud` claim of the token. The store rejects any token whose audience is not its own, so a token stolen from the store cannot be replayed against the API server, and the pod's API token cannot be replayed against the store. When the field is omitted, the audience is the API server's identifier. 2. **`expirationSeconds`**: the requested lifetime. The default is one hour and the API server rejects anything under ten minutes. 3. **`path`**: the file name inside the volume. The token is **bound** to the pod object, so it stops validating once the pod is deleted, even before it expires. This replaces the old pattern of long-lived token Secrets, which never expired and outlived their pods. ## Rotation and the subPath trap The kubelet refreshes the token when it is **older than 80% of its TTL or older than 24 hours**, and atomically replaces the file. Two things break that: - **The app caches the token forever.** It reads the file at startup, and once the token expires the store starts returning authentication errors. Re-read the file periodically or on an authentication failure. - **The container mounts the file with `subPath`.** A subPath mount is bound to the file that existed when the container started and never sees the kubelet's replacement. The container keeps presenting an expired token. Mount the **whole directory** instead. ## Example ```yaml apiVersion: v1 kind: Pod metadata: name: ocr-worker-9d1m spec: serviceAccountName: ocr-worker containers: - name: ocr image: registry.example.com/ocr/engine:5.3.0 volumeMounts: - name: ocr-identity mountPath: /var/run/ocr readOnly: true volumes: - name: ocr-identity projected: defaultMode: 0440 sources: - serviceAccountToken: audience: ocr-results-store expirationSeconds: 3600 path: results-token - configMap: name: ocr-settings items: - key: pipeline.yaml path: pipeline.yaml - downwardAPI: items: - path: labels fieldRef: fieldPath: metadata.labels ``` ## Boundaries - How ConfigMap and Secret **contents** propagate after an edit is a separate subject. This question is about the projection mechanism. - The projected directory is **read-only**, so apps cannot write scratch data there. Use an emptyDir for that. - A mode such as `0440` combined with a non-root user may need `fsGroup` so the process can still read the files.
- A container mounts only the projected token file with subPath, and an hour later the external store rejects every request. Why?A `subPath` mount is bound to the file as it was when the container started. The kubelet rotates the token by writing a new file and swapping it in atomically, and a subPath mount never follows that swap. The container keeps presenting the original token after it expires. Mount the whole projected directory and have the app re-read the file.
- What does binding a projected service account token to the pod add over a token that only has an expiry time?The token's claims name the pod it was issued for, and the API server checks that the pod still exists when it validates the token. Deleting the pod therefore invalidates a leaked token immediately, without waiting for `expirationSeconds` to run out. An external service that validates tokens through the TokenReview API gets the same check.
saying these in an interview costs you the question
- Mounts a projected token with subPath and expects it to rotate
- Thinks a projected token never expires, like a legacy token Secret
- Reads the token file once at startup and caches it forever
- Believes the audience field is cosmetic and never checked by receivers
- Expects the app to write scratch files into a projected directory