From 8c45cdecb4da982fbeb6687a15313ccd05c49ecb Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Sat, 4 Jul 2026 13:37:13 +0200 Subject: [PATCH] fix(kubescape): mount scanner config.json dir so posture scans persist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In offline mode the chart mounts the writable `kubescape-volume` emptyDir at the FILE path /home/nonroot/.kubescape/config.json with `subPath: config.json`. emptyDir + subPath makes Kubernetes create the subPath as a DIRECTORY, so config.json is a directory, not a file. The v4.0.8 scanner never wrote it; v4.0.9 (pinned for the CRD-exceptions getter) writes a generated account ID there and now aborts every scan with `open .../config.json: is a directory` before any WorkloadConfigurationScan result is persisted — so posture data froze on 2026-06-27 and ClusterSecurityException posture exceptions are never evaluated (dashboard shows stale findings, exceptions read "0 excluded"). Add a Flux postRenderer that mounts the emptyDir at the .kubescape directory (no subPath) — the same writable layout the chart's online branch uses — so the scanner can create config.json as a file; host-scanner.yaml stays a nested mount. Verified by rendering the chart and applying the patch: the file-path mount is gone. Drop once fixed upstream in kubescape/helm-charts. Co-Authored-By: Claude Opus 4.8 --- .../controllers/kubescape/helm-release.yaml | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/k8s/bases/infrastructure/controllers/kubescape/helm-release.yaml b/k8s/bases/infrastructure/controllers/kubescape/helm-release.yaml index b522c0965..a0738523d 100644 --- a/k8s/bases/infrastructure/controllers/kubescape/helm-release.yaml +++ b/k8s/bases/infrastructure/controllers/kubescape/helm-release.yaml @@ -142,3 +142,38 @@ spec: limits: cpu: 500m memory: 1Gi + # Fix the scanner's config.json mount so posture scans actually persist. + # In OFFLINE mode (kubescapeOffline: enable) the chart mounts the writable + # `kubescape-volume` emptyDir at the FILE path /home/nonroot/.kubescape/config.json + # with `subPath: config.json`. An emptyDir + subPath makes Kubernetes create the + # subPath as a DIRECTORY, so config.json is a directory, not a file. The old + # v4.0.8 scanner never wrote it; v4.0.9 (pinned above for the CRD-exceptions + # getter) does — it writes a generated account ID there — so every scan now + # aborts with `open /home/nonroot/.kubescape/config.json: is a directory` BEFORE + # any WorkloadConfigurationScan result is persisted. Result: posture data goes + # stale and ClusterSecurityException posture exceptions are never evaluated. + # Mount the emptyDir at the .kubescape DIRECTORY instead (no subPath) — the same + # writable layout the chart's ONLINE branch uses — so the scanner can create + # config.json as a file; host-scanner.yaml stays a nested read-only mount. + # Drop once the chart fixes the offline mount upstream (kubescape/helm-charts). + postRenderers: + - kustomize: + patches: + - target: + kind: Deployment + name: kubescape + patch: | + apiVersion: apps/v1 + kind: Deployment + metadata: + name: kubescape + spec: + template: + spec: + containers: + - name: kubescape + volumeMounts: + - mountPath: /home/nonroot/.kubescape/config.json + $patch: delete + - name: kubescape-volume + mountPath: /home/nonroot/.kubescape