Address 6 critical Prowler IaC findings (mute + grafana RBAC tighten) #340

Merged
eblume merged 3 commits from prowler-iac-mutelist into main 2026-04-29 10:43:33 -07:00
Owner

Summary

The weekly Prowler IaC scan reported 6 critical findings against argocd/manifests/. They split cleanly into two patterns:

  • Legitimate-by-design RBAC → mute with new compensating controls

    • external-secrets-controller, external-secrets-cert-controller manage secrets (KSV-0041) and the cert-controller mutates its own webhook configurations (KSV-0114). This is what the operator is for. New CC: operator-purpose-bound-rbac.
    • kube-state-metrics (both minikube-indri and k3s-ringtail) holds list/watch on secrets to expose kube_secret_info and kube_secret_labels metrics. KSM's metric schema only reads metadata, never the data: field. New CC: kube-state-metrics-metadata-only.
  • Over-broad RBAC → fix

    • grafana-clusterrole had get/watch/list on secrets because the dashboard-sidecar config used RESOURCE=both (ConfigMaps + Secrets). Nothing in the cluster labels Secrets with grafana_dashboard=1, so this was unused power. Switched both sidecar instances to RESOURCE=configmap and removed secrets from the ClusterRole.

The IaC cronjob also did not previously pass --mutelist-file, which is why every IaC finding reported as unmuted regardless of mutelist configuration. The new mutelist/iac.yaml is bundled into the existing prowler-mutelist ConfigMap and mounted via items: selector.

Test plan

  • kubectl --context=minikube-indri kustomize argocd/manifests/prowler/ — already passes locally
  • kubectl --context=minikube-indri kustomize argocd/manifests/grafana/ — already passes locally
  • Deploy from this branch via argocd app set prowler --revision prowler-iac-mutelist && argocd app sync prowler and same for grafana
  • Manually trigger the IaC cronjob and verify MUTED=True on the 6 critical findings (kubectl --context=minikube-indri -n prowler create job --from=cronjob/prowler-iac-scan prowler-iac-test)
  • Restart grafana pod and confirm dashboards still render (sidecar still finds them via ConfigMap watch)
  • After verify, argocd app set <app> --revision main && argocd app sync <app> post-merge

🤖 Generated with Claude Code

## Summary The weekly Prowler IaC scan reported 6 critical findings against `argocd/manifests/`. They split cleanly into two patterns: - **Legitimate-by-design RBAC → mute with new compensating controls** - `external-secrets-controller`, `external-secrets-cert-controller` manage `secrets` (KSV-0041) and the cert-controller mutates its own webhook configurations (KSV-0114). This is what the operator is *for*. New CC: `operator-purpose-bound-rbac`. - `kube-state-metrics` (both `minikube-indri` and `k3s-ringtail`) holds `list/watch` on secrets to expose `kube_secret_info` and `kube_secret_labels` metrics. KSM's metric schema only reads metadata, never the `data:` field. New CC: `kube-state-metrics-metadata-only`. - **Over-broad RBAC → fix** - `grafana-clusterrole` had `get/watch/list` on `secrets` because the dashboard-sidecar config used `RESOURCE=both` (ConfigMaps + Secrets). Nothing in the cluster labels Secrets with `grafana_dashboard=1`, so this was unused power. Switched both sidecar instances to `RESOURCE=configmap` and removed `secrets` from the ClusterRole. The IaC cronjob also did not previously pass `--mutelist-file`, which is why every IaC finding reported as unmuted regardless of mutelist configuration. The new `mutelist/iac.yaml` is bundled into the existing `prowler-mutelist` ConfigMap and mounted via `items:` selector. ## Test plan - [ ] `kubectl --context=minikube-indri kustomize argocd/manifests/prowler/` — already passes locally - [ ] `kubectl --context=minikube-indri kustomize argocd/manifests/grafana/` — already passes locally - [ ] Deploy from this branch via `argocd app set prowler --revision prowler-iac-mutelist && argocd app sync prowler` and same for `grafana` - [ ] Manually trigger the IaC cronjob and verify `MUTED=True` on the 6 critical findings (`kubectl --context=minikube-indri -n prowler create job --from=cronjob/prowler-iac-scan prowler-iac-test`) - [ ] Restart grafana pod and confirm dashboards still render (sidecar still finds them via ConfigMap watch) - [ ] After verify, `argocd app set <app> --revision main && argocd app sync <app>` post-merge 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Six critical IaC findings against argocd/manifests/ broke into two
patterns: legitimate-by-design RBAC (mute) and over-broad RBAC (fix).

Plumbing:
  - cronjob-iac-scan.yaml now passes --mutelist-file (previously
    unused, which is why all IaC findings reported as unmuted)
  - new mutelist/iac.yaml is bundled into the prowler-mutelist
    ConfigMap and mounted into the IaC cronjob via items: selector

Compensating controls (in compensating-controls.yaml):
  - operator-purpose-bound-rbac — external-secrets-operator's whole
    function is to manage Secret objects; ClusterRole over secrets
    matches its purpose. cert-controller mutates its own validating
    webhooks to inject a rotating CA bundle.
  - kube-state-metrics-metadata-only — KSM exposes only Secret
    metadata via kube_secret_info / kube_secret_labels; the data
    field is never read into exposed metrics.

Mutes (mutelist/iac.yaml):
  - KSV-0041 for external-secrets/rbac.yaml,
    kube-state-metrics/rbac.yaml,
    kube-state-metrics-ringtail/rbac.yaml
  - KSV-0114 for external-secrets/rbac.yaml

Real fix:
  - grafana-clusterrole no longer reads secrets. The dashboard sidecar
    (RESOURCE=both → configmap, both init and watch instances) only
    needs ConfigMap-labeled dashboards; no Secrets are labeled
    grafana_dashboard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prowler's IaC provider hardcodes self._mutelist = None and delegates
filtering to Trivy, but doesn't plumb --ignorefile through. The original
attempt with --mutelist-file silently no-op'd. Add a wrapper around
trivy in our image that injects --ignorefile $TRIVY_IGNOREFILE on `fs`
subcommands; switch the IaC cronjob to mount a Trivy-format
trivyignore.yaml and set the env var.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Includes the trivy ignorefile shim needed to make IaC mutes work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
eblume merged commit 495e45d01d into main 2026-04-29 10:43:33 -07:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
eblume/blumeops!340
No description provided.