|
| 1 | +# kubeaid-security-exporter |
| 2 | + |
| 3 | +Deploys the [KubeAid Security Exporter](https://gitea.obmondo.com/EnableIT/kubeaid-security-exporter) — a |
| 4 | +Kubernetes-native exporter that reads a cluster's security posture and reports it as Prometheus metrics and |
| 5 | +a JSON API. It only ever reads: no Kubernetes object is created, mutated, or deleted. |
| 6 | + |
| 7 | +What it collects, where the relevant operator is installed: |
| 8 | + |
| 9 | +- **Vulnerabilities and least-privilege findings** from Trivy Operator's report CRs — full CVE detail |
| 10 | + (CVSS score, installed and fixed version, advisory link), rather than the lossy Prometheus projection. |
| 11 | +- **Upgrade availability**, by joining those findings against version-checker on canonical image |
| 12 | + references resolved in Go. Rebuilding image references in PromQL fails silently. |
| 13 | +- **Network enforcement** from Cilium — whether policy is actually realised on an app's pods, which is a |
| 14 | + different question from whether the app ships a policy. |
| 15 | +- **Runtime detection posture** from Tetragon and KubeArmor — which engines are armed, and whether they |
| 16 | + observe or enforce. |
| 17 | + |
| 18 | +None of those are required. Each source is detected through API discovery, and an absent operator is |
| 19 | +reported as absent rather than as a failure or as a clean result. |
| 20 | + |
| 21 | +## Relationship to kubeaid-agent |
| 22 | + |
| 23 | +The exporter collects; it never talks to the Obmondo API. `kubeaid-agent` GETs `/api/v1/security-posture` |
| 24 | +from this Service and forwards the snapshot, so reporting posture off-cluster is the agent's concern and |
| 25 | +cluster read access is this chart's. That split is why the agent holds no CRD access at all. |
| 26 | + |
| 27 | +Deploying this chart without the agent is fine — the metrics and the JSON API work standalone. |
| 28 | + |
| 29 | +## Prerequisites |
| 30 | + |
| 31 | +- **trivy-operator**, for vulnerability and least-privilege data. Without it the exporter reports no |
| 32 | + findings, and says so rather than reporting a clean cluster. |
| 33 | +- **version-checker**, for upgrade availability. Without it findings still ship, with upgrade availability |
| 34 | + unknown rather than "up to date". |
| 35 | +- kube-prometheus, if `serviceMonitor` / `prometheusRule` stay enabled (both default to `true`). |
| 36 | + |
| 37 | +Cilium, Tetragon and KubeArmor are read when present and skipped when not. |
| 38 | + |
| 39 | +## Key values |
| 40 | + |
| 41 | +| Value | Default | Meaning | |
| 42 | +|---|---|---| |
| 43 | +| `exporter.interval` | `12h` | Collection cadence. Trivy refreshes its reports on a 24h TTL, so polling faster re-reads identical data. | |
| 44 | +| `exporter.port` | `8080` | Serves `/healthz`, `/readyz`, `/metrics` and `/api/v1/security-posture`. | |
| 45 | +| `prometheusRule.upgradableThreshold` | `20` | `ImageOutdatedAndVulnerable` fires above this many images having both a fixable Critical/High CVE and a newer tag available. | |
| 46 | +| `prometheusRule.upgradableFor` | `24h` | How long the count must hold before the alert fires. | |
| 47 | + |
| 48 | +## Alerting |
| 49 | + |
| 50 | +One alert, `ImageOutdatedAndVulnerable`. It is a count, so it fires once per cluster rather than once per |
| 51 | +image, and the threshold is deliberately high — every real cluster carries a few of these at any moment, so |
| 52 | +a low threshold fires everywhere on day one and gets ignored. The signal worth acting on is a pile of easy |
| 53 | +upgrades, not the existence of one. |
| 54 | + |
| 55 | +Collection status is exported as `security_exporter_collection` (1 ok, 0 failed, -1 not installed) but is |
| 56 | +deliberately not alerted on: a collection failure is a debugging signal, not something worth paging for. |
| 57 | + |
| 58 | +## Operational notes |
| 59 | + |
| 60 | +- RBAC is least-privilege by construction — a purpose-built ClusterRole granting exactly the verbs the code |
| 61 | + calls, with no `watch` anywhere since the exporter builds no informers. Every rule is consumed through a |
| 62 | + dynamic client, so none of those resource names appear in Go source; removing a rule fails silently and |
| 63 | + renders the cluster as clean rather than erroring. |
| 64 | +- Runs unprivileged: non-root, read-only root filesystem, all capabilities dropped, `RuntimeDefault` seccomp. |
| 65 | +- A collection pass holds every VulnerabilityReport in memory at once, so the memory ceiling scales with |
| 66 | + image count rather than with request rate. |
| 67 | + |
| 68 | +## Docs links |
| 69 | + |
| 70 | +- Chart source: `templates/` and [values.yaml](./values.yaml) in this directory (documented inline). |
| 71 | +- Obmondo: <https://obmondo.com> |
0 commit comments