mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Monitor truststore certificates in SslMeterBinder
See gh-49641 Signed-off-by: bbbbooo <hyeons1213@gmail.com>
This commit is contained in:
committed by
Moritz Halbritter
parent
c06489019b
commit
f169bf8a48
+4
-1
@@ -815,7 +815,7 @@ To customize the tags, provide a javadoc:org.springframework.context.annotation.
|
||||
=== 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.
|
||||
The metric `ssl.chain.expiry` gauges the expiry date of each certificate chain in key stores and trust stores in seconds.
|
||||
This number will be negative if the chain has already expired.
|
||||
This metric is tagged with the following information:
|
||||
|
||||
@@ -830,6 +830,9 @@ This metric is tagged with the following information:
|
||||
|
||||
| `chain`
|
||||
| The name of the certificate chain.
|
||||
|
||||
| `source`
|
||||
| Whether the certificate chain comes from the key store (`keystore`) or trust store (`truststore`)
|
||||
|===
|
||||
|
||||
|
||||
|
||||
+19
-6
@@ -52,6 +52,12 @@ class SslMeterBinder implements MeterBinder {
|
||||
|
||||
private static final String CHAIN_EXPIRY_METRIC_NAME = "ssl.chain.expiry";
|
||||
|
||||
private static final String SOURCE_TAG_NAME = "source";
|
||||
|
||||
private static final String KEY_STORE_SOURCE_TAG_VALUE = "keystore";
|
||||
|
||||
private static final String TRUST_STORE_SOURCE_TAG_VALUE = "truststore";
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
private final SslInfo sslInfo;
|
||||
@@ -95,16 +101,23 @@ class SslMeterBinder implements MeterBinder {
|
||||
private void createOrUpdateBundleMetrics(MeterRegistry meterRegistry, BundleInfo bundle) {
|
||||
MultiGauge multiGauge = this.bundleMetrics.getGauge(bundle, meterRegistry);
|
||||
List<Row<CertificateInfo>> rows = new ArrayList<>();
|
||||
for (CertificateChainInfo chain : bundle.getCertificateChains()) {
|
||||
Row<CertificateInfo> row = createRowForChain(bundle, chain);
|
||||
addRows(rows, bundle, bundle.getCertificateChains(), KEY_STORE_SOURCE_TAG_VALUE);
|
||||
addRows(rows, bundle, bundle.getTrustStoreCertificateChains(), TRUST_STORE_SOURCE_TAG_VALUE);
|
||||
multiGauge.register(rows, true);
|
||||
}
|
||||
|
||||
private void addRows(List<Row<CertificateInfo>> rows, BundleInfo bundle, List<CertificateChainInfo> chains,
|
||||
String source) {
|
||||
for (CertificateChainInfo chain : chains) {
|
||||
Row<CertificateInfo> row = createRowForChain(bundle, chain, source);
|
||||
if (row != null) {
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
multiGauge.register(rows, true);
|
||||
}
|
||||
|
||||
private @Nullable Row<CertificateInfo> createRowForChain(BundleInfo bundle, CertificateChainInfo chain) {
|
||||
private @Nullable Row<CertificateInfo> createRowForChain(BundleInfo bundle, CertificateChainInfo chain,
|
||||
String source) {
|
||||
CertificateInfo leastValidCertificate = chain.getCertificates()
|
||||
.stream()
|
||||
.filter((c) -> c.getValidityEnds() != null)
|
||||
@@ -114,8 +127,8 @@ class SslMeterBinder implements MeterBinder {
|
||||
return null;
|
||||
}
|
||||
String serialNumber = leastValidCertificate.getSerialNumber();
|
||||
Tags tags = Tags.of("chain", chain.getAlias(), "bundle", bundle.getName(), "certificate",
|
||||
(serialNumber != null) ? serialNumber : "");
|
||||
Tags tags = Tags.of("chain", chain.getAlias(), "bundle", bundle.getName(), SOURCE_TAG_NAME, source,
|
||||
"certificate", (serialNumber != null) ? serialNumber : "");
|
||||
return Row.of(tags, leastValidCertificate, this::getChainExpiry);
|
||||
}
|
||||
|
||||
|
||||
+86
-2
@@ -64,6 +64,44 @@ class SslMeterBinderTests {
|
||||
.hasDays(36889);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterTrustStoreChainExpiryMetrics() {
|
||||
DefaultSslBundleRegistry sslBundleRegistry = new DefaultSslBundleRegistry();
|
||||
sslBundleRegistry.registerBundle("test-0",
|
||||
SslBundle.of(createTrustStoreBundle("classpath:certificates/chains.p12")));
|
||||
MeterRegistry meterRegistry = bindToRegistry(sslBundleRegistry);
|
||||
assertThat(Duration
|
||||
.ofSeconds(findExpiryGauge(meterRegistry, "truststore", "ca", "419224ce190242b2c44069dd3c560192b3b669f3")))
|
||||
.hasDays(1095);
|
||||
assertThat(Duration.ofSeconds(findExpiryGauge(meterRegistry, "truststore", "intermediary",
|
||||
"60f79365fc46bf69149754d377680192b3b6bcf5")))
|
||||
.hasDays(730);
|
||||
assertThat(Duration.ofSeconds(
|
||||
findExpiryGauge(meterRegistry, "truststore", "server", "504c45129526ac050abb11459b1f0192b3b70fe9")))
|
||||
.hasDays(365);
|
||||
assertThat(Duration.ofSeconds(
|
||||
findExpiryGauge(meterRegistry, "truststore", "expired", "562bc5dcf4f26bb179abb13068180192b3bb53dc")))
|
||||
.hasDays(-386);
|
||||
assertThat(Duration.ofSeconds(findExpiryGauge(meterRegistry, "truststore", "not-yet-valid",
|
||||
"7df79335f274e2cfa7467fd5f9ce0192b3bcf4aa")))
|
||||
.hasDays(36889);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDifferentiateKeyStoreAndTrustStoreMetrics() {
|
||||
DefaultSslBundleRegistry sslBundleRegistry = new DefaultSslBundleRegistry();
|
||||
sslBundleRegistry.registerBundle("test-0",
|
||||
SslBundle.of(createKeyAndTrustStoreBundle("classpath:certificates/chains.p12",
|
||||
"classpath:certificates/chains.p12")));
|
||||
MeterRegistry meterRegistry = bindToRegistry(sslBundleRegistry);
|
||||
assertThat(Duration
|
||||
.ofSeconds(findExpiryGauge(meterRegistry, "keystore", "ca", "419224ce190242b2c44069dd3c560192b3b669f3")))
|
||||
.hasDays(1095);
|
||||
assertThat(Duration
|
||||
.ofSeconds(findExpiryGauge(meterRegistry, "truststore", "ca", "419224ce190242b2c44069dd3c560192b3b669f3")))
|
||||
.hasDays(1095);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWatchUpdatesForBundlesRegisteredAfterConstruction() {
|
||||
DefaultSslBundleRegistry sslBundleRegistry = new DefaultSslBundleRegistry();
|
||||
@@ -90,6 +128,35 @@ class SslMeterBinderTests {
|
||||
.hasDays(36889);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWatchTrustStoreUpdatesForBundlesRegisteredAfterConstruction() {
|
||||
DefaultSslBundleRegistry sslBundleRegistry = new DefaultSslBundleRegistry();
|
||||
sslBundleRegistry.registerBundle("dummy",
|
||||
SslBundle.of(createTrustStoreBundle("classpath:certificates/chains2.p12")));
|
||||
MeterRegistry meterRegistry = bindToRegistry(sslBundleRegistry);
|
||||
sslBundleRegistry.registerBundle("test-0",
|
||||
SslBundle.of(createTrustStoreBundle("classpath:certificates/chains2.p12")));
|
||||
sslBundleRegistry.updateBundle("test-0",
|
||||
SslBundle.of(createTrustStoreBundle("classpath:certificates/chains.p12")));
|
||||
assertThat(meterRegistry.find("ssl.chain.expiry").tags("bundle", "test-0", "source", "truststore").meters())
|
||||
.hasSize(5);
|
||||
assertThat(Duration
|
||||
.ofSeconds(findExpiryGauge(meterRegistry, "truststore", "ca", "419224ce190242b2c44069dd3c560192b3b669f3")))
|
||||
.hasDays(1095);
|
||||
assertThat(Duration.ofSeconds(findExpiryGauge(meterRegistry, "truststore", "intermediary",
|
||||
"60f79365fc46bf69149754d377680192b3b6bcf5")))
|
||||
.hasDays(730);
|
||||
assertThat(Duration.ofSeconds(
|
||||
findExpiryGauge(meterRegistry, "truststore", "server", "504c45129526ac050abb11459b1f0192b3b70fe9")))
|
||||
.hasDays(365);
|
||||
assertThat(Duration.ofSeconds(
|
||||
findExpiryGauge(meterRegistry, "truststore", "expired", "562bc5dcf4f26bb179abb13068180192b3bb53dc")))
|
||||
.hasDays(-386);
|
||||
assertThat(Duration.ofSeconds(findExpiryGauge(meterRegistry, "truststore", "not-yet-valid",
|
||||
"7df79335f274e2cfa7467fd5f9ce0192b3bcf4aa")))
|
||||
.hasDays(36889);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterMetricsIfNoBundleExistsAtBindTime() {
|
||||
DefaultSslBundleRegistry sslBundleRegistry = new DefaultSslBundleRegistry();
|
||||
@@ -100,8 +167,14 @@ class SslMeterBinderTests {
|
||||
}
|
||||
|
||||
private long findExpiryGauge(MeterRegistry meterRegistry, String chain, String certificateSerialNumber) {
|
||||
return findExpiryGauge(meterRegistry, "keystore", chain, certificateSerialNumber);
|
||||
}
|
||||
|
||||
private long findExpiryGauge(MeterRegistry meterRegistry, String source, String chain,
|
||||
String certificateSerialNumber) {
|
||||
return (long) meterRegistry.get("ssl.chain.expiry")
|
||||
.tag("bundle", "test-0")
|
||||
.tag("source", source)
|
||||
.tag("chain", chain)
|
||||
.tag("certificate", certificateSerialNumber)
|
||||
.gauge()
|
||||
@@ -117,8 +190,19 @@ class SslMeterBinderTests {
|
||||
}
|
||||
|
||||
private SslStoreBundle createSslStoreBundle(String location) {
|
||||
JksSslStoreDetails keyStoreDetails = JksSslStoreDetails.forLocation(location).withPassword("secret");
|
||||
return new JksSslStoreBundle(keyStoreDetails, null);
|
||||
return new JksSslStoreBundle(createStoreDetails(location), null);
|
||||
}
|
||||
|
||||
private SslStoreBundle createTrustStoreBundle(String location) {
|
||||
return new JksSslStoreBundle(null, createStoreDetails(location));
|
||||
}
|
||||
|
||||
private SslStoreBundle createKeyAndTrustStoreBundle(String keyStoreLocation, String trustStoreLocation) {
|
||||
return new JksSslStoreBundle(createStoreDetails(keyStoreLocation), createStoreDetails(trustStoreLocation));
|
||||
}
|
||||
|
||||
private JksSslStoreDetails createStoreDetails(String location) {
|
||||
return JksSslStoreDetails.forLocation(location).withPassword("secret");
|
||||
}
|
||||
|
||||
private DefaultSslBundleRegistry createSslBundleRegistry(String... locations) {
|
||||
|
||||
Reference in New Issue
Block a user