Add leaf certificate details in probe_tls_certificate_info metric (#943)
authorDaniel Jolly <daniel@danieljolly.com>
Tue, 9 Aug 2022 10:17:33 +0000 (06:17 -0400)
committerGitHub <noreply@github.com>
Tue, 9 Aug 2022 10:17:33 +0000 (15:47 +0530)
Add left certificate's "subject", "issuer", "subjectalternative" labels in `probe_tls_certificate_info` metric.

See relevent discussion in #892

Co-authored-by: Daniel Jolly <code@danieljolly.com>
prober/grpc.go
prober/http.go
prober/tcp.go
prober/tls.go
prober/utils_test.go

index dc616ff22cbe4f18953aa610966cc1e44d0df738..e24f510021f23f674d00798cd8dfd561653bb60e 100644 (file)
@@ -108,6 +108,14 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr
                },
                        []string{"version"},
                )
+
+               probeSSLLastInformation = prometheus.NewGaugeVec(
+                       prometheus.GaugeOpts{
+                               Name: "probe_ssl_last_chain_info",
+                               Help: "Contains SSL leaf certificate information",
+                       },
+                       []string{"fingerprint_sha256", "subject", "issuer", "subjectalternative"},
+               )
        )
 
        for _, lv := range []string{"resolve"} {
@@ -120,6 +128,7 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr
        registry.MustRegister(healthCheckResponseGaugeVec)
        registry.MustRegister(probeSSLEarliestCertExpiryGauge)
        registry.MustRegister(probeTLSVersion)
+       registry.MustRegister(probeSSLLastInformation)
 
        if !strings.HasPrefix(target, "http://") && !strings.HasPrefix(target, "https://") {
                target = "http://" + target
@@ -202,6 +211,7 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr
                        isSSLGauge.Set(float64(1))
                        probeSSLEarliestCertExpiryGauge.Set(float64(getEarliestCertExpiry(&tlsInfo.State).Unix()))
                        probeTLSVersion.WithLabelValues(getTLSVersion(&tlsInfo.State)).Set(1)
+                       probeSSLLastInformation.WithLabelValues(getFingerprint(&tlsInfo.State), getSubject(&tlsInfo.State), getIssuer(&tlsInfo.State), getDNSNames(&tlsInfo.State)).Set(1)
                } else {
                        isSSLGauge.Set(float64(0))
                }
index 82ef7343bfaf04b43d61d885a81c918cc2a0120b..ec54bd78c7dad6b2bf5393c8be774414fcf5e20f 100644 (file)
@@ -281,7 +281,7 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr
                                Name: "probe_ssl_last_chain_info",
                                Help: "Contains SSL leaf certificate information",
                        },
-                       []string{"fingerprint_sha256"},
+                       []string{"fingerprint_sha256", "subject", "issuer", "subjectalternative"},
                )
 
                probeTLSVersion = prometheus.NewGaugeVec(
@@ -651,7 +651,7 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr
                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)
+               probeSSLLastInformation.WithLabelValues(getFingerprint(resp.TLS), getSubject(resp.TLS), getIssuer(resp.TLS), getDNSNames(resp.TLS)).Set(1)
                if httpConfig.FailIfSSL {
                        level.Error(logger).Log("msg", "Final request was over SSL")
                        success = false
index 1923cdb596dce1a4990558f6772f9b608fd1fa20..8f2db40d0627a8881fad0b633795aa08e2fafae8 100644 (file)
@@ -102,7 +102,7 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry
                        Name: "probe_ssl_last_chain_info",
                        Help: "Contains SSL leaf certificate information",
                },
-               []string{"fingerprint_sha256"},
+               []string{"fingerprint_sha256", "subject", "issuer", "subjectalternative"},
        )
        probeTLSVersion := prometheus.NewGaugeVec(
                prometheus.GaugeOpts{
@@ -139,7 +139,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)
+               probeSSLLastInformation.WithLabelValues(getFingerprint(&state), getSubject(&state), getIssuer(&state), getDNSNames(&state)).Set(1)
        }
        scanner := bufio.NewScanner(conn)
        for i, qr := range module.TCP.QueryResponse {
@@ -205,7 +205,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)
+                       probeSSLLastInformation.WithLabelValues(getFingerprint(&state), getSubject(&state), getIssuer(&state), getDNSNames(&state)).Set(1)
                }
        }
        return true
index 38d5533611f7b7dc3cad35657bca27dcb16838d1..7df8e5758fc29e5b474a814ab6de8089eedc1236 100644 (file)
@@ -17,6 +17,7 @@ import (
        "crypto/sha256"
        "crypto/tls"
        "encoding/hex"
+       "strings"
        "time"
 )
 
@@ -36,6 +37,21 @@ func getFingerprint(state *tls.ConnectionState) string {
        return hex.EncodeToString(fingerprint[:])
 }
 
+func getSubject(state *tls.ConnectionState) string {
+       cert := state.PeerCertificates[0]
+       return cert.Subject.String()
+}
+
+func getIssuer(state *tls.ConnectionState) string {
+       cert := state.PeerCertificates[0]
+       return cert.Issuer.String()
+}
+
+func getDNSNames(state *tls.ConnectionState) string {
+       cert := state.PeerCertificates[0]
+       return strings.Join(cert.DNSNames, ",")
+}
+
 func getLastChainExpiry(state *tls.ConnectionState) time.Time {
        lastChainExpiry := time.Time{}
        for _, chain := range state.VerifiedChains {
index def607a0351edc0ceef6e1075d493d715388168f..1b1c41dcbbef0a32190d19235a0078a71732ae30 100644 (file)
@@ -85,6 +85,7 @@ func generateCertificateTemplate(expiry time.Time, IPAddressSAN bool) *x509.Cert
                SubjectKeyId:          []byte{1},
                SerialNumber:          big.NewInt(1),
                Subject: pkix.Name{
+                       CommonName:   "Example",
                        Organization: []string{"Example Org"},
                },
                NotBefore:   time.Now(),