kube_bench_cache

Execution module: kube_bench_cache

Added in version 3.0.0.

Collect kube-bench results and write a validated JSON array to a minion-local cache file. Every control SLS calls kube_bench_cache.ensure_fresh at Jinja render time before reading the cache.

All cluster interaction goes through this extension’s own kubernetes execution module (saltext.kubernetes.modules.kubernetesmod, loaded as __salt__["kubernetes.*"]) rather than shelling out to kubectl. This means connection/auth handling (kubeconfig file, inline kubeconfig data, explicit host+credentials, in-cluster ServiceAccount) is inherited for free from that module’s _setup_conn resolution instead of being reimplemented here.

Two collection strategies are supported (selected via the collection_strategy parameter or the kube_bench:collection_strategy pillar key):

  • job (default): creates a one-time Kubernetes Job from the suspended kube-bench CronJob template, waits for completion, collects JSON output from every pod (one per node), then deletes the Job. Use run_assessment() to trigger this path on demand – it bypasses the TTL cache check and always runs a new Job. This matches how helm/kube-bench-job actually deploys kube-bench (a suspended CronJob, no standing pods), so it’s the default that works out of the box without any pillar configuration.

  • daemonset (backward-compatible): reads logs from an existing long-running kube-bench DaemonSet. Only useful if you’ve deployed kube-bench that way yourself; set collection_strategy: daemonset explicitly.

Because ensure_fresh is invoked via salt['kube_bench_cache.ensure_fresh']() at Jinja render time it MUST NOT honour __opts__['test'] – doing so would silently skip collection during every policy.assessment run (which uses test=True) and leave all controls reading a stale or missing file.

saltext.kubernetes.modules.kube_bench_cache.ensure_fresh(namespace=None, label=None, cache_path=None, ttl_seconds=None, kubeconfig=None, auth_mode=None, collection_strategy=None, cronjob_name=None, job_timeout=None)[source]

Return cache_path after guaranteeing it exists and is no older than ttl_seconds. If the file is missing or stale, collect kube-bench results using the selected collection_strategy and write them atomically to cache_path.

Any parameter left as None falls back to the kube_bench pillar key of the same name, then to the module-level default constant. Explicit arguments always take precedence over pillar values.

Parameters

namespacestr or None

Kubernetes namespace where kube-bench resources live.

labelstr or None

Label selector for kube-bench pods (DaemonSet strategy only).

cache_pathstr or None

Absolute path where the JSON array is stored on the minion.

ttl_secondsint or None

Maximum cache age in seconds before re-collection is triggered.

kubeconfigstr or None

Path to a kubeconfig file. None falls back to pillar, then to kubernetes.*’s own credential auto-detection.

auth_modestr or None

"in_cluster" or "kubeconfig". None falls back to pillar, then to kubernetes.*’s own auto-detection (kubeconfig file/env, then in-cluster ServiceAccount).

collection_strategystr or None

"job" or "daemonset". None falls back to pillar, then "job".

cronjob_namestr or None

Name of the suspended CronJob used as the Job template (Job strategy).

job_timeoutint or None

Seconds to wait for the Job to complete before raising (Job strategy).

Returns

str

cache_path (for use in Jinja {%- set _ = salt['...']() %}).

CLI Example:

salt '*' kube_bench_cache.ensure_fresh
saltext.kubernetes.modules.kube_bench_cache.run_assessment(namespace=None, cache_path=None, kubeconfig=None, auth_mode=None, cronjob_name=None, job_timeout=None)[source]

Trigger an on-demand kube-bench assessment using the Job strategy.

Unlike ensure_fresh, this function always runs a new Job regardless of whether a fresh cache already exists. Results are written atomically to cache_path after the Job completes.

Any parameter left as None falls back to the kube_bench pillar key of the same name, then to the module-level default constant.

Parameters

namespacestr or None

Kubernetes namespace where the kube-bench CronJob lives.

cache_pathstr or None

Absolute path where the JSON array is stored on the minion.

kubeconfigstr or None

Path to a kubeconfig file. None falls back to pillar, then to kubernetes.*’s own credential auto-detection.

auth_modestr or None

"in_cluster" or "kubeconfig". None falls back to pillar, then to kubernetes.*’s own auto-detection.

cronjob_namestr or None

Name of the suspended CronJob used as the Job template.

job_timeoutint or None

Seconds to wait for the Job to complete before raising.

Returns

dict

{"result": True, "message": "<cache_path>"} on success, or {"result": False, "message": "<error>"} on failure.

CLI Example:

salt '*' kube_bench_cache.run_assessment
saltext.kubernetes.modules.kube_bench_cache.status_for_check(test_number, namespace=None, label=None, cache_path=None, ttl_seconds=None, kubeconfig=None, auth_mode=None, collection_strategy=None, cronjob_name=None, job_timeout=None)[source]

Return an aggregated status/comment for test_number across every node present in the merged kube-bench cache.

Added in version 2.2.0.

kube-bench runs the full CIS check set on every node regardless of its actual role (master/worker), so the cache can hold multiple results for the same test_number – one per node. Control SLS files that instead hand-parse the cache and stop at the first matching result silently ignore every other node – this is the correct replacement for that pattern, not just a convenience wrapper.

Aggregation policy is worst-status-wins: FAIL if any node FAILs, WARN if none FAIL but any node WARNs/INFOs, PASS only if every node PASSes, ERROR if no node reports this check at all.

Parameters mirror ensure_fresh() (which this calls internally to guarantee freshness) with one addition:

test_numberstr

The kube-bench control ID to look up, e.g. "1.1.11".

Returns

dict

{"status": "PASS"|"WARN"|"FAIL"|"ERROR", "comment": str}. comment lists one line per node (via the node_name tag each cached result carries) – the only place that detail can surface to a caller like RaaS, whose compliance-finding model has no per-node field of its own, only a single free-text comment per minion per check. Each line is "<node>: <status> [expected=<...>] actual=<...>"; expected is omitted when kube-bench doesn’t populate expected_result for that check, and actual falls back to kube-bench’s reason (the real audit-command error, e.g. “no such file or directory”) when actual_value itself is blank, which is common on FAILs where the audit command errored out rather than producing comparable output.

CLI Example:

salt '*' kube_bench_cache.status_for_check test_number=1.1.11