81 Commits
Author SHA1 Message Date
Thibault VINCENT 8bab222d43 fix(chart): require https scheme for monitor basicAuth 2026-08-05 19:14:52 +02:00
Thibault VINCENT 732b3856f4 fix(chart): reject basicAuth on monitors when rbacProxy already authenticates scrapes 2026-08-05 19:14:52 +02:00
Thibault VINCENT ff3552d77f feat(chart): enforce strict values schema end-to-end 2026-08-05 19:14:52 +02:00
Thibault VINCENT 647ce98f16 fix(chart): render extraDeploy document separators lint-clean 2026-08-05 19:14:52 +02:00
enix-renovate[bot] db6df228f3 chore: update quay.io/brancz/kube-rbac-proxy docker tag to v0.22.1 2026-08-04 15:25:31 +02:00
Thibault VINCENT d73f3c0c76 feat(chart): per-alert expr and for overrides + render ratchet
Two new value maps under `prometheusRules`:

- `alertExprOverrides` — alertName -> custom Prometheus expression
  that replaces the default `expr:`. Closes #253: multi-cluster setups
  can inject `max by (cluster, …)` aggregations or label filters
  without having to disableBuiltinAlertGroup and reconstruct everything.
- `alertForOverrides` — alertName -> `for:` duration that replaces
  the default. Schema-validated against Prometheus's duration syntax.

Both maps key on bare alert names (no `rulePrefix`). Overrides are
taken whole — no merge with the default. Single-line and multi-line
strings both render correctly: the template now emits each `expr:`
via `| quote` which wraps in a YAML double-quoted scalar (newlines
serialise to \n; Prometheus parses the resulting string identically
to the multi-line form).

Test ratchets added alongside:

- test/schema/valid/prometheusrules-alert-overrides.yaml — valid case
  covering both maps, with a multi-line expr to exercise the quoting
  path.
- test/schema/invalid/prometheusrules-alertforoverrides-bad-duration
  — paired with .expect.txt, ensures the duration regex rejects
  natural-language inputs ("30 minutes").
- test/render/prometheusrules-default-alerts.{yaml,expect-pass.txt} —
  new ratchet under the existing TestHelmRender machinery (extended
  to support "positive substring" checks via .expect-pass.txt). Locks
  in the exact 13 alert names the chart ships by default: any rename
  or accidental removal breaks the test at PR time.

Docs:

- chart/README.md.gotmpl alert table refreshed — was still on the v3
  layout (X509ExporterReadErrors + 3 cert alerts). Now lists all 13.
- values.yaml gets the two new map keys with descriptions and
  commented examples.
2026-05-25 19:18:58 +02:00
Thibault VINCENT 82e96e11a3 refactor(chart)!: rebuild PrometheusRule template, rename v3-era alerts
MAJOR CHANGE: PrometheusRule alert names and chart value keys
renamed. Users with AlertManager routing or silencing rules that
reference the old names must update them on upgrade. Migration map:

  X509ExporterReadErrors     -> SourceErrors (+ SourceErrorsSustained)
  alertOnReadErrors          -> alertOnSourceErrors
  readErrorsSeverity         -> sourceErrorsSeverity (+ ...SustainedSeverity)
  alertOnPassphraseFailures  -> alertOnKeystorePassphraseFailures
  passphraseFailuresSeverity -> keystorePassphraseFailuresSeverity
  alertOnCertificateErrors   -> alertOnCertificateError       (singular)
  certificateErrorsSeverity  -> certificateErrorSeverity      (singular)
  alertOnCertificateCollisions -> alertOnCertificateCollision (singular)
  certificateCollisionsSeverity -> certificateCollisionSeverity
  alertOnCRLs                -> (removed; CRL alerts always-on like Renewal/Expiration)

Substantive changes alongside the renames:

- SourceErrors gets the same two-band shape as KubeTransportErrors
  (warning at >5/15min for 5m, critical for 30m). The previous "fire
  on any single error" expression paged on a single malformed PEM,
  which was indistinguishable from a real outage.
- SourceErrors and KubeTransportErrors both aggregate over
  reason/resource via 'sum without (...)', so the alert routes by
  source. The breakdown stays available on the metric for triage.
- CRLNeedsRefresh and CRLStale no longer require alertOnCRLs — same
  policy as CertificateRenewal/Expiration. The underlying x509_crl_*
  series only exist when a CRL is actually observed, so installs that
  don't watch CRLs get nothing.
- CertificateRenewal expression now anchors to (not_after - now) > 0,
  so it doesn't keep firing alongside CertificateExpiration once a
  cert is past its NotAfter (those two alerts had identical truth
  values in the late-life window).
- CertificateExpiration description distinguishes 'expires in X' from
  'expired X ago' via humanizeDuration's sign, so the message stays
  accurate after the cert is past NotAfter.
- CertificateError, CertificateNotYetValid, CertificateRenewal and
  CertificateExpiration each get inline comments explaining their
  dependency or always-on status — consistent with the recent
  KubeTransportErrors / CertificateCollision additions.

Template hygiene:

- Three helper templates in _helpers.tpl: alertExtraLabels and
  alertExtraAnnotations (replace 12 duplicated if-blocks);
  alertLocationSuffix (replaces the 5 sites that re-emit the
  "{{if $labels.secret_name}}in Kubernetes secret …{{else}}at
  location …{{end}}" literal — when we flipped quotes to backticks
  recently, 5 edits were required; the helper would have made that
  one).
- All alerts stay in a single PrometheusRule group. Groups in
  PrometheusRule only matter for sequential recording-rule ordering,
  per-group evaluation intervals, or execution isolation — none of
  which apply here. AlertManager routes on labels (alertname,
  severity), not on group names, so splitting by domain would
  fragment without buying anything; worse, it would trap users who
  add recording rules via extraAlertGroups into the wrong group.
  Category-based routing on the consumer side is straightforward via
  alertname regex (Source.*, Cert.*, CRL.*, Kube.*).
2026-05-25 19:18:58 +02:00
Thibault VINCENT 52e9c2e66c feat: emit x509_kube_transport_errors_total for LIST/WATCH/informer failures
The Kubernetes source's transport layer had 9 distinct failure sites
that surfaced only as logs: LIST failure (with retry/backoff), WATCH
start failure, WATCH stream Error event, watch flap (close within 5s
of open), and namespace informer sync timeout — once for secrets, once
for configmaps. None had a corresponding metric, so an exporter that
was functionally up but operationally degraded (token rotation drift,
apiserver throttling, intermittent network) was invisible to ops.

Introduce x509_kube_transport_errors_total{source_name,resource,reason}
incremented at each of the 9 sites via a new Recorder interface on
k8s.Options. The interface keeps the source decoupled from
*registry.Registry; nil is valid (the unmetered path keeps the old
log-only behaviour) and tests plug a fake. main.go wires the actual
registry through.

Ship a paired KubeTransportErrors PrometheusRule alert (opt-in via
alertOnKubeTransportErrors, default true). docs/metrics.md documents
the new metric, the five reason codes, and cross-links the alert.
2026-05-25 19:18:58 +02:00
Thibault VINCENT 5291d5a989 feat: split collision counter metric into total + dropped
Detect-only x509_cert_collision_total ticks on every scrape under the
default CollisionAuto policy, even when the discriminator label
silently resolves the overlap and no certificate is dropped. Alerting
on it produced perma-firing pages for a benign config quirk.

Introduce x509_cert_collision_dropped_total: counts only the items
the registry actually threw away (CollisionNever policy). The
CertificateCollision PrometheusRule now points at the new counter so
the alert fires only when data is genuinely lost. The detect-only
counter stays available for dashboards and diagnostics.

Tests in pkg/registry cement the contract: dropped stays at 0 under
CollisionAuto and is positive under CollisionNever. docs/metrics.md
documents both counters and cross-links them.
2026-05-25 19:18:58 +02:00
Thibault VINCENT ee273b4b04 feat(chart): add SourceDown, PassphraseFailures, NotYetValid, Collision alerts
Four new opt-in alerts close observability holes the existing rules
left uncovered:

- SourceDown (critical) — x509_source_up == 0 for 5m. Catches RBAC
  failures, persistent K8s API errors, unreadable file paths. A source
  that never converges would otherwise be silent while certs it should
  watch are not checked.
- KeystorePassphraseFailures (warning) — increase on
  x509_{pkcs12,jks}_passphrase_failures_total. A misconfigured
  passphraseKey / passphraseSecretRef previously only leaked into
  logs and an unwatched counter.
- CertificateNotYetValid (warning) — x509_cert_not_before > time().
  Depends on exposeNotBeforeMetric: true; gated for explicit opt-in.
- CertificateCollision (warning) — increase on x509_cert_collision_total.
  Two certs sharing a label set means one is silently invisible.

Each alert ships with an alertOnXxx toggle (default true) and its own
severity in values.yaml. Schema + README regenerated. helm lint, helm
template, schema fixtures and helm-examples all pass.
2026-05-25 19:18:58 +02:00
Thibault VINCENT a782c9b20d fix(chart): rename x509_read_errors to x509_source_errors_total and use increase()
The X509ExporterReadErrors PrometheusRule alert matched x509_read_errors,
the v3 metric name. The v4 exporter emits x509_source_errors_total
instead — the alert never fired since the v4 rewrite. Same stale name
lingered in the values.yaml extraAlertGroups example and across 8
Grafana dashboard panels. All three sites renamed in lockstep.

Switch the alert's range function from delta() to increase() while
we're here. delta() is documented for gauges; for monotonic counters
increase() is the canonical choice and correctly handles counter
resets across exporter restarts.
2026-05-25 19:18:58 +02:00
Thibault VINCENT 9d32abf486 fix(chart): use backticks instead of double quotes in alert descriptions
Closes #400. AlertManager descriptions wrapped subject_CN, issuer_CN,
secret_namespace/secret_name and filepath in double quotes, which
broke parsing when the description was reinjected into JSON-bearing
webhook payloads (Teams Adaptive Cards, custom receivers).

Switch to backticks: no JSON escaping required, Slack/Teams/Discord
render them as inline code spans, plain-text destinations still show
the value clearly. The surrounding Helm template machinery is
unchanged — only the literal quote characters around the placeholders
flip from " to `.
2026-05-25 19:18:58 +02:00
Thibault VINCENT 0eed4da25d feat(chart, docs): expose jks.passphraseSecretRef in schema + examples 2026-05-15 16:54:25 +02:00
Thibault VINCENT a163963eeb docs(README, metrics): announce JKS/JCEKS support and x509_jks_passphrase_failures_total 2026-05-13 17:55:30 +02:00
Thibault VINCENT 3bcafda799 feat(chart): add jks format + jks: passphrase block to secretTypes schema 2026-05-13 17:55:30 +02:00
Thibault VINCENT 0e186d8059 docs(README): announce support for DER and CRLs 2026-05-13 17:12:37 +02:00
Thibault VINCENT c6e9aecf02 docs(chart, metrics): document DER format and add a positive schema fixture 2026-05-13 16:57:02 +02:00
Thibault VINCENT 5770c37dfc docs(readme): announce CRL freshness monitoring in the v4 highlights 2026-05-13 16:57:02 +02:00
Thibault VINCENT 9d4b97f1f1 feat(chart): add CRLNeedsRefresh and CRLStale PrometheusRule alerts 2026-05-13 16:57:02 +02:00
Thibault VINCENT 7610ca68fe feat(cabundle): extend source to APIService and CRD conversion webhooks 2026-05-13 14:24:36 +02:00
Thibault VINCENT 830ca14902 feat(chart): expose cluster-scoped cabundle exporter (opt-in) 2026-05-13 14:24:36 +02:00
Thibault VINCENT 4fbbafa8c7 chore(chart): tighten schema annotations on secretsExporter selector arrays 2026-05-13 14:07:27 +02:00
Thibault VINCENT 2958d64ecd fix(chart): omit registry segment when image.registry is empty
Fix #499
2026-05-12 18:16:22 +02:00
Thibault VINCENT 9626e52baf feat(chart): support glob and recursive (**) patterns in watchDirectories
Fix #108
2026-05-12 15:08:40 +02:00
Thibault VINCENT 587d0f1589 feat(chart): expose include/exclude secrets and configmaps by name 2026-05-11 18:16:38 +02:00
Thibault VINCENT ab1ce3ad77 feat(k8s): support shell-glob patterns in include/exclude names 2026-05-11 18:16:38 +02:00
Thibault VINCENT c3a255cd47 docs(README): don't generalize memory gains in v4 2026-05-07 16:08:53 +02:00
Thibault VINCENT 4860a8069d refactor(chart): drop redundant chart-version guards in migration tasks 2026-05-07 12:57:44 +02:00
Thibault VINCENT 8f1a1f9585 fix(chart): preserve build metadata in pre-upgrade version detection
fix #580
2026-05-07 12:21:34 +02:00
Thibault VINCENT e4457ef2c3 feat(chart): drop privileged from hostPathsExporter securityContext defaults
fix #581
2026-05-07 12:21:34 +02:00
Thibault VINCENT 26c1a48c87 refactor(chart): consolidate pre-upgrade hooks into one task-oriented job 2026-05-07 12:21:34 +02:00
Thibault VINCENT 36b8902ffb docs(chart): fix misplaced example; clarify description 2026-05-07 12:21:34 +02:00
Thibault VINCENT 91cd1543c5 docs(README): add what's new in v4 sections (home and chart) 2026-05-06 14:43:51 +02:00
Thibault VINCENT 711c27dd77 feat(chart): switch default image.registry from docker.io to quay.io 2026-05-06 04:09:01 +02:00
Thibault VINCENT 5c82ade682 fix(chart): kube-rbac-proxy deprecation warnings — explicit TLS cert + drop --logtostderr 2026-05-06 03:41:57 +02:00
Thibault VINCENT 7463220175 feat(chart): plain-HTTP probe port replaces tcpSocket fallback for auth-gated /metrics 2026-05-06 02:56:14 +02:00
Thibault VINCENT 486cd1bd62 fix(chart): rbacProxy plumbing + tcpSocket probes when /metrics is auth-gated 2026-05-06 02:38:54 +02:00
Thibault VINCENT 7541605185 fix(chart): tighten values.schema.json (missing enums and nullable defaults) 2026-05-06 01:37:07 +02:00
Thibault VINCENT f51b74460d fix(chart): rbacProxy.enable typo silently dropped RBAC and monitor scrape config 2026-05-06 01:32:11 +02:00
Thibault VINCENT abbb9b5fa8 feat(chart): values.schema.json with strict mode + helm-side regression tests 2026-05-06 01:21:22 +02:00
Thibault VINCENT 076dd46b77 feat(chart): expose ConfigMap labels as Prometheus labels via configMaps.exposeLabels 2026-05-05 21:38:11 +02:00
Thibault VINCENT b3e2c024ff fix(chart): enable privileged mode for hostPathsExporter by default 2026-05-05 19:11:44 +02:00
Thibault VINCENT 6ec3aeeac9 fix(chart): make pre-upgrade-4-0-0 also delete the v3 Deployment and DaemonSets 2026-05-05 18:19:05 +02:00
Thibault VINCENT 2d5cb1d346 fix(chart): exposeRelative/exposePerCertError defaults flipped on by Helm 'default true' 2026-05-05 16:35:38 +02:00
Thibault VINCENT a65d5758bd docs(chart): warn about high-cardinality exposed secret labels 2026-05-05 14:24:13 +02:00
Thibault VINCENT 66d2ee4bb0 feat(k8s): make LIST page size configurable per source 2026-05-05 14:20:37 +02:00
Thibault VINCENT 2ee438dda2 refactor(k8s): replace SharedInformer with paginated direct LIST+WATCH 2026-05-05 14:17:17 +02:00
Thibault VINCENT 5cc5b6112d refactor(chart): new project logo for chart icon 2026-05-04 14:48:05 +02:00
Thibault VINCENT 84b9d1d0df chore(helm): remove remnants of release-please managed annotations and version 2026-05-04 13:08:15 +02:00
Thibault VINCENT 63b7c8ce81 feat(chart): add v4 pre-upgrade hook for service clusterIP changes 2026-05-04 04:03:08 +02:00