Skip to content

One chart for kubeaid-agent and both exporters, plus Kubescape RBAC - #217

Merged
ashish1099 merged 5 commits into
masterfrom
feat/security-exporter-kubescape
Aug 18, 2026
Merged

One chart for kubeaid-agent and both exporters, plus Kubescape RBAC#217
ashish1099 merged 5 commits into
masterfrom
feat/security-exporter-kubescape

Conversation

@ashish1099

@ashish1099 ashish1099 commented Aug 18, 2026

Copy link
Copy Markdown
Member

Collapses the three charts that make up a cluster's Obmondo reporting into one, and teaches the
security exporter to read Kubescape as well as Trivy.

One chart, one Argo CD application

argocd-helm-charts/kubeaid-agent now ships all three workloads. backup-exporter and
kubeaid-security-exporter are removed as separate charts, and the security exporter drops its
kubeaid- prefix to match backup-exporter's naming.

Workload Purpose
kubeaid-agent Talks to the Obmondo API over mTLS. Holds the credential; holds no CRD access.
security-exporter Collects security posture, serves /api/v1/security-posture. Holds cluster-wide read; talks to nothing outside the cluster.
backup-exporter Reports backup health for PostgreSQL, Velero, MongoDB and sealed-secrets.

Three Deployments with three ServiceAccounts, not sidecars. A pod carries one ServiceAccount, so
co-locating them would hand the workload holding the Obmondo credential the exporters' cluster-wide
read — undoing, one layer down, the separation that splitting them out achieved. It also bounds
failure: a security collection pass holds every VulnerabilityReport in memory at once, and a sidecar
OOM there would take the agent down with it, silencing the cluster-liveness ping.

Each exporter is independently switchable: securityExporter.enabled, backupExporter.enabled.

Two names are pinned, and both would have failed silently

  • backup-exporter — the agent discovers it by listing Deployments and Services with
    app.kubernetes.io/name=backup-exporter (internal/core/backup/exporter.go). Derived from
    .Chart.Name that label would read kubeaid-agent, the selector would match nothing, and
    discover() would return errExporterAbsent — indistinguishable from "not installed", so backups
    would simply stop being reported. Its upstream fullname also fell back to a bare .Release.Name,
    which here would have collided head-on with the agent's own Deployment and Service.
  • security-exporter — the agent reaches it at the Service name in
    appConfig.securityPosture.exporterURL, updated to match.

Kubescape as a second vulnerability source

The exporter's Source interface, discovery-based scanner detection and the Relevant field already
existed but were never wired to anything. This grants the RBAC that makes Kubescape readable:
spdx.softwarecomposition.kubescape.io (vulnerabilitymanifests, workloadconfigurationscans).

Served by an aggregated APIService rather than CRDs, which is why detection uses API discovery — a
CRD-existence check would silently never find it. Exactly one scanner is active at a time, Trivy
first by precedence.

The exporter-side Go implementation is in
EnableIT/kubeaid-security-exporter
on feat/kubescape-source.

Also here

  • promtool workflow repointed at the new chart path and template names; rule fixtures moved with it.
  • Docs updated: the backup-exporter guide (including re-nesting its values example under
    backupExporter), monitoring.md, backup-restore.md.
  • Fixed three comments-indentation warnings and a missing trailing newline inherited from the old
    backup-exporter values — CI runs yamllint --strict, where warnings fail.
  • Corrected the README's documented exporterURL default, which no longer matched the actual default.

Verification

Rendered the chart and checked: three Deployments, three ServiceAccounts, three ClusterRoles, no name
collisions, both exporters carrying the labels the agent selects on, the agent's rendered
exporterURL matching the rendered Service name, and every enable toggle honoured. helm lint and
yamllint --strict clean. The promtool CI step reproduced locally: 8 backup rules + unit tests pass,
1 security rule checks.

Before merging

  • Tag the exporter image. The chart pins ghcr.io/obmondo/kubeaid-security-exporter:v0.1.0,
    which does not exist yet. With securityExporter.enabled defaulting to true, merging first
    gives every cluster an ImagePullBackOff on the new Deployment. Tag it, or default it to
    false until the image ships.
  • Plan the Argo app migration. Clusters already running backup-exporter as its own
    application will have that app and this chart both claiming identically-named objects. The old
    apps need deleting, and kubeaid-cli needs to stop creating them — not touched in this PR.

… api

The exporter now reads Kubescape as an alternative to Trivy. Every rule in this
ClusterRole is consumed through a dynamic client, so without the grant the
scanner is found through discovery and then fails every collection -- which at
least fails loudly, but the feature is inert until this lands.

vulnerabilitymanifests carries the per-CVE detail; the summaries kind holds
counters only and is deliberately not read. workloadconfigurationscans is the
control-scan equivalent of Trivy's three least-privilege report kinds.

Unlike Trivy's CRDs these resources come from an aggregated APIService, so they
vanish from discovery whenever the kubescape storage pod is down rather than
persisting the way a CRD would.

The README gains which-scanner-wins and the two fields that differ between them:
relevant, which only Kubescape can answer, and os.eosl, which only Trivy can.
Collapses argocd-helm-charts/kubeaid-security-exporter into the agent chart, so
a cluster gets both workloads from one Argo CD application instead of two, and
there is one chart to maintain rather than two.

They remain TWO Deployments with TWO ServiceAccounts. That is the point of the
arrangement, not an accident of it: a pod carries a single ServiceAccount, so
running the exporter as a sidecar would hand the workload holding the Obmondo
mTLS credential the exporter's cluster-wide read across seven API groups --
undoing, one layer down, the separation that splitting them achieved. It also
bounds failure: a collection pass holds every VulnerabilityReport in memory at
once, and a sidecar OOM would take the agent down with it, silencing the
cluster-liveness ping that tells the portal a cluster is alive.

The exporter's Service name is pinned to kubeaid-security-exporter rather than
derived from the release name. The agent's default
appConfig.securityPosture.exporterURL is that literal string, and a
release-prefixed Service would leave the agent polling a host that does not
resolve -- which fails quietly, because a failed poll only sets a metric and
submits nothing. The helper says so, so nobody "fixes" it later.

securityExporter.enabled: false runs the agent alone. The agent already
tolerates a missing exporter, so that switch and
appConfig.securityPosture.enabled stay independent.

backup-exporter is deliberately left as its own chart. Its values are some two
hundred lines of S3 credentials, RPO policy and four alert blocks for Postgres,
Velero, MongoDB and sealed-secrets, none of which the agent has anything to do
with; folding it in would make the agent chart mostly backup configuration, and
would drag five docs pages and a promtool CI job along with it.
…ity-exporter

Collapses argocd-helm-charts/backup-exporter into the agent chart alongside the
security exporter, so one Argo CD application deploys all three workloads and
there is one chart to maintain instead of three. The security exporter also
loses its kubeaid- prefix, matching backup-exporter's naming.

All three stay SEPARATE Deployments with SEPARATE ServiceAccounts. A pod carries
one ServiceAccount, so running the exporters as sidecars would hand the workload
holding the Obmondo mTLS credential their cluster-wide read -- undoing, one layer
down, the separation that splitting them out achieved.

Two names are pinned rather than release-derived, and both would have failed
silently otherwise:

  - backup-exporter. The agent discovers it by listing Deployments AND Services
    with app.kubernetes.io/name=backup-exporter
    (internal/core/backup/exporter.go). Derived from .Chart.Name that label
    would read "kubeaid-agent", the selector would match nothing, and discover()
    would return errExporterAbsent -- indistinguishable from "not installed", so
    backups would simply stop being reported. Its upstream fullname also fell
    back to a bare .Release.Name, which here would have collided head-on with
    the agent's own Deployment and Service.
  - security-exporter. The agent reaches it at the Service name in
    appConfig.securityPosture.exporterURL, which is updated to match.

Both exporters are independently switchable via backupExporter.enabled and
securityExporter.enabled; the agent already tolerates either being absent.

The promtool workflow is repointed at the new chart path and template names, and
the rule fixtures move with it. The backup-exporter guide, monitoring and
backup-restore docs are updated, including re-nesting the guide's values example
under the backupExporter key.

Verified by rendering: three Deployments, three ServiceAccounts, three
ClusterRoles, no name collisions, both exporters carrying the labels the agent
selects on, every enable toggle honoured, and the promtool step reproduced
locally (8 rules, unit tests pass).
Follow-up to the rename. The chart component is called security-exporter now,
but several places still named the old one, and the README's values table was
outright wrong: it documented the exporterURL default as
http://kubeaid-security-exporter while the actual default and the rendered
Service are both security-exporter. Anyone setting that value by hand from the
table would have pointed the agent at a host that does not resolve, which fails
quietly -- a failed poll only sets a metric.

Also renames the alert group to match the component, and updates the schema
descriptions, values comments and the RBAC comment.

The two remaining occurrences are correct and stay: the upstream repository is
still EnableIT/kubeaid-security-exporter and the image is still
ghcr.io/obmondo/kubeaid-security-exporter. Only the in-chart component was
renamed.
@ashish1099 ashish1099 changed the title Feat/security exporter kubescape One chart for kubeaid-agent and both exporters, plus Kubescape RBAC Aug 18, 2026
The backup exporter cannot start without configuration. Every backend it
reports on -- Postgres, Velero, MongoDB, sealed-secrets -- needs S3 credentials
and a bucket, and there is no sane default for any of them. Shipping it enabled
meant a fresh cluster deployed a pod that could not do its job.

The security exporter stays on: it needs no credentials, discovers its sources
through the API, and reports "not installed" where none are present.

kubeaid-cli has no support for supplying those S3 credentials yet, so until it
does, enabling this is a deliberate per-cluster act alongside the
backupExporter.exporter.<backend>.s3 settings.
@ashish1099
ashish1099 merged commit 5e3cc95 into master Aug 18, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant