dne is a tiny Kubernetes controller that watches every Secret in the cluster, parses any X.509 certificate it finds (in any value, not just tls.crt), and exports Prometheus metrics for the certificates' validity windows. Pair it with the bundled Grafana dashboard and PrometheusRule to get a single pane of glass for "which TLS materials are about to expire."
- Zero configuration to get useful output: install the chart and you have
dne_certificate_not_after_secondsfor every cert in the cluster. - Four formats supported: PEM (
-----BEGIN CERTIFICATE-----), raw X.509 DER, PKCS#12 / PFX bundles, and JKS / JCEKS Java keystores (encrypted bundles use a password from the same Secret via annotation — see docs/configuration.md). - cert-manager interop:
--skip-cert-managerfilters out Secrets cert-manager already manages, so dne raises alerts only on the rest. - Multi-cert chains via a
cert_indexlabel. - Stable metric lifecycle: when a cert rotates, the previous series with the old subject/serial/SANs are removed; when a Secret is deleted, all its series are removed. No accumulating stale series.
- Optional namespace and label-selector filtering at the watch layer, so large clusters can scope the controller to a subset.
- Bundled Grafana dashboard and PrometheusRule, both gated by chart values and discoverable by the kube-prometheus-stack sidecar / operator out of the box.
helm repo add dne https://dgamo.github.io/dne
helm repo update
helm install dne dne/dne \
--namespace dne-system --create-namespace \
--set serviceMonitor.enabled=true \
--set prometheusRule.enabled=true \
--set grafanaDashboard.enabled=trueThen in Prometheus:
# Seconds until each cert expires, joined with identifying labels.
(dne_certificate_not_after_seconds - time())
* on(namespace, secret, key, cert_index) group_left(subject, issuer, dns_names)
dne_certificate_info
dne uses controller-runtime to watch Secret objects. For each one it walks every value and tries four formats in sequence, stopping at the first that produces certs:
- PEM —
pem.Decodein a loop,x509.ParseCertificatefor eachCERTIFICATEblock. - Raw DER —
x509.ParseCertificatedirectly on the bytes. - PKCS#12 —
pkcs12.DecodeChainwith the password (if any) looked up from thedne.k8s.io/pkcs12-passwordsannotation; falls back to the empty password for unencrypted bundles. - JKS / JCEKS — Java KeyStore decoded with the same annotation-supplied password; certificates emitted in alphabetical alias order with sequential
cert_index.
Values that match none of those are skipped silently. Multi-cert chains produce one metric series per cert, distinguished by a cert_index label.
If --skip-cert-manager (skipCertManager: true in Helm) is on, Secrets bearing cert-manager.io/certificate-name are filtered out entirely — their reconciles count toward dne_reconcile_total{result="skipped"} and any previously-emitted series are cleared.
Two Gauge collectors carry the numeric data (dne_certificate_not_after_seconds, dne_certificate_not_before_seconds) with a small label set; a companion dne_certificate_info gauge carries the identifying labels (subject, issuer, serial, DNS SANs) at value 1, following the kube-state-metrics pattern. This keeps the queryable gauges low-cardinality while letting dashboards join in the human-readable fields.
A small per-Secret tracker remembers exactly which label combinations were emitted last time, so cert rotation and Secret deletion drop the stale series instead of accumulating them.
See docs/configuration.md for the full reference. Most-asked-about settings:
| Flag / value | Default | Meaning |
|---|---|---|
namespaces |
[] |
Comma-separated list of namespaces to watch; empty = cluster-wide. |
labelSelector |
"" |
Standard k8s label selector applied to Secrets at the watch layer. |
replicaCount |
1 |
Run a single replica; enable leaderElection.enabled if you want >1. |
serviceMonitor.enabled |
false |
Emit a monitoring.coreos.com/v1 ServiceMonitor for kube-prometheus. |
prometheusRule.enabled |
false |
Emit a PrometheusRule with sensible default alert thresholds. |
grafanaDashboard.enabled |
false |
Emit a ConfigMap labelled grafana_dashboard=1 for the sidecar. |
See docs/metrics.md for the full reference, including the recommended PromQL.
The bundled PrometheusRule covers warning / critical expiry, already-expired, not-yet-valid (clock skew), controller-down, and parse errors. See docs/alerts.md.
See docs/dashboard.md. The dashboard JSON lives at deploy/grafana/dne.json and is importable manually if you don't use the sidecar.
See docs/development.md. Short version:
make test # unit + envtest, race detector on
make lint # golangci-lint
make helm-lint # helm lint + several template combinations
make kind-up
make kind-load IMG=dne:devWhy a ClusterRole instead of per-namespace Roles?
Even with --namespaces set, dne uses a ClusterRole (secrets: [get,list,watch]). Per-namespace Role objects would be unwieldy in the chart with no real security gain — the controller still authenticates as a single ServiceAccount. If you need strict isolation, deploy dne separately in each namespace.
Does dne ever write to Secrets? No. dne is a read-only observer.
See CONTRIBUTING.md.
Apache 2.0. See LICENSE.