Projected Service Account Token#
Kyverno's Helm chart supports disabling the default Kubernetes automatic service account token mounting (automountServiceAccountToken: false) across all four controllers β admission, background, cleanup, and reports β and substitutes a Kubernetes projected volume that provides a time-limited, audience-bound token in its place. This was introduced in PR #14766 and closes issue #8184.
Why It Exists#
Kubernetes automatically mounts a long-lived service account token secret into every pod unless the feature is disabled. Projected tokens are preferable because:
- They have configurable TTLs (the kubelet rotates them before expiry).
- They can be scoped to a specific audience, so a leaked token cannot be replayed against an unintended service.
- Disabling automounting entirely shrinks the attack surface when the controller manages its own token volume.
How It Works#
Each controller's ServiceAccount resource unconditionally sets automountServiceAccountToken: false at the object level . This prevents Kubernetes from auto-mounting the legacy secret-based token.
Each Deployment then uses a Helm template variable to drive conditional behavior :
{{- $automountSAToken := .Values.admissionController.rbac.serviceAccount.automountServiceAccountToken }}
The variable is written to spec.automountServiceAccountToken on the pod spec . When it is false, an additional serviceaccount-token projected volume is rendered with three sources:
| Source | Content |
|---|---|
serviceAccountToken | Short-lived OIDC token, rotated by the kubelet |
configMap: kube-root-ca.crt | Cluster CA certificate |
downwardAPI | Pod's namespace |
This volume is mounted at /var/run/secrets/kubernetes.io/serviceaccount β the same path used by legacy auto-mounted tokens β so all Go client code that reads from InClusterConfig() continues to work without modification .
The same pattern is implemented identically across all four controllers:
- Admission controller:
- Background controller:
- Cleanup controller:
- Reports controller:
apiCallToken β A Separate Scoped Token#
All controllers also mount a second projected volume (apicall-token) at /var/run/secrets/kyverno/apicall. This is separate from the service-account token and is injected into outbound APICall and CEL HTTP requests . It carries a custom audience (kyverno-svc.kyverno.io by default) so that if the token is leaked to an external service it cannot be replayed against the Kubernetes API server .
Configuration Reference#
All values live under <controller>.rbac.serviceAccount in charts/kyverno/values.yaml.
| Value | Default | Description |
|---|---|---|
admissionController.rbac.serviceAccount.automountServiceAccountToken | true | Enables/disables auto-mounting for the admission controller |
admissionController.rbac.serviceAccount.projectedServiceAccountToken.expirationSeconds | 3600 | Token TTL in seconds |
admissionController.rbac.serviceAccount.projectedServiceAccountToken.audience | "" | Optional audience claim; empty means unrestricted |
apiCallToken.audience | "kyverno-svc.kyverno.io" | Audience for outbound API call tokens |
apiCallToken.expirationSeconds | 3600 | TTL for outbound API call tokens |
The same projectedServiceAccountToken.* sub-keys apply for backgroundController, cleanupController, and reportsController .
Note:
automountServiceAccountTokendefaults totrueinvalues.yaml, meaning the projected fallback volume is not rendered unless you opt in by setting the value tofalse.
Key Files#
| File | Purpose |
|---|---|
charts/kyverno/values.yaml | All configurable defaults |
templates/admission-controller/deployment.yaml | Admission controller pod spec |
templates/admission-controller/serviceaccount.yaml | ServiceAccount with automountServiceAccountToken: false |
templates/background-controller/deployment.yaml | Background controller pod spec |
templates/reports-controller/deployment.yaml | Reports controller pod spec |
templates/cleanup-controller/deployment.yaml | Cleanup controller pod spec |
pkg/config/client.go | CreateClientConfig β uses InClusterConfig which reads the standard token path |