Skip to content

security(k8s): give each workload an explicit service-account-token verdict (CKV_K8S_38)Β #2845

Description

@devantler

πŸ€– Generated by the Agentic Engineer

Evidence

CKV_K8S_38 ("service account tokens are only mounted where necessary") is the second-largest
group in #2787's checkov backlog β€” 8 of the 42 Kubernetes-framework findings measured on main
2026-07-28:

workload file
Deployment.kube-system.coredns k8s/providers/docker/infrastructure/controllers/coredns/deployment.yaml
CronJob.longhorn-system.longhorn-stale-node-cleanup k8s/providers/hetzner/infrastructure/controllers/longhorn/cron-job-stale-node-cleanup.yaml
Job.openbao.vault-snapshot-init k8s/bases/infrastructure/vault-backup/job.yaml
CronJob.openbao.vault-snapshot k8s/bases/infrastructure/vault-backup/cron-job.yaml
Job.openbao.vault-config k8s/bases/infrastructure/vault-config/job.yaml
Deployment.opencost.opencost-usage-scraper k8s/bases/infrastructure/opencost/components/usage-scraper/deployment.yaml
Deployment.cdi.cdi-operator k8s/bases/infrastructure/controllers/cdi/cdi-operator.yaml
Deployment.kubevirt.virt-operator k8s/bases/infrastructure/controllers/kubevirt/kubevirt-operator.yaml

Why it matters

A mounted service-account token is a credential sitting in the container filesystem. Where the
workload never calls the API server it buys nothing and is simply reachable by anything that gets
code execution in that pod β€” which is the whole reason the check exists. Kubescape does not cover
this: its own exception CRs disable the pod-security controls cluster-wide, and the Kyverno
pod-security policies match kinds: [Pod] while k8s/ contains no Pod objects.

Why this is NOT a blanket sweep

At least three of the eight legitimately need their token β€” the KubeVirt and CDI operators are
controllers, and the Longhorn cleanup CronJob acts on nodes through the API. Setting
automountServiceAccountToken: false across all eight would break them. Each workload needs an
individual verdict, and the ones that keep the token need that recorded so the next pass does not
re-litigate it.

What

Per workload, either set automountServiceAccountToken: false on the pod spec, or record in the
manifest why the token is required. Prefer the pod-spec field over the ServiceAccount-level one so
the decision is visible where the workload is defined.

Acceptance criteria

  • Every one of the eight workloads above has an explicit verdict: the field set to false, or
    a comment naming the API access that requires it.
  • CKV_K8S_38 reports zero remaining findings it can act on, measured with the reproducible
    command from the measurement child.
  • Each workload whose token was removed is confirmed still healthy after reconcile β€” a
    controller that silently loses API access fails quietly.
  • No ClusterSecurityException or linter suppression is added; this is fixed at the manifest.

Size: M β€” eight small diffs plus a per-workload judgement, and the verification is the
expensive half.

Part of #2787. Depends on the measurement child for its before/after count.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

Status
βœ… Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions