diff --git a/prober/grpc.go b/prober/grpc.go index e7a5921e..9e4863b0 100644 --- a/prober/grpc.go +++ b/prober/grpc.go @@ -111,6 +111,14 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr }, []string{"fingerprint_sha256", "subject", "issuer", "subjectalternative"}, ) + + probeSSLLastKeyBits = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "probe_ssl_last_chain_key_bits", + Help: "Contains SSL leaf key information and size in bits", + }, + []string{"type", "fingerprint_sha256"}, + ) ) for _, lv := range []string{"resolve"} { @@ -124,6 +132,7 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr registry.MustRegister(probeSSLEarliestCertExpiryGauge) registry.MustRegister(probeTLSVersion) registry.MustRegister(probeSSLLastInformation) + registry.MustRegister(probeSSLLastKeyBits) if !strings.HasPrefix(target, "http://") && !strings.HasPrefix(target, "https://") { target = "http://" + target @@ -207,6 +216,8 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr 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) + keyType, keySize := getTLSKeyTypeAndSize(&tlsInfo.State) + probeSSLLastKeyBits.WithLabelValues(keyType, getTLSKeyFingerprint(&tlsInfo.State)).Set(float64(keySize)) } else { isSSLGauge.Set(float64(0)) } diff --git a/prober/http.go b/prober/http.go index d79e8e1c..a30e8a71 100644 --- a/prober/http.go +++ b/prober/http.go @@ -277,6 +277,14 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr []string{"fingerprint_sha256", "subject", "issuer", "subjectalternative"}, ) + probeSSLLastKeyBits = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "probe_ssl_last_chain_key_bits", + Help: "Contains SSL leaf key information and size in bits", + }, + []string{"type", "fingerprint_sha256"}, + ) + probeTLSVersion = prometheus.NewGaugeVec( probeTLSInfoGaugeOpts, []string{"version"}, @@ -643,12 +651,14 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr if resp.TLS != nil { isSSLGauge.Set(float64(1)) - registry.MustRegister(probeSSLEarliestCertExpiryGauge, probeTLSVersion, probeTLSCipher, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation) + registry.MustRegister(probeSSLEarliestCertExpiryGauge, probeTLSVersion, probeTLSCipher, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation, probeSSLLastKeyBits) probeSSLEarliestCertExpiryGauge.Set(float64(getEarliestCertExpiry(resp.TLS).Unix())) probeTLSVersion.WithLabelValues(getTLSVersion(resp.TLS)).Set(1) probeTLSCipher.WithLabelValues(getTLSCipher(resp.TLS)).Set(1) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(resp.TLS).Unix())) probeSSLLastInformation.WithLabelValues(getFingerprint(resp.TLS), getSubject(resp.TLS), getIssuer(resp.TLS), getDNSNames(resp.TLS)).Set(1) + keyType, keySize := getTLSKeyTypeAndSize(resp.TLS) + probeSSLLastKeyBits.WithLabelValues(keyType, getTLSKeyFingerprint(resp.TLS)).Set(float64(keySize)) 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 de960db2..51b337e5 100644 --- a/prober/tcp.go +++ b/prober/tcp.go @@ -98,6 +98,15 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry }, []string{"fingerprint_sha256", "subject", "issuer", "subjectalternative"}, ) + + probeSSLLastKeyBits := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "probe_ssl_last_chain_key_bits", + Help: "Contains SSL leaf certificate information", + }, + []string{"type", "fingerprint_sha256"}, + ) + probeTLSVersion := prometheus.NewGaugeVec( probeTLSInfoGaugeOpts, []string{"version"}, @@ -126,11 +135,13 @@ 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, probeSSLLastInformation) + registry.MustRegister(probeSSLEarliestCertExpiry, probeTLSVersion, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation, probeSSLLastKeyBits) probeSSLEarliestCertExpiry.Set(float64(getEarliestCertExpiry(&state).Unix())) probeTLSVersion.WithLabelValues(getTLSVersion(&state)).Set(1) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(&state).Unix())) probeSSLLastInformation.WithLabelValues(getFingerprint(&state), getSubject(&state), getIssuer(&state), getDNSNames(&state)).Set(1) + keyType, keySize := getTLSKeyTypeAndSize(&state) + probeSSLLastKeyBits.WithLabelValues(keyType, getTLSKeyFingerprint(&state)).Set(float64(keySize)) } scanner := bufio.NewScanner(conn) for i, qr := range module.TCP.QueryResponse { @@ -192,11 +203,13 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry // Get certificate expiry. state := tlsConn.ConnectionState() - registry.MustRegister(probeSSLEarliestCertExpiry, probeTLSVersion, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation) + registry.MustRegister(probeSSLEarliestCertExpiry, probeTLSVersion, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation, probeSSLLastKeyBits) probeSSLEarliestCertExpiry.Set(float64(getEarliestCertExpiry(&state).Unix())) probeTLSVersion.WithLabelValues(getTLSVersion(&state)).Set(1) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(&state).Unix())) probeSSLLastInformation.WithLabelValues(getFingerprint(&state), getSubject(&state), getIssuer(&state), getDNSNames(&state)).Set(1) + keyType, keySize := getTLSKeyTypeAndSize(&state) + probeSSLLastKeyBits.WithLabelValues(keyType, getTLSKeyFingerprint(&state)).Set(float64(keySize)) } } return true diff --git a/prober/tls.go b/prober/tls.go index 3da17a05..b99d998c 100644 --- a/prober/tls.go +++ b/prober/tls.go @@ -16,6 +16,8 @@ package prober import ( "crypto/sha256" "crypto/tls" + "crypto/rsa" + "crypto/ecdsa" "encoding/hex" "strings" "time" @@ -87,3 +89,20 @@ func getTLSVersion(state *tls.ConnectionState) string { func getTLSCipher(state *tls.ConnectionState) string { return tls.CipherSuiteName(state.CipherSuite) } + +func getTLSKeyTypeAndSize(state *tls.ConnectionState) (string, int) { + cert := state.PeerCertificates[0] + if key, ok := cert.PublicKey.(*ecdsa.PublicKey); ok { + return "ec", key.Curve.Params().BitSize + } + if key, ok := cert.PublicKey.(*rsa.PublicKey); ok { + return "rsa", key.N.BitLen() + } + return "", 0 +} + +func getTLSKeyFingerprint(state *tls.ConnectionState) string { + cert := state.PeerCertificates[0] + fingerprint := sha256.Sum256(cert.RawSubjectPublicKeyInfo) + return hex.EncodeToString(fingerprint[:]) +} \ No newline at end of file