Skip to content

Commit fbe0c5d

Browse files
Add SslInfoContributor and SslHealthIndicator
1 parent b6d648e commit fbe0c5d

File tree

24 files changed

+1330
-9
lines changed

24 files changed

+1330
-9
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfiguration.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.info;
1818

19+
import org.springframework.boot.actuate.autoconfigure.ssl.SslHealthIndicatorProperties;
1920
import org.springframework.boot.actuate.info.BuildInfoContributor;
2021
import org.springframework.boot.actuate.info.EnvironmentInfoContributor;
2122
import org.springframework.boot.actuate.info.GitInfoContributor;
2223
import org.springframework.boot.actuate.info.InfoContributor;
2324
import org.springframework.boot.actuate.info.JavaInfoContributor;
2425
import org.springframework.boot.actuate.info.OsInfoContributor;
2526
import org.springframework.boot.actuate.info.ProcessInfoContributor;
27+
import org.springframework.boot.actuate.info.SslInfoContributor;
2628
import org.springframework.boot.autoconfigure.AutoConfiguration;
2729
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2830
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -31,6 +33,8 @@
3133
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3234
import org.springframework.boot.info.BuildProperties;
3335
import org.springframework.boot.info.GitProperties;
36+
import org.springframework.boot.info.SslInfo;
37+
import org.springframework.boot.ssl.SslBundles;
3438
import org.springframework.context.annotation.Bean;
3539
import org.springframework.core.Ordered;
3640
import org.springframework.core.annotation.Order;
@@ -46,7 +50,7 @@
4650
* @since 2.0.0
4751
*/
4852
@AutoConfiguration(after = ProjectInfoAutoConfiguration.class)
49-
@EnableConfigurationProperties(InfoContributorProperties.class)
53+
@EnableConfigurationProperties({ InfoContributorProperties.class, SslHealthIndicatorProperties.class })
5054
public class InfoContributorAutoConfiguration {
5155

5256
/**
@@ -100,4 +104,18 @@ public ProcessInfoContributor processInfoContributor() {
100104
return new ProcessInfoContributor();
101105
}
102106

107+
@Bean
108+
@ConditionalOnEnabledInfoContributor(value = "ssl", fallback = InfoContributorFallback.DISABLE)
109+
@Order(DEFAULT_ORDER)
110+
public SslInfoContributor sslInfoContributor(SslInfo sslInfo) {
111+
return new SslInfoContributor(sslInfo);
112+
}
113+
114+
@Bean
115+
@ConditionalOnMissingBean
116+
@ConditionalOnEnabledInfoContributor(value = "ssl", fallback = InfoContributorFallback.DISABLE)
117+
public SslInfo sslInfo(SslBundles sslBundles, SslHealthIndicatorProperties sslHealthIndicatorProperties) {
118+
return new SslInfo(sslBundles, sslHealthIndicatorProperties.getCertificateValidityWarningThreshold());
119+
}
120+
103121
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.ssl;
18+
19+
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
20+
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
21+
import org.springframework.boot.actuate.ssl.SslHealthIndicator;
22+
import org.springframework.boot.autoconfigure.AutoConfiguration;
23+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
24+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
25+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
26+
import org.springframework.boot.info.SslInfo;
27+
import org.springframework.boot.ssl.SslBundles;
28+
import org.springframework.context.annotation.Bean;
29+
30+
/**
31+
* {@link EnableAutoConfiguration Auto-configuration} for {@link SslHealthIndicator}.
32+
*
33+
* @author Jonatan Ivanov
34+
* @since 3.4.0
35+
*/
36+
@AutoConfiguration(before = HealthContributorAutoConfiguration.class)
37+
@ConditionalOnEnabledHealthIndicator("ssl")
38+
@EnableConfigurationProperties(SslHealthIndicatorProperties.class)
39+
public class SslHealthContributorAutoConfiguration {
40+
41+
@Bean
42+
@ConditionalOnMissingBean(name = "sslHealthIndicator")
43+
public SslHealthIndicator sslHealthIndicator(SslInfo sslInfo) {
44+
return new SslHealthIndicator(sslInfo);
45+
}
46+
47+
@Bean
48+
@ConditionalOnMissingBean
49+
public SslInfo sslInfo(SslBundles sslBundles, SslHealthIndicatorProperties sslHealthIndicatorProperties) {
50+
return new SslInfo(sslBundles, sslHealthIndicatorProperties.getCertificateValidityWarningThreshold());
51+
}
52+
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.ssl;
18+
19+
import java.time.Duration;
20+
21+
import org.springframework.boot.actuate.ssl.SslHealthIndicator;
22+
import org.springframework.boot.context.properties.ConfigurationProperties;
23+
24+
/**
25+
* External configuration properties for {@link SslHealthIndicator}.
26+
*
27+
* @author Jonatan Ivanov
28+
* @since 3.4.0
29+
*/
30+
@ConfigurationProperties(prefix = "management.health.ssl")
31+
public class SslHealthIndicatorProperties {
32+
33+
/**
34+
* If an SSL Certificate will be invalid within the time span defined by this
35+
* threshold, it should trigger a warning.
36+
*/
37+
private Duration certificateValidityWarningThreshold = Duration.ofDays(14);
38+
39+
public Duration getCertificateValidityWarningThreshold() {
40+
return this.certificateValidityWarningThreshold;
41+
}
42+
43+
public void setCertificateValidityWarningThreshold(Duration certificateValidityWarningThreshold) {
44+
this.certificateValidityWarningThreshold = certificateValidityWarningThreshold;
45+
}
46+
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Auto-configuration for actuator ssl concerns.
19+
*/
20+
package org.springframework.boot.actuate.autoconfigure.ssl;

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,18 @@
227227
"description": "Whether to enable Redis health check.",
228228
"defaultValue": true
229229
},
230+
{
231+
"name": "management.health.ssl.certificate-validity-warning-threshold",
232+
"type": "java.time.Duration",
233+
"description": "If an SSL Certificate will be invalid within the time span defined by this threshold, it should trigger a warning.",
234+
"defaultValue": "14d"
235+
},
236+
{
237+
"name": "management.health.ssl.enabled",
238+
"type": "java.lang.Boolean",
239+
"description": "Whether to enable SSL Certificate health check.",
240+
"defaultValue": true
241+
},
230242
{
231243
"name": "management.httpexchanges.recording.enabled",
232244
"type": "java.lang.Boolean",
@@ -283,6 +295,12 @@
283295
"description": "Whether to enable process info.",
284296
"defaultValue": false
285297
},
298+
{
299+
"name": "management.info.ssl.enabled",
300+
"type": "java.lang.Boolean",
301+
"description": "Whether to enable SSL Certificate info.",
302+
"defaultValue": false
303+
},
286304
{
287305
"name": "management.metrics.binders.files.enabled",
288306
"type": "java.lang.Boolean",

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagem
104104
org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
105105
org.springframework.boot.actuate.autoconfigure.session.SessionsEndpointAutoConfiguration
106106
org.springframework.boot.actuate.autoconfigure.startup.StartupEndpointAutoConfiguration
107+
org.springframework.boot.actuate.autoconfigure.ssl.SslHealthContributorAutoConfiguration
107108
org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration
108109
org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration
109110
org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfigurationTests.java

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.info;
1818

19+
import java.time.Duration;
1920
import java.util.Map;
2021
import java.util.Properties;
2122

2223
import org.junit.jupiter.api.Test;
2324

25+
import org.springframework.boot.actuate.autoconfigure.ssl.SslHealthIndicatorProperties;
2426
import org.springframework.boot.actuate.info.BuildInfoContributor;
2527
import org.springframework.boot.actuate.info.EnvironmentInfoContributor;
2628
import org.springframework.boot.actuate.info.GitInfoContributor;
@@ -29,12 +31,16 @@
2931
import org.springframework.boot.actuate.info.JavaInfoContributor;
3032
import org.springframework.boot.actuate.info.OsInfoContributor;
3133
import org.springframework.boot.actuate.info.ProcessInfoContributor;
34+
import org.springframework.boot.actuate.info.SslInfoContributor;
3235
import org.springframework.boot.autoconfigure.AutoConfigurations;
36+
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
3337
import org.springframework.boot.info.BuildProperties;
3438
import org.springframework.boot.info.GitProperties;
3539
import org.springframework.boot.info.JavaInfo;
3640
import org.springframework.boot.info.OsInfo;
3741
import org.springframework.boot.info.ProcessInfo;
42+
import org.springframework.boot.info.SslInfo;
43+
import org.springframework.boot.ssl.SslBundles;
3844
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3945
import org.springframework.context.annotation.Bean;
4046
import org.springframework.context.annotation.Configuration;
@@ -60,7 +66,8 @@ void envContributor() {
6066

6167
@Test
6268
void defaultInfoContributorsEnabled() {
63-
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(InfoContributor.class));
69+
this.contextRunner.run(
70+
(context) -> assertThat(context).doesNotHaveBean(InfoContributor.class).doesNotHaveBean(SslInfo.class));
6471
}
6572

6673
@Test
@@ -176,6 +183,54 @@ void processInfoContributor() {
176183
});
177184
}
178185

186+
@Test
187+
void sslInfoContributor() {
188+
this.contextRunner.withConfiguration(AutoConfigurations.of(SslAutoConfiguration.class))
189+
.withPropertyValues("management.info.ssl.enabled=true", "server.ssl.bundle=ssltest",
190+
"spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks")
191+
.run((context) -> {
192+
assertThat(context).hasSingleBean(SslInfoContributor.class);
193+
assertThat(context).hasSingleBean(SslInfo.class);
194+
Map<String, Object> content = invokeContributor(context.getBean(SslInfoContributor.class));
195+
assertThat(content).containsKey("ssl");
196+
assertThat(content.get("ssl")).isInstanceOf(SslInfo.class);
197+
});
198+
}
199+
200+
@Test
201+
void sslInfoContributorWithWarningThreshold() {
202+
this.contextRunner.withConfiguration(AutoConfigurations.of(SslAutoConfiguration.class))
203+
.withPropertyValues("management.info.ssl.enabled=true", "server.ssl.bundle=ssltest",
204+
"spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks",
205+
"management.health.ssl.certificate-validity-warning-threshold=1d")
206+
.run((context) -> {
207+
assertThat(context).hasSingleBean(SslInfoContributor.class);
208+
assertThat(context).hasSingleBean(SslInfo.class);
209+
assertThat(context).hasSingleBean(SslHealthIndicatorProperties.class);
210+
assertThat(context.getBean(SslHealthIndicatorProperties.class).getCertificateValidityWarningThreshold())
211+
.isEqualTo(Duration.ofDays(1));
212+
Map<String, Object> content = invokeContributor(context.getBean(SslInfoContributor.class));
213+
assertThat(content).containsKey("ssl");
214+
assertThat(content.get("ssl")).isInstanceOf(SslInfo.class);
215+
});
216+
}
217+
218+
@Test
219+
void customSslInfo() {
220+
this.contextRunner.withUserConfiguration(CustomSslInfoConfiguration.class)
221+
.withConfiguration(AutoConfigurations.of(SslAutoConfiguration.class))
222+
.withPropertyValues("management.info.ssl.enabled=true", "server.ssl.bundle=ssltest",
223+
"spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks")
224+
.run((context) -> {
225+
assertThat(context).hasSingleBean(SslInfoContributor.class);
226+
assertThat(context).hasSingleBean(SslInfo.class);
227+
assertThat(context.getBean(SslInfo.class)).isSameAs(context.getBean("customSslInfo"));
228+
Map<String, Object> content = invokeContributor(context.getBean(SslInfoContributor.class));
229+
assertThat(content).containsKey("ssl");
230+
assertThat(content.get("ssl")).isInstanceOf(SslInfo.class);
231+
});
232+
}
233+
179234
private Map<String, Object> invokeContributor(InfoContributor contributor) {
180235
Info.Builder builder = new Info.Builder();
181236
contributor.contribute(builder);
@@ -241,4 +296,14 @@ BuildInfoContributor customBuildInfoContributor() {
241296

242297
}
243298

299+
@Configuration(proxyBeanMethods = false)
300+
static class CustomSslInfoConfiguration {
301+
302+
@Bean
303+
SslInfo customSslInfo(SslBundles sslBundles) {
304+
return new SslInfo(sslBundles, Duration.ofDays(7));
305+
}
306+
307+
}
308+
244309
}

0 commit comments

Comments
 (0)