From c79355fedab23c4343df321cfa4e7cf64274a67c Mon Sep 17 00:00:00 2001 From: Felix Ehrenpfort Date: Wed, 19 Aug 2020 00:55:48 +0200 Subject: [PATCH] prober/tls: adding metric to expose certificate fingerprint info (#678) this change adds a new metric `probe_ssl_fingerprint_info` to both tcp and http probes. the metric always returns 1 similar to the tls version metric and contains the leaf certificates sha256 fingerprint (hex) as a label value. this change allows users to validate in prometheus if a particular certificate is being served. Signed-off-by: xinau --- prober/http.go | 11 ++++++++++- prober/tcp.go | 11 ++++++++++- prober/tcp_test.go | 2 ++ prober/tls.go | 8 ++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/prober/http.go b/prober/http.go index 7fa6c9c..8362c04 100644 --- a/prober/http.go +++ b/prober/http.go @@ -266,6 +266,14 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr Help: "Returns last SSL chain expiry in timestamp seconds", }) + probeSSLLastInformation = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "probe_ssl_last_chain_info", + Help: "Contains SSL leaf certificate information", + }, + []string{"fingerprint_sha256"}, + ) + probeTLSVersion = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: "probe_tls_version_info", @@ -549,10 +557,11 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr if resp.TLS != nil { isSSLGauge.Set(float64(1)) - registry.MustRegister(probeSSLEarliestCertExpiryGauge, probeTLSVersion, probeSSLLastChainExpiryTimestampSeconds) + registry.MustRegister(probeSSLEarliestCertExpiryGauge, probeTLSVersion, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation) probeSSLEarliestCertExpiryGauge.Set(float64(getEarliestCertExpiry(resp.TLS).Unix())) probeTLSVersion.WithLabelValues(getTLSVersion(resp.TLS)).Set(1) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(resp.TLS).Unix())) + probeSSLLastInformation.WithLabelValues(getFingerprint(resp.TLS)).Set(1) if httpConfig.FailIfSSL { level.Error(logger).Log("msg", "Final request was over SSL") success = false diff --git a/prober/tcp.go b/prober/tcp.go index b5f5add..3e8339b 100644 --- a/prober/tcp.go +++ b/prober/tcp.go @@ -98,6 +98,13 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry Name: "probe_ssl_last_chain_expiry_timestamp_seconds", Help: "Returns last SSL chain expiry in unixtime", }) + probeSSLLastInformation := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "probe_ssl_last_chain_info", + Help: "Contains SSL leaf certificate information", + }, + []string{"fingerprint_sha256"}, + ) probeTLSVersion := prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: "probe_tls_version_info", @@ -129,10 +136,11 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry } if module.TCP.TLS { state := conn.(*tls.Conn).ConnectionState() - registry.MustRegister(probeSSLEarliestCertExpiry, probeTLSVersion, probeSSLLastChainExpiryTimestampSeconds) + registry.MustRegister(probeSSLEarliestCertExpiry, probeTLSVersion, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation) probeSSLEarliestCertExpiry.Set(float64(getEarliestCertExpiry(&state).Unix())) probeTLSVersion.WithLabelValues(getTLSVersion(&state)).Set(1) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(&state).Unix())) + probeSSLLastInformation.WithLabelValues(getFingerprint(&state)).Set(1) } scanner := bufio.NewScanner(conn) for i, qr := range module.TCP.QueryResponse { @@ -203,6 +211,7 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry probeSSLEarliestCertExpiry.Set(float64(getEarliestCertExpiry(&state).Unix())) probeTLSVersion.WithLabelValues(getTLSVersion(&state)).Set(1) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(&state).Unix())) + probeSSLLastInformation.WithLabelValues(getFingerprint(&state)).Set(1) } } return true diff --git a/prober/tcp_test.go b/prober/tcp_test.go index d05f317..fd219f9 100644 --- a/prober/tcp_test.go +++ b/prober/tcp_test.go @@ -196,6 +196,7 @@ func TestTCPConnectionWithTLS(t *testing.T) { // Check values expectedResults := map[string]float64{ "probe_ssl_earliest_cert_expiry": float64(certExpiry.Unix()), + "probe_ssl_last_chain_info": 1, "probe_tls_version_info": 1, } checkRegistryResults(expectedResults, mfs, t) @@ -317,6 +318,7 @@ func TestTCPConnectionWithTLSAndVerifiedCertificateChain(t *testing.T) { expectedResults := map[string]float64{ "probe_ssl_earliest_cert_expiry": float64(serverCertExpiry.Unix()), "probe_ssl_last_chain_expiry_timestamp_seconds": float64(serverCertExpiry.Unix()), + "probe_ssl_last_chain_info": 1, "probe_tls_version_info": 1, } checkRegistryResults(expectedResults, mfs, t) diff --git a/prober/tls.go b/prober/tls.go index a4ef0f9..8710797 100644 --- a/prober/tls.go +++ b/prober/tls.go @@ -14,7 +14,9 @@ package prober import ( + "crypto/sha256" "crypto/tls" + "encoding/hex" "time" ) @@ -28,6 +30,12 @@ func getEarliestCertExpiry(state *tls.ConnectionState) time.Time { return earliest } +func getFingerprint(state *tls.ConnectionState) string { + cert := state.PeerCertificates[0] + fingerprint := sha256.Sum256(cert.Raw) + return hex.EncodeToString(fingerprint[:]) +} + func getLastChainExpiry(state *tls.ConnectionState) time.Time { lastChainExpiry := time.Time{} for _, chain := range state.VerifiedChains { -- 2.25.1