diff --git a/prober/grpc.go b/prober/grpc.go index a73b6d19..fbe29da9 100644 --- a/prober/grpc.go +++ b/prober/grpc.go @@ -97,6 +97,8 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr Help: "Response HealthCheck response", }, []string{"serving_status"}) + probeSSLEarliestCertStartGauge = prometheus.NewGauge(sslEarliestCertStartGaugeOpts) + probeSSLEarliestCertExpiryGauge = prometheus.NewGauge(sslEarliestCertExpiryGaugeOpts) probeTLSVersion = prometheus.NewGaugeVec( @@ -200,8 +202,9 @@ func ProbeGRPC(ctx context.Context, target string, module config.Module, registr if serverPeer != nil { tlsInfo, tlsOk := serverPeer.AuthInfo.(credentials.TLSInfo) if tlsOk { - registry.MustRegister(probeSSLEarliestCertExpiryGauge, probeTLSVersion, probeSSLLastInformation) + registry.MustRegister(probeSSLEarliestCertStartGauge, probeSSLEarliestCertExpiryGauge, probeTLSVersion, probeSSLLastInformation) isSSLGauge.Set(float64(1)) + probeSSLEarliestCertStartGauge.Set(float64(getEarliestCertStart(&tlsInfo.State).Unix())) 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), getSerialNumber(&tlsInfo.State)).Set(1) diff --git a/prober/http.go b/prober/http.go index 9aa89398..3f42ae37 100644 --- a/prober/http.go +++ b/prober/http.go @@ -318,8 +318,12 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr Help: "Response HTTP status code", }) + probeSSLEarliestCertStartGauge = prometheus.NewGauge(sslEarliestCertStartGaugeOpts) + probeSSLEarliestCertExpiryGauge = prometheus.NewGauge(sslEarliestCertExpiryGaugeOpts) + probeSSLLastChainStartTimestampSeconds = prometheus.NewGauge(sslChainStartInTimeStampGaugeOpts) + probeSSLLastChainExpiryTimestampSeconds = prometheus.NewGauge(sslChainExpiryInTimeStampGaugeOpts) probeSSLLastInformation = prometheus.NewGaugeVec( @@ -714,10 +718,12 @@ 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(probeSSLEarliestCertStartGauge, probeSSLEarliestCertExpiryGauge, probeTLSVersion, probeTLSCipher, probeSSLLastChainStartTimestampSeconds, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation) + probeSSLEarliestCertStartGauge.Set(float64(getEarliestCertStart(resp.TLS).Unix())) probeSSLEarliestCertExpiryGauge.Set(float64(getEarliestCertExpiry(resp.TLS).Unix())) probeTLSVersion.WithLabelValues(getTLSVersion(resp.TLS)).Set(1) probeTLSCipher.WithLabelValues(getTLSCipher(resp.TLS)).Set(1) + probeSSLLastChainStartTimestampSeconds.Set(float64(getLastChainStart(resp.TLS).Unix())) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(resp.TLS).Unix())) probeSSLLastInformation.WithLabelValues(getFingerprint(resp.TLS), getSubject(resp.TLS), getIssuer(resp.TLS), getDNSNames(resp.TLS), getSerialNumber(resp.TLS)).Set(1) if httpConfig.FailIfSSL { diff --git a/prober/prober.go b/prober/prober.go index 5988c032..201623a9 100644 --- a/prober/prober.go +++ b/prober/prober.go @@ -25,18 +25,30 @@ import ( type ProbeFn func(ctx context.Context, target string, config config.Module, registry *prometheus.Registry, logger *slog.Logger) bool const ( - helpSSLEarliestCertExpiry = "Returns last SSL chain expiry in unixtime" + helpSSLEarliestCertStart = "Returns earliest SSL cert start in unixtime" + helpSSLEarliestCertExpiry = "Returns earliest SSL cert expiry in unixtime" + helpSSLChainStartInTimeStamp = "Returns last SSL chain start in timestamp" helpSSLChainExpiryInTimeStamp = "Returns last SSL chain expiry in timestamp" helpProbeTLSInfo = "Returns the TLS version used or NaN when unknown" helpProbeTLSCipher = "Returns the TLS cipher negotiated during handshake" ) var ( + sslEarliestCertStartGaugeOpts = prometheus.GaugeOpts{ + Name: "probe_ssl_earliest_cert_start", + Help: helpSSLEarliestCertStart, + } + sslEarliestCertExpiryGaugeOpts = prometheus.GaugeOpts{ Name: "probe_ssl_earliest_cert_expiry", Help: helpSSLEarliestCertExpiry, } + sslChainStartInTimeStampGaugeOpts = prometheus.GaugeOpts{ + Name: "probe_ssl_last_chain_start_timestamp_seconds", + Help: helpSSLChainStartInTimeStamp, + } + sslChainExpiryInTimeStampGaugeOpts = prometheus.GaugeOpts{ Name: "probe_ssl_last_chain_expiry_timestamp_seconds", Help: helpSSLChainExpiryInTimeStamp, diff --git a/prober/tcp.go b/prober/tcp.go index b10c0b5d..022c2811 100644 --- a/prober/tcp.go +++ b/prober/tcp.go @@ -106,7 +106,9 @@ func probeExpectInfo(registry *prometheus.Registry, qr *config.QueryResponse, by } func ProbeTCP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger *slog.Logger) bool { + probeSSLEarliestCertStart := prometheus.NewGauge(sslEarliestCertStartGaugeOpts) probeSSLEarliestCertExpiry := prometheus.NewGauge(sslEarliestCertExpiryGaugeOpts) + probeSSLLastChainStartTimestampSeconds := prometheus.NewGauge(sslChainStartInTimeStampGaugeOpts) probeSSLLastChainExpiryTimestampSeconds := prometheus.NewGauge(sslChainExpiryInTimeStampGaugeOpts) probeSSLLastInformation := prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -143,9 +145,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, probeSSLLastInformation) + registry.MustRegister(probeSSLEarliestCertStart, probeSSLEarliestCertExpiry, probeTLSVersion, probeSSLLastChainStartTimestampSeconds, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation) + probeSSLEarliestCertStart.Set(float64(getEarliestCertStart(&state).Unix())) probeSSLEarliestCertExpiry.Set(float64(getEarliestCertExpiry(&state).Unix())) probeTLSVersion.WithLabelValues(getTLSVersion(&state)).Set(1) + probeSSLLastChainStartTimestampSeconds.Set(float64(getLastChainStart(&state).Unix())) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(&state).Unix())) probeSSLLastInformation.WithLabelValues(getFingerprint(&state), getSubject(&state), getIssuer(&state), getDNSNames(&state), getSerialNumber(&state)).Set(1) } @@ -210,11 +214,13 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry conn = net.Conn(tlsConn) scanner = bufio.NewScanner(conn) - // Get certificate expiry. + // Get certificate start and expiry. state := tlsConn.ConnectionState() - registry.MustRegister(probeSSLEarliestCertExpiry, probeTLSVersion, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation) + registry.MustRegister(probeSSLEarliestCertStart, probeSSLEarliestCertExpiry, probeTLSVersion, probeSSLLastChainStartTimestampSeconds, probeSSLLastChainExpiryTimestampSeconds, probeSSLLastInformation) + probeSSLEarliestCertStart.Set(float64(getEarliestCertStart(&state).Unix())) probeSSLEarliestCertExpiry.Set(float64(getEarliestCertExpiry(&state).Unix())) probeTLSVersion.WithLabelValues(getTLSVersion(&state)).Set(1) + probeSSLLastChainStartTimestampSeconds.Set(float64(getLastChainStart(&state).Unix())) probeSSLLastChainExpiryTimestampSeconds.Set(float64(getLastChainExpiry(&state).Unix())) probeSSLLastInformation.WithLabelValues(getFingerprint(&state), getSubject(&state), getIssuer(&state), getDNSNames(&state), getSerialNumber(&state)).Set(1) } diff --git a/prober/tls.go b/prober/tls.go index 0589036e..84339cce 100644 --- a/prober/tls.go +++ b/prober/tls.go @@ -22,14 +22,24 @@ import ( "time" ) +func getEarliestCertStart(state *tls.ConnectionState) time.Time { + earliestStart := time.Time{} + for _, cert := range state.PeerCertificates { + if (earliestStart.IsZero() || cert.NotBefore.Before(earliestStart)) && !cert.NotBefore.IsZero() { + earliestStart = cert.NotBefore + } + } + return earliestStart +} + func getEarliestCertExpiry(state *tls.ConnectionState) time.Time { - earliest := time.Time{} + earliestExpiry := time.Time{} for _, cert := range state.PeerCertificates { - if (earliest.IsZero() || cert.NotAfter.Before(earliest)) && !cert.NotAfter.IsZero() { - earliest = cert.NotAfter + if (earliestExpiry.IsZero() || cert.NotAfter.Before(earliestExpiry)) && !cert.NotAfter.IsZero() { + earliestExpiry = cert.NotAfter } } - return earliest + return earliestExpiry } func getFingerprint(state *tls.ConnectionState) string { @@ -53,6 +63,23 @@ func getDNSNames(state *tls.ConnectionState) string { return strings.Join(cert.DNSNames, ",") } +func getLastChainStart(state *tls.ConnectionState) time.Time { + lastChainStart := time.Time{} + for _, chain := range state.VerifiedChains { + earliestCertStart := time.Time{} + for _, cert := range chain { + if (earliestCertStart.IsZero() || cert.NotBefore.After(earliestCertStart)) && !cert.NotAfter.IsZero() { + earliestCertStart = cert.NotBefore + } + } + if lastChainStart.IsZero() || lastChainStart.After(earliestCertStart) { + lastChainStart = earliestCertStart + } + + } + return lastChainStart +} + func getLastChainExpiry(state *tls.ConnectionState) time.Time { lastChainExpiry := time.Time{} for _, chain := range state.VerifiedChains {