From 4cc697ace43cde2e18c95d5aa8cff35870228220 Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Thu, 25 Mar 2021 08:03:04 -0600 Subject: [PATCH] autocert: add metrics for renewal count, total and next expiration (#2019) --- internal/autocert/manager.go | 4 ++ internal/telemetry/metrics/autocert.go | 90 ++++++++++++++++++++++++++ internal/telemetry/metrics/registry.go | 5 ++ pkg/metrics/constants.go | 3 + 4 files changed, 102 insertions(+) create mode 100644 internal/telemetry/metrics/autocert.go diff --git a/internal/autocert/manager.go b/internal/autocert/manager.go index 2b40b131b..4227977aa 100644 --- a/internal/autocert/manager.go +++ b/internal/autocert/manager.go @@ -17,6 +17,7 @@ import ( "github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/log" + "github.com/pomerium/pomerium/internal/telemetry/metrics" ) var ( @@ -177,6 +178,7 @@ func (mgr *Manager) obtainCert(domain string, cm *certmagic.Config) (certmagic.C log.Error().Err(err).Msg("autocert failed to obtain client certificate") return certmagic.Certificate{}, errObtainCertFailed } + metrics.RecordAutocertRenewal() cert, err = cm.CacheManagedCertificate(domain) } return cert, err @@ -226,6 +228,8 @@ func (mgr *Manager) updateAutocert(cfg *config.Config) error { cfg.AutoCertificates = append(cfg.AutoCertificates, cert.Certificate) } + metrics.RecordAutocertCertificates(cfg.AutoCertificates) + return nil } diff --git a/internal/telemetry/metrics/autocert.go b/internal/telemetry/metrics/autocert.go new file mode 100644 index 000000000..fea299a1f --- /dev/null +++ b/internal/telemetry/metrics/autocert.go @@ -0,0 +1,90 @@ +package metrics + +import ( + "crypto/tls" + "crypto/x509" + "sync/atomic" + "time" + + "go.opencensus.io/metric" + + "github.com/pomerium/pomerium/pkg/metrics" +) + +var ( + autocertRenewalsTotal int64 + autocertCertificatesTotal int64 + autocertCertificateNextExpiresSeconds int64 +) + +func registerAutocertMetrics(registry *metric.Registry) error { + gaugeMetrics := []struct { + name string + desc string + ptr *int64 + }{ + {metrics.AutocertCertificatesTotal, "Number of certificates tracked by autocert.", &autocertCertificatesTotal}, + {metrics.AutocertCertificateNextExpiresSeconds, "The next expiration timestamp in seconds.", &autocertCertificateNextExpiresSeconds}, + } + for _, gm := range gaugeMetrics { + m, err := registry.AddInt64DerivedGauge(gm.name, metric.WithDescription(gm.desc)) + if err != nil { + return err + } + err = m.UpsertEntry(func() int64 { + return atomic.LoadInt64(gm.ptr) + }) + if err != nil { + return err + } + } + + cumulativeMetrics := []struct { + name string + desc string + ptr *int64 + }{ + {metrics.AutocertRenewalsTotal, "Number of autocert renewals.", &autocertRenewalsTotal}, + } + for _, cm := range cumulativeMetrics { + m, err := registry.AddInt64DerivedCumulative(cm.name, metric.WithDescription(cm.desc)) + if err != nil { + return err + } + err = m.UpsertEntry(func() int64 { + return atomic.LoadInt64(cm.ptr) + }) + if err != nil { + return err + } + } + + return nil +} + +// RecordAutocertRenewal records an autocert renewal. +func RecordAutocertRenewal() { + atomic.AddInt64(&autocertRenewalsTotal, 1) +} + +// RecordAutocertCertificates records the next timestamp an autocert certificate will expire. +func RecordAutocertCertificates(certs []tls.Certificate) { + var expiresAt time.Time + for _, cert := range certs { + if len(cert.Certificate) == 0 { + continue + } + + c, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + continue + } + if expiresAt.IsZero() || c.NotAfter.Before(expiresAt) { + expiresAt = c.NotAfter + } + } + if !expiresAt.IsZero() { + atomic.StoreInt64(&autocertCertificateNextExpiresSeconds, expiresAt.Unix()) + } + atomic.StoreInt64(&autocertCertificatesTotal, int64(len(certs))) +} diff --git a/internal/telemetry/metrics/registry.go b/internal/telemetry/metrics/registry.go index ecf19bbac..54f17d524 100644 --- a/internal/telemetry/metrics/registry.go +++ b/internal/telemetry/metrics/registry.go @@ -67,6 +67,11 @@ func (r *metricRegistry) init() { if err != nil { log.Error().Err(err).Msg("telemetry/metrics: failed to register policy count metric") } + + err = registerAutocertMetrics(r.registry) + if err != nil { + log.Error().Err(err).Msg("telemetry/metrics: failed to register autocert metrics") + } }) } diff --git a/pkg/metrics/constants.go b/pkg/metrics/constants.go index 8118d4d25..59633964d 100644 --- a/pkg/metrics/constants.go +++ b/pkg/metrics/constants.go @@ -4,6 +4,9 @@ package metrics // metrics const ( + AutocertRenewalsTotal = "autocert_renewals_total" + AutocertCertificatesTotal = "autocert_certificates_total" + AutocertCertificateNextExpiresSeconds = "autocert_certificate_next_expires_seconds" // ConfigLastReloadTimestampSeconds is unix timestamp when configuration was last reloaded ConfigLastReloadTimestampSeconds = "config_last_reload_success_timestamp" // ConfigLastReloadSuccess is set to 1 if last configuration was successfully reloaded