Skip to content

Commit 1c6695d

Browse files
Add SslInfoContributor and SslHealthIndicator
1 parent 1669268 commit 1c6695d

File tree

13 files changed

+561
-7
lines changed

13 files changed

+561
-7
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 the certificate will be invalid within the time span defined by this threshold,
35+
* 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/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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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.info;
18+
19+
import org.springframework.boot.actuate.info.Info.Builder;
20+
import org.springframework.boot.info.SslInfo;
21+
22+
/**
23+
* An {@link InfoContributor} that exposes {@link SslInfo}.
24+
*
25+
* @author Jonatan Ivanov
26+
* @since 3.4.0
27+
*/
28+
public class SslInfoContributor implements InfoContributor {
29+
30+
private final SslInfo sslInfo;
31+
32+
public SslInfoContributor(SslInfo sslInfo) {
33+
this.sslInfo = sslInfo;
34+
}
35+
36+
@Override
37+
public void contribute(Builder builder) {
38+
builder.withDetail("ssl", this.sslInfo);
39+
}
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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.ssl;
18+
19+
import java.util.List;
20+
import java.util.Set;
21+
import java.util.stream.Collectors;
22+
23+
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
24+
import org.springframework.boot.actuate.health.Health.Builder;
25+
import org.springframework.boot.actuate.health.HealthIndicator;
26+
import org.springframework.boot.actuate.health.Status;
27+
import org.springframework.boot.info.SslInfo;
28+
import org.springframework.boot.info.SslInfo.CertificateInfo;
29+
import org.springframework.boot.info.SslInfo.CertificateInfo.Validity;
30+
31+
/**
32+
* {@link HealthIndicator} that checks the certificates the application uses and reports
33+
* {@link Status#OUT_OF_SERVICE} when a certificate is invalid or "WILL_EXPIRE_SOON" if it
34+
* will expire within the configurable threshold.
35+
*
36+
* @author Jonatan Ivanov
37+
* @since 3.4.0
38+
*/
39+
public class SslHealthIndicator extends AbstractHealthIndicator {
40+
41+
private static final Status WILL_EXPIRE_SOON_STATUS = new Status(Validity.Status.WILL_EXPIRE_SOON.name(),
42+
"One of the certificates will expire within the defined threshold.");
43+
44+
private final SslInfo sslInfo;
45+
46+
public SslHealthIndicator(SslInfo sslInfo) {
47+
this.sslInfo = sslInfo;
48+
}
49+
50+
@Override
51+
protected void doHealthCheck(Builder builder) throws Exception {
52+
List<CertificateInfo> notValidCertificates = this.sslInfo.getBundles()
53+
.stream()
54+
.flatMap((bundle) -> bundle.getCertificateChains().stream())
55+
.flatMap((certificateChain) -> certificateChain.getCertificates().stream())
56+
.filter((certificate) -> certificate.getValidity() != null)
57+
.filter((certificate) -> certificate.getValidity().getStatus() != Validity.Status.VALID)
58+
.toList();
59+
60+
if (notValidCertificates.isEmpty()) {
61+
builder.status(Status.UP);
62+
}
63+
else {
64+
Set<Validity.Status> statuses = notValidCertificates.stream()
65+
.map((certificate) -> certificate.getValidity().getStatus())
66+
.collect(Collectors.toUnmodifiableSet());
67+
if (statuses.contains(Validity.Status.EXPIRED) || statuses.contains(Validity.Status.NOT_YET_VALID)) {
68+
builder.status(Status.OUT_OF_SERVICE);
69+
}
70+
else if (statuses.contains(Validity.Status.WILL_EXPIRE_SOON)) {
71+
// TODO: Should we introduce Status.WARNING
72+
// (returns 200 but indicates that something is not right)?
73+
builder.status(WILL_EXPIRE_SOON_STATUS);
74+
}
75+
else {
76+
builder.status(Status.OUT_OF_SERVICE);
77+
}
78+
builder.withDetail("certificates", notValidCertificates);
79+
}
80+
}
81+
82+
}
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+
* Actuator support for ssl concerns.
19+
*/
20+
package org.springframework.boot.actuate.ssl;

0 commit comments

Comments
 (0)