From ab162fa0bb802b111004c438a4b50f73de617cae Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 20 May 2025 17:17:53 -0700 Subject: [PATCH] Remove SSL status tag from metrics Update `SslMeterBinder` to the status tag is no longer included in the reported metrics. Instead the expiry date should be used directly by the monitoring system. Closes gh-45602 --- .../autoconfigure/ssl/SslMeterBinder.java | 65 +------------------ .../ssl/SslMeterBinderTests.java | 9 --- .../reference/pages/actuator/metrics.adoc | 8 +-- 3 files changed, 4 insertions(+), 78 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/ssl/SslMeterBinder.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/ssl/SslMeterBinder.java index 9e9c93a2cf6..68f4f3a2e07 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/ssl/SslMeterBinder.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/ssl/SslMeterBinder.java @@ -23,14 +23,12 @@ import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; -import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.MultiGauge; import io.micrometer.core.instrument.MultiGauge.Row; @@ -42,21 +40,16 @@ import org.springframework.boot.info.SslInfo; import org.springframework.boot.info.SslInfo.BundleInfo; import org.springframework.boot.info.SslInfo.CertificateChainInfo; import org.springframework.boot.info.SslInfo.CertificateInfo; -import org.springframework.boot.info.SslInfo.CertificateValidityInfo; -import org.springframework.boot.info.SslInfo.CertificateValidityInfo.Status; import org.springframework.boot.ssl.SslBundles; /** - * {@link MeterBinder} which registers the SSL chain validity (soonest to expire - * certificate in the chain) as a {@link TimeGauge}. Also contributes two {@link Gauge - * gauges} to count the valid and invalid chains. + * {@link MeterBinder} which registers the SSL chain expiry (soonest to expire certificate + * in the chain) as a {@link TimeGauge}. * * @author Moritz Halbritter */ class SslMeterBinder implements MeterBinder { - private static final String CHAINS_METRIC_NAME = "ssl.chains"; - private static final String CHAIN_EXPIRY_METRIC_NAME = "ssl.chain.expiry"; private final Clock clock; @@ -91,18 +84,6 @@ class SslMeterBinder implements MeterBinder { for (BundleInfo bundle : this.sslInfo.getBundles()) { createOrUpdateBundleMetrics(meterRegistry, bundle); } - Gauge.builder(CHAINS_METRIC_NAME, () -> countChainsByStatus(Status.VALID)) - .tag("status", "valid") - .register(meterRegistry); - Gauge.builder(CHAINS_METRIC_NAME, () -> countChainsByStatus(Status.EXPIRED)) - .tag("status", "expired") - .register(meterRegistry); - Gauge.builder(CHAINS_METRIC_NAME, () -> countChainsByStatus(Status.NOT_YET_VALID)) - .tag("status", "not-yet-valid") - .register(meterRegistry); - Gauge.builder(CHAINS_METRIC_NAME, () -> countChainsByStatus(Status.WILL_EXPIRE_SOON)) - .tag("status", "will-expire-soon") - .register(meterRegistry); } private void createOrUpdateBundleMetrics(MeterRegistry meterRegistry, BundleInfo bundle) { @@ -130,36 +111,6 @@ class SslMeterBinder implements MeterBinder { return Row.of(tags, leastValidCertificate, this::getChainExpiry); } - private long countChainsByStatus(Status status) { - long count = 0; - for (BundleInfo bundle : this.bundleMetrics.getBundles()) { - for (CertificateChainInfo chain : bundle.getCertificateChains()) { - if (getChainStatus(chain) == status) { - count++; - } - } - } - return count; - } - - private Status getChainStatus(CertificateChainInfo chain) { - EnumSet statuses = EnumSet.noneOf(Status.class); - for (CertificateInfo certificate : chain.getCertificates()) { - CertificateValidityInfo validity = certificate.getValidity(); - statuses.add(validity.getStatus()); - } - if (statuses.contains(Status.EXPIRED)) { - return Status.EXPIRED; - } - if (statuses.contains(Status.NOT_YET_VALID)) { - return Status.NOT_YET_VALID; - } - if (statuses.contains(Status.WILL_EXPIRE_SOON)) { - return Status.WILL_EXPIRE_SOON; - } - return statuses.isEmpty() ? null : Status.VALID; - } - private long getChainExpiry(CertificateInfo certificate) { Duration valid = Duration.between(Instant.now(this.clock), certificate.getValidityEnds()); return valid.get(ChronoUnit.SECONDS); @@ -184,18 +135,6 @@ class SslMeterBinder implements MeterBinder { return gauges.getGauge(meterRegistry); } - /** - * Returns all bundles. - * @return all bundles - */ - Collection getBundles() { - List result = new ArrayList<>(); - for (Gauges metrics : this.gauges.values()) { - result.add(metrics.bundle()); - } - return result; - } - /** * Returns all meter registries. * @return all meter registries diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/ssl/SslMeterBinderTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/ssl/SslMeterBinderTests.java index 0d3d0057bcf..b9a6f44459d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/ssl/SslMeterBinderTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/ssl/SslMeterBinderTests.java @@ -44,15 +44,6 @@ class SslMeterBinderTests { private static final Clock CLOCK = Clock.fixed(Instant.parse("2024-10-21T13:51:40Z"), ZoneId.of("UTC")); - @Test - void shouldRegisterChainMetrics() { - MeterRegistry meterRegistry = bindToRegistry(); - assertThat(meterRegistry.get("ssl.chains").tag("status", "valid").gauge().value()).isEqualTo(3.0); - assertThat(meterRegistry.get("ssl.chains").tag("status", "expired").gauge().value()).isEqualTo(1.0); - assertThat(meterRegistry.get("ssl.chains").tag("status", "not-yet-valid").gauge().value()).isEqualTo(1.0); - assertThat(meterRegistry.get("ssl.chains").tag("status", "will-expire-soon").gauge().value()).isEqualTo(0.0); - } - @Test void shouldRegisterChainExpiryMetrics() { MeterRegistry meterRegistry = bindToRegistry(); diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/metrics.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/metrics.adoc index 8c8619a6561..ca9cbfd65c3 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/metrics.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/metrics.adoc @@ -836,13 +836,9 @@ To customize the tags, provide a javadoc:org.springframework.context.annotation. [[actuator.metrics.supported.ssl]] -=== SSL bundle metrics - -Spring Boot Actuator publishes two metrics about SSL bundles: - -The metric `ssl.chains` gauges how many certificate chains have been registered. -The `status` tag can be used to differentiate between valid, not-yet-valid, expired and soon-to-be-expired certificates. +=== SSL Bundle Metrics +Spring Boot Actuator publishes expiry metrics about SSL bundles. The metric `ssl.chain.expiry` gauges the expiry date of each certificate chain in seconds. This number will be negative if the chain has already expired. This metric is tagged with the following information: