From 93a48d8ed8e4f5578ebb39f14b7cc16e9b7cdbf8 Mon Sep 17 00:00:00 2001 From: Silke Hofstra Date: Mon, 14 Oct 2019 10:27:37 +0200 Subject: [PATCH] Export TLS version (#538) * Add a metric for the TLS version used Signed-off-by: Silke Hofstra --- prober/http.go | 11 ++++++++++- prober/tcp.go | 11 ++++++++++- prober/tcp_test.go | 15 ++++++++++++++- prober/tls.go | 15 +++++++++++++++ prober/utils_test.go | 29 +++++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/prober/http.go b/prober/http.go index bd9722d..f4abdbe 100644 --- a/prober/http.go +++ b/prober/http.go @@ -261,6 +261,14 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr Help: "Returns earliest SSL cert expiry in unixtime", }) + probeTLSVersion = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "probe_tls_version_info", + Help: "Contains the TLS version used", + }, + []string{"version"}, + ) + probeHTTPVersionGauge = prometheus.NewGauge(prometheus.GaugeOpts{ Name: "probe_http_version", Help: "Returns the version of HTTP of the probe response", @@ -538,8 +546,9 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr if resp.TLS != nil { isSSLGauge.Set(float64(1)) - registry.MustRegister(probeSSLEarliestCertExpiryGauge) + registry.MustRegister(probeSSLEarliestCertExpiryGauge, probeTLSVersion) probeSSLEarliestCertExpiryGauge.Set(float64(getEarliestCertExpiry(resp.TLS).Unix())) + probeTLSVersion.WithLabelValues(getTLSVersion(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 e497625..a450fd2 100644 --- a/prober/tcp.go +++ b/prober/tcp.go @@ -94,6 +94,13 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry Name: "probe_ssl_earliest_cert_expiry", Help: "Returns earliest SSL cert expiry date", }) + probeTLSVersion := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "probe_tls_version_info", + Help: "Returns the TLS version used, or NaN when unknown", + }, + []string{"version"}, + ) probeFailedDueToRegex := prometheus.NewGauge(prometheus.GaugeOpts{ Name: "probe_failed_due_to_regex", Help: "Indicates if probe failed due to regex", @@ -118,8 +125,9 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry } if module.TCP.TLS { state := conn.(*tls.Conn).ConnectionState() - registry.MustRegister(probeSSLEarliestCertExpiry) + registry.MustRegister(probeSSLEarliestCertExpiry, probeTLSVersion) probeSSLEarliestCertExpiry.Set(float64(getEarliestCertExpiry(&state).Unix())) + probeTLSVersion.WithLabelValues(getTLSVersion(&state)).Set(1) } scanner := bufio.NewScanner(conn) for i, qr := range module.TCP.QueryResponse { @@ -188,6 +196,7 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry state := tlsConn.ConnectionState() registry.MustRegister(probeSSLEarliestCertExpiry) probeSSLEarliestCertExpiry.Set(float64(getEarliestCertExpiry(&state).Unix())) + probeTLSVersion.WithLabelValues(getTLSVersion(&state)).Set(1) } } return true diff --git a/prober/tcp_test.go b/prober/tcp_test.go index 8201300..e2fcbd4 100644 --- a/prober/tcp_test.go +++ b/prober/tcp_test.go @@ -118,6 +118,8 @@ func TestTCPConnectionWithTLS(t *testing.T) { tlsConfig := &tls.Config{ ServerName: "localhost", Certificates: []tls.Certificate{testcert}, + MinVersion: tls.VersionTLS12, + MaxVersion: tls.VersionTLS12, } tlsConn := tls.Server(conn, tlsConfig) defer tlsConn.Close() @@ -168,13 +170,24 @@ func TestTCPConnectionWithTLS(t *testing.T) { } <-ch - // Check the probe_ssl_earliest_cert_expiry. + // Check the resulting metrics. mfs, err := registry.Gather() if err != nil { t.Fatal(err) } + + // Check labels + expectedLabels := map[string]map[string]string{ + "probe_tls_version_info": { + "version": "TLS 1.2", + }, + } + checkRegistryLabels(expectedLabels, mfs, t) + + // Check values expectedResults := map[string]float64{ "probe_ssl_earliest_cert_expiry": float64(certExpiry.Unix()), + "probe_tls_version_info": 1, } checkRegistryResults(expectedResults, mfs, t) } diff --git a/prober/tls.go b/prober/tls.go index ea21dd1..758cbf3 100644 --- a/prober/tls.go +++ b/prober/tls.go @@ -27,3 +27,18 @@ func getEarliestCertExpiry(state *tls.ConnectionState) time.Time { } return earliest } + +func getTLSVersion(state *tls.ConnectionState) string { + switch state.Version { + case tls.VersionTLS10: + return "TLS 1.0" + case tls.VersionTLS11: + return "TLS 1.1" + case tls.VersionTLS12: + return "TLS 1.2" + case tls.VersionTLS13: + return "TLS 1.3" + default: + return "unknown" + } +} diff --git a/prober/utils_test.go b/prober/utils_test.go index 38b95ee..326da5d 100644 --- a/prober/utils_test.go +++ b/prober/utils_test.go @@ -45,6 +45,35 @@ func checkRegistryResults(expRes map[string]float64, mfs []*dto.MetricFamily, t } } +// Check if expected labels are in the registry +func checkRegistryLabels(expRes map[string]map[string]string, mfs []*dto.MetricFamily, t *testing.T) { + results := make(map[string]map[string]string) + for _, mf := range mfs { + result := make(map[string]string) + for _, metric := range mf.Metric { + for _, l := range metric.GetLabel() { + result[l.GetName()] = l.GetValue() + } + } + results[mf.GetName()] = result + } + + for metric, labelValues := range expRes { + if _, ok := results[metric]; !ok { + t.Fatalf("Expected metric %v not found in returned metrics", metric) + } + for name, exp := range labelValues { + val, ok := results[metric][name] + if !ok { + t.Fatalf("Expected label %v for metric %v not found in returned metrics", val, name) + } + if val != exp { + t.Fatalf("Expected: %v{%q=%q}, got: %v{%q=%q}", metric, name, exp, metric, name, val) + } + } + } +} + // Create test certificate with specified expiry date // Certificate will be self-signed and use localhost/127.0.0.1 // Generated certificate and key are returned in PEM encoding -- 2.25.1