Address 6 critical Prowler IaC findings (mute + grafana RBAC tighten) #340
6 changed files with 69 additions and 46 deletions
Replace dead Prowler IaC mutelist with Trivy ignorefile shim
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>
commit
2daf6291b7
|
|
@ -19,13 +19,19 @@ spec:
|
||||||
- name: prowler
|
- name: prowler
|
||||||
image: registry.ops.eblu.me/blumeops/prowler:kustomized
|
image: registry.ops.eblu.me/blumeops/prowler:kustomized
|
||||||
command: ["/bin/sh", "-c"]
|
command: ["/bin/sh", "-c"]
|
||||||
|
# Prowler's --mutelist-file is a no-op for the IaC provider
|
||||||
|
# (it delegates to Trivy). The Prowler image's trivy shim
|
||||||
|
# injects --ignorefile $TRIVY_IGNOREFILE when set; see
|
||||||
|
# containers/prowler/Dockerfile.
|
||||||
|
env:
|
||||||
|
- name: TRIVY_IGNOREFILE
|
||||||
|
value: /mutelist/trivyignore.yaml
|
||||||
args:
|
args:
|
||||||
- |
|
- |
|
||||||
DATEDIR=/reports/prowler-iac/$(date +%Y-%m-%d)
|
DATEDIR=/reports/prowler-iac/$(date +%Y-%m-%d)
|
||||||
mkdir -p "$DATEDIR"
|
mkdir -p "$DATEDIR"
|
||||||
prowler iac \
|
prowler iac \
|
||||||
--scan-repository-url https://forge.ops.eblu.me/eblume/blumeops.git \
|
--scan-repository-url https://forge.ops.eblu.me/eblume/blumeops.git \
|
||||||
--mutelist-file /mutelist/iac.yaml \
|
|
||||||
-z \
|
-z \
|
||||||
--output-formats html csv json-ocsf \
|
--output-formats html csv json-ocsf \
|
||||||
--output-directory "$DATEDIR"
|
--output-directory "$DATEDIR"
|
||||||
|
|
@ -44,5 +50,5 @@ spec:
|
||||||
configMap:
|
configMap:
|
||||||
name: prowler-mutelist
|
name: prowler-mutelist
|
||||||
items:
|
items:
|
||||||
- key: iac.yaml
|
- key: trivyignore.yaml
|
||||||
path: iac.yaml
|
path: trivyignore.yaml
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ configMapGenerator:
|
||||||
- mutelist/core-pod-security.yaml
|
- mutelist/core-pod-security.yaml
|
||||||
- mutelist/manual-node-checks.yaml
|
- mutelist/manual-node-checks.yaml
|
||||||
- mutelist/rbac.yaml
|
- mutelist/rbac.yaml
|
||||||
- mutelist/iac.yaml
|
- mutelist/trivyignore.yaml
|
||||||
|
|
||||||
images:
|
images:
|
||||||
- name: registry.ops.eblu.me/blumeops/prowler
|
- name: registry.ops.eblu.me/blumeops/prowler
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
# IaC scan mutes — Trivy KSV checks against argocd/manifests/.
|
|
||||||
#
|
|
||||||
# Check ID format: "KSV-XXXX" (Trivy Kubernetes Security check IDs).
|
|
||||||
# Region / Resource semantics for Prowler IaC: Region == repo path,
|
|
||||||
# Resource == manifest file path (relative to repo root).
|
|
||||||
Mutelist:
|
|
||||||
Accounts:
|
|
||||||
"*":
|
|
||||||
Checks:
|
|
||||||
"KSV-0041":
|
|
||||||
# Mutelist entries under one CHECK_ID share a Resources list.
|
|
||||||
# Each resource here justifies muting under a distinct CC; see
|
|
||||||
# the per-resource notes below.
|
|
||||||
Regions: ["*"]
|
|
||||||
Resources:
|
|
||||||
# CC: operator-purpose-bound-rbac. external-secrets-operator's
|
|
||||||
# entire function is to read and synthesize Secret objects;
|
|
||||||
# ClusterRole over secrets is its purpose. Both the controller
|
|
||||||
# and cert-controller are upstream-defined.
|
|
||||||
- "argocd/manifests/external-secrets/rbac.yaml"
|
|
||||||
# CC: kube-state-metrics-metadata-only. KSM exposes only
|
|
||||||
# Secret metadata (name, namespace, type, labels), never the
|
|
||||||
# data field. list/watch on secrets is required to expose
|
|
||||||
# kube_secret_info and kube_secret_labels.
|
|
||||||
- "argocd/manifests/kube-state-metrics/rbac.yaml"
|
|
||||||
- "argocd/manifests/kube-state-metrics-ringtail/rbac.yaml"
|
|
||||||
Description: >-
|
|
||||||
CC: operator-purpose-bound-rbac (external-secrets);
|
|
||||||
kube-state-metrics-metadata-only (kube-state-metrics).
|
|
||||||
"KSV-0114":
|
|
||||||
Regions: ["*"]
|
|
||||||
Resources:
|
|
||||||
- "argocd/manifests/external-secrets/rbac.yaml"
|
|
||||||
Description: >-
|
|
||||||
CC: operator-purpose-bound-rbac. cert-controller manages the
|
|
||||||
external-secrets validating webhook configurations to inject
|
|
||||||
its own rotating CA bundle. RBAC is scoped to two named
|
|
||||||
webhooks (secretstore-validate, externalsecret-validate) via
|
|
||||||
resourceNames; KSV-0114 doesn't see the resourceNames
|
|
||||||
restriction so reports the full ClusterRole.
|
|
||||||
39
argocd/manifests/prowler/mutelist/trivyignore.yaml
Normal file
39
argocd/manifests/prowler/mutelist/trivyignore.yaml
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Trivy ignorefile for Prowler IaC scan.
|
||||||
|
#
|
||||||
|
# Prowler's `--mutelist-file` flag is a no-op for the IaC provider
|
||||||
|
# (iac_provider.py sets self._mutelist = None and delegates to Trivy).
|
||||||
|
# Trivy in turn does not auto-discover this YAML form from cwd, so the
|
||||||
|
# Prowler image ships a shim wrapper around `trivy` that injects
|
||||||
|
# --ignorefile $TRIVY_IGNOREFILE when the env var is set. The cronjob
|
||||||
|
# mounts this file and sets TRIVY_IGNOREFILE accordingly.
|
||||||
|
#
|
||||||
|
# Schema: https://trivy.dev/latest/docs/configuration/filtering/
|
||||||
|
# IDs use the hyphenated form Trivy displays (KSV-0041, not KSV0041).
|
||||||
|
misconfigurations:
|
||||||
|
- id: KSV-0041
|
||||||
|
paths:
|
||||||
|
- "argocd/manifests/external-secrets/rbac.yaml"
|
||||||
|
statement: >-
|
||||||
|
CC: operator-purpose-bound-rbac. external-secrets-operator's entire
|
||||||
|
function is to read and synthesize Secret objects; ClusterRole over
|
||||||
|
secrets is its purpose. Both the controller and cert-controller are
|
||||||
|
upstream-defined.
|
||||||
|
- id: KSV-0041
|
||||||
|
paths:
|
||||||
|
- "argocd/manifests/kube-state-metrics/rbac.yaml"
|
||||||
|
- "argocd/manifests/kube-state-metrics-ringtail/rbac.yaml"
|
||||||
|
statement: >-
|
||||||
|
CC: kube-state-metrics-metadata-only. KSM exposes only Secret
|
||||||
|
metadata (name, namespace, type, labels), never the data field.
|
||||||
|
list/watch on secrets is required for kube_secret_info /
|
||||||
|
kube_secret_labels metrics.
|
||||||
|
- id: KSV-0114
|
||||||
|
paths:
|
||||||
|
- "argocd/manifests/external-secrets/rbac.yaml"
|
||||||
|
statement: >-
|
||||||
|
CC: operator-purpose-bound-rbac. cert-controller manages the
|
||||||
|
external-secrets validating webhook configurations to inject its
|
||||||
|
own rotating CA bundle. RBAC is scoped to two named webhooks
|
||||||
|
(secretstore-validate, externalsecret-validate) via resourceNames;
|
||||||
|
KSV-0114 doesn't see the resourceNames restriction so reports the
|
||||||
|
full ClusterRole.
|
||||||
|
|
@ -44,10 +44,28 @@ RUN ARCH=$(dpkg --print-architecture) \
|
||||||
&& apt-get update && apt-get install -y --no-install-recommends wget ca-certificates \
|
&& apt-get update && apt-get install -y --no-install-recommends wget ca-certificates \
|
||||||
&& wget -q "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_${TRIVY_ARCH}.tar.gz" -O /tmp/trivy.tar.gz \
|
&& wget -q "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_${TRIVY_ARCH}.tar.gz" -O /tmp/trivy.tar.gz \
|
||||||
&& tar xzf /tmp/trivy.tar.gz -C /usr/local/bin trivy \
|
&& tar xzf /tmp/trivy.tar.gz -C /usr/local/bin trivy \
|
||||||
&& chmod +x /usr/local/bin/trivy \
|
&& mv /usr/local/bin/trivy /usr/local/bin/trivy.real \
|
||||||
|
&& chmod +x /usr/local/bin/trivy.real \
|
||||||
&& rm /tmp/trivy.tar.gz \
|
&& rm /tmp/trivy.tar.gz \
|
||||||
&& apt-get purge -y wget && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*
|
&& apt-get purge -y wget && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Shim: Prowler's IaC provider invokes `trivy fs` directly with no
|
||||||
|
# --ignorefile flag, so any TRIVY_IGNOREFILE the user sets is ignored.
|
||||||
|
# This wrapper injects --ignorefile when the env var points at a real
|
||||||
|
# file and the invocation is `trivy fs ...`. Other subcommands and
|
||||||
|
# global-only invocations (--version, --help) pass through unchanged.
|
||||||
|
# TODO(upstream): contribute --ignorefile plumbing to prowler-cloud/prowler
|
||||||
|
# iac_provider.py so this shim isn't necessary.
|
||||||
|
RUN printf '%s\n' \
|
||||||
|
'#!/bin/sh' \
|
||||||
|
'if [ "${1:-}" = "fs" ] && [ -n "${TRIVY_IGNOREFILE:-}" ] && [ -f "${TRIVY_IGNOREFILE}" ]; then' \
|
||||||
|
' shift' \
|
||||||
|
' exec /usr/local/bin/trivy.real fs --ignorefile "${TRIVY_IGNOREFILE}" "$@"' \
|
||||||
|
'fi' \
|
||||||
|
'exec /usr/local/bin/trivy.real "$@"' \
|
||||||
|
> /usr/local/bin/trivy \
|
||||||
|
&& chmod +x /usr/local/bin/trivy
|
||||||
|
|
||||||
RUN addgroup --gid 1000 prowler \
|
RUN addgroup --gid 1000 prowler \
|
||||||
&& adduser --uid 1000 --gid 1000 --disabled-password --gecos "" prowler \
|
&& adduser --uid 1000 --gid 1000 --disabled-password --gecos "" prowler \
|
||||||
&& mkdir -p /tmp/.cache/trivy && chown prowler:prowler /tmp/.cache/trivy
|
&& mkdir -p /tmp/.cache/trivy && chown prowler:prowler /tmp/.cache/trivy
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Address the 6 critical Prowler IaC findings against `argocd/manifests/`. The IaC cronjob now passes `--mutelist-file` (previously unused), fed from a new `mutelist/iac.yaml`. Two new compensating controls — `operator-purpose-bound-rbac` and `kube-state-metrics-metadata-only` — justify muting the `external-secrets` and `kube-state-metrics` Secret-access findings (KSV-0041, KSV-0114). Separately, `grafana-clusterrole` is tightened to remove `secrets` access entirely: the dashboard sidecar already only consumes ConfigMap-labeled dashboards, so its `RESOURCE` env var is now `configmap` instead of `both`.
|
Address the 6 critical Prowler IaC findings against `argocd/manifests/`. Prowler's IaC provider hardcodes `self._mutelist = None` and delegates filtering to Trivy, but doesn't plumb `--ignorefile` through — so the documented "use Trivy filtering" path is actually broken. Added a shim around `trivy` in the Prowler image that injects `--ignorefile $TRIVY_IGNOREFILE` for `trivy fs` invocations when the env var points at a real file. The IaC cronjob now mounts `mutelist/trivyignore.yaml` (Trivy's per-path schema) and sets the env var. Two new compensating controls — `operator-purpose-bound-rbac` and `kube-state-metrics-metadata-only` — justify muting the `external-secrets` and `kube-state-metrics` Secret-access findings (KSV-0041, KSV-0114). Separately, `grafana-clusterrole` is tightened to remove `secrets` access entirely: the dashboard sidecar already only consumes ConfigMap-labeled dashboards, so its `RESOURCE` env var is now `configmap` instead of `both`.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue