diff --git a/argocd/apps/prowler.yaml b/argocd/apps/prowler.yaml new file mode 100644 index 0000000..a98aa4f --- /dev/null +++ b/argocd/apps/prowler.yaml @@ -0,0 +1,17 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: prowler + namespace: argocd +spec: + project: default + source: + repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git + targetRevision: main + path: argocd/manifests/prowler + destination: + server: https://kubernetes.default.svc + namespace: prowler + syncPolicy: + syncOptions: + - CreateNamespace=true diff --git a/argocd/manifests/prowler/cronjob.yaml b/argocd/manifests/prowler/cronjob.yaml new file mode 100644 index 0000000..e9d6844 --- /dev/null +++ b/argocd/manifests/prowler/cronjob.yaml @@ -0,0 +1,53 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: prowler + namespace: prowler +spec: + schedule: "0 3 * * 0" # Sunday 3am + concurrencyPolicy: Forbid + jobTemplate: + spec: + ttlSecondsAfterFinished: 604800 # Auto-delete after 7 days + template: + spec: + serviceAccountName: prowler + containers: + - name: prowler + image: registry.ops.eblu.me/blumeops/prowler:kustomized + args: + - kubernetes + - --compliance + - cis_1.11_kubernetes + - -z + - --output-formats + - html,csv,json-ocsf + - --output-directory + - /reports + volumeMounts: + - name: reports + mountPath: /reports + - name: var-lib-kubelet + mountPath: /var/lib/kubelet + readOnly: true + - name: etc-kubernetes + mountPath: /etc/kubernetes + readOnly: true + - name: var-lib-etcd + mountPath: /var/lib/etcd + readOnly: true + hostPID: true + restartPolicy: OnFailure + volumes: + - name: reports + persistentVolumeClaim: + claimName: prowler-reports + - name: var-lib-kubelet + hostPath: + path: /var/lib/kubelet + - name: etc-kubernetes + hostPath: + path: /etc/kubernetes + - name: var-lib-etcd + hostPath: + path: /var/lib/etcd diff --git a/argocd/manifests/prowler/kustomization.yaml b/argocd/manifests/prowler/kustomization.yaml new file mode 100644 index 0000000..0cc8240 --- /dev/null +++ b/argocd/manifests/prowler/kustomization.yaml @@ -0,0 +1,15 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: prowler + +resources: + - serviceaccount.yaml + - rbac.yaml + - pv-nfs.yaml + - pvc.yaml + - cronjob.yaml + +images: + - name: registry.ops.eblu.me/blumeops/prowler + newTag: v5.22.0-placeholder diff --git a/argocd/manifests/prowler/pv-nfs.yaml b/argocd/manifests/prowler/pv-nfs.yaml new file mode 100644 index 0000000..9f76b22 --- /dev/null +++ b/argocd/manifests/prowler/pv-nfs.yaml @@ -0,0 +1,22 @@ +# NFS PersistentVolume for Prowler compliance reports +# Requires: NFS share on sifaka at /volume1/reports with NFS permissions for indri +# +# To create on Synology: +# 1. Control Panel > Shared Folder > Create +# 2. Name: reports, Location: Volume 1 +# 3. Control Panel > File Services > NFS > NFS Rules +# 4. Add rule for "reports" share: Hostname=indri, Privilege=Read/Write, Squash=No mapping +apiVersion: v1 +kind: PersistentVolume +metadata: + name: prowler-reports-nfs-pv +spec: + capacity: + storage: 10Gi + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: "" + nfs: + server: sifaka + path: /volume1/reports/prowler diff --git a/argocd/manifests/prowler/pvc.yaml b/argocd/manifests/prowler/pvc.yaml new file mode 100644 index 0000000..8d94378 --- /dev/null +++ b/argocd/manifests/prowler/pvc.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: prowler-reports + namespace: prowler +spec: + accessModes: + - ReadWriteMany + storageClassName: "" + volumeName: prowler-reports-nfs-pv + resources: + requests: + storage: 10Gi diff --git a/argocd/manifests/prowler/rbac.yaml b/argocd/manifests/prowler/rbac.yaml new file mode 100644 index 0000000..38fcfae --- /dev/null +++ b/argocd/manifests/prowler/rbac.yaml @@ -0,0 +1,24 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: prowler-reader +rules: + - apiGroups: [""] + resources: ["pods", "configmaps", "nodes", "namespaces"] + verbs: ["get", "list", "watch"] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["clusterroles", "clusterrolebindings", "roles", "rolebindings"] + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: prowler-reader +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: prowler-reader +subjects: + - kind: ServiceAccount + name: prowler + namespace: prowler diff --git a/argocd/manifests/prowler/serviceaccount.yaml b/argocd/manifests/prowler/serviceaccount.yaml new file mode 100644 index 0000000..26aaaa7 --- /dev/null +++ b/argocd/manifests/prowler/serviceaccount.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: prowler + namespace: prowler diff --git a/containers/prowler/Dockerfile b/containers/prowler/Dockerfile new file mode 100644 index 0000000..207b96a --- /dev/null +++ b/containers/prowler/Dockerfile @@ -0,0 +1,45 @@ +# Prowler CIS scanner — slim build for Kubernetes provider only +# Strips PowerShell (M365), Trivy (IaC), and dashboard dependencies from upstream +ARG CONTAINER_APP_VERSION=5.22.0 + +FROM python:3.12-slim-bookworm AS build + +ARG CONTAINER_APP_VERSION + +RUN apt-get update && apt-get install -y --no-install-recommends \ + git ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /build + +RUN git clone --depth 1 --branch ${CONTAINER_APP_VERSION} \ + https://github.com/prowler-cloud/prowler.git . + +# Install prowler into a virtualenv so we can copy it cleanly +RUN python -m venv /opt/prowler \ + && /opt/prowler/bin/pip install --no-cache-dir --upgrade pip \ + && /opt/prowler/bin/pip install --no-cache-dir . + +# --- + +FROM python:3.12-slim-bookworm + +ARG CONTAINER_APP_VERSION + +LABEL org.opencontainers.image.title="prowler" +LABEL org.opencontainers.image.version="${CONTAINER_APP_VERSION}" +LABEL org.opencontainers.image.source="https://forge.ops.eblu.me/eblume/blumeops" +LABEL org.opencontainers.image.vendor="blumeops" +LABEL org.opencontainers.image.description="Prowler CIS scanner (Kubernetes provider)" + +RUN addgroup --gid 1000 prowler \ + && adduser --uid 1000 --gid 1000 --disabled-password --gecos "" prowler + +COPY --from=build /opt/prowler /opt/prowler + +ENV PATH="/opt/prowler/bin:${PATH}" + +USER prowler +WORKDIR /home/prowler + +ENTRYPOINT ["prowler"] diff --git a/docs/changelog.d/deploy-prowler.feature.md b/docs/changelog.d/deploy-prowler.feature.md new file mode 100644 index 0000000..64236c7 --- /dev/null +++ b/docs/changelog.d/deploy-prowler.feature.md @@ -0,0 +1 @@ +Deploy Prowler CIS scanner as a weekly CronJob on minikube-indri, with reports written to sifaka NFS share. diff --git a/docs/how-to/operations/deploy-prowler.md b/docs/how-to/operations/deploy-prowler.md new file mode 100644 index 0000000..8c9980b --- /dev/null +++ b/docs/how-to/operations/deploy-prowler.md @@ -0,0 +1,63 @@ +--- +title: Deploy Prowler CIS Scanner +modified: 2026-03-24 +last-reviewed: 2026-03-24 +tags: + - how-to + - kubernetes + - security + - compliance +--- + +# Deploy Prowler CIS Scanner + +Prowler runs weekly CIS Kubernetes Benchmark scans against minikube-indri and writes HTML/CSV/JSON reports to the NFS share on sifaka. + +## What it checks + +Prowler's Kubernetes provider runs ~70 checks from the CIS Kubernetes Benchmark v1.11, grouped into: + +| Category | Checks | How it works | +|----------|--------|-------------| +| **Core (pod security)** | 13 | Queries K8s API for privileged containers, hostPID/hostNetwork, capabilities, secrets in env vars, seccomp | +| **RBAC** | 9 | Queries RBAC API for overprivileged roles, wildcard access, cluster-admin bindings | +| **Apiserver** | 29 | Inspects `kube-apiserver` pod args in kube-system (TLS, auth, audit, admission plugins) | +| **Etcd** | 7 | Inspects `etcd` pod args (TLS, cert auth) | +| **Controller Manager** | 7 | Inspects `kube-controller-manager` pod args | +| **Kubelet** | 16 | Reads kubelet-config ConfigMap + node file permissions (file checks need hostPID) | +| **Scheduler** | 2 | Inspects `kube-scheduler` pod args | + +**Minikube relevance:** Most checks work because minikube runs control plane as static pods. Kubelet file permission checks return MANUAL unless Prowler runs on the node (we mount host paths to enable this). + +**k3s note:** k3s embeds the control plane in a single binary — no static pods exist. Only core + RBAC checks (~22 of 70) produce results. Consider `kube-bench` for k3s control plane checks. + +## Reports + +Reports are written to `sifaka:/volume1/reports/prowler//` in three formats: + +- **HTML** — human-readable, self-contained, filterable +- **CSV** — flat tabular, one row per finding +- **JSON-OCSF** — structured, for SIEM ingestion + +## Running an ad-hoc scan + +```fish +kubectl create job --from=cronjob/prowler prowler-manual -n prowler --context=minikube-indri +``` + +Watch progress: + +```fish +kubectl logs -f job/prowler-manual -n prowler --context=minikube-indri +``` + +## Container + +Custom slim build at `containers/prowler/Dockerfile` — strips PowerShell, Trivy, and non-Kubernetes providers from upstream. See [[build-container-image]] for the build/release process. + +**TODO:** Mirror prowler-cloud/prowler on forge for supply chain control. + +## See also + +- [[deploy-k8s-service]] — general K8s deployment how-to +- [[build-container-image]] — container build pipeline diff --git a/docs/reference/kubernetes/apps.md b/docs/reference/kubernetes/apps.md index 270cc55..02215fc 100644 --- a/docs/reference/kubernetes/apps.md +++ b/docs/reference/kubernetes/apps.md @@ -40,6 +40,7 @@ Registry of all applications deployed via [[argocd]]. | `forgejo-runner` | forgejo-runner | `argocd/manifests/forgejo-runner/` | [[forgejo]] CI | | `ollama` | ollama | `argocd/manifests/ollama/` | [[ollama]] | | `mealie` | mealie | `argocd/manifests/mealie/` | [[mealie]] | +| `prowler` | prowler | `argocd/manifests/prowler/` | [[prowler]] | ## Sync Policies diff --git a/docs/reference/services/prowler.md b/docs/reference/services/prowler.md new file mode 100644 index 0000000..997a3a5 --- /dev/null +++ b/docs/reference/services/prowler.md @@ -0,0 +1,30 @@ +--- +title: Prowler +modified: 2026-03-24 +last-reviewed: 2026-03-24 +tags: + - service + - security +--- + +# Prowler + +CIS Kubernetes Benchmark scanner for compliance posture reporting. + +## Quick Reference + +| Property | Value | +|----------|-------| +| **Namespace** | `prowler` | +| **Image** | `registry.ops.eblu.me/blumeops/prowler` (see `argocd/manifests/prowler/kustomization.yaml` for current tag) | +| **Schedule** | Weekly (Sunday 3am) | +| **Reports** | `sifaka:/volume1/reports/prowler/` (NFS) | +| **Manifests** | `argocd/manifests/prowler/` | + +## What it does + +Runs Prowler 5 as a CronJob against minikube-indri, executing CIS Kubernetes Benchmark v1.11 checks across pod security, RBAC, apiserver, etcd, kubelet, controller-manager, and scheduler. Reports are written in HTML, CSV, and JSON-OCSF to the NFS share on sifaka. + +## See also + +- [[deploy-prowler]] — deployment how-to, ad-hoc scan instructions, check relevance notes