Skip to content

Commit

Permalink
Add SslInfoContributor and SslHealthIndicator
Browse files Browse the repository at this point in the history
  • Loading branch information
jonatan-ivanov committed Aug 1, 2024
1 parent 1669268 commit 1c6695d
Show file tree
Hide file tree
Showing 13 changed files with 561 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

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

import org.springframework.boot.actuate.autoconfigure.ssl.SslHealthIndicatorProperties;
import org.springframework.boot.actuate.info.BuildInfoContributor;
import org.springframework.boot.actuate.info.EnvironmentInfoContributor;
import org.springframework.boot.actuate.info.GitInfoContributor;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.actuate.info.JavaInfoContributor;
import org.springframework.boot.actuate.info.OsInfoContributor;
import org.springframework.boot.actuate.info.ProcessInfoContributor;
import org.springframework.boot.actuate.info.SslInfoContributor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
Expand All @@ -31,6 +33,8 @@
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.info.BuildProperties;
import org.springframework.boot.info.GitProperties;
import org.springframework.boot.info.SslInfo;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
Expand All @@ -46,7 +50,7 @@
* @since 2.0.0
*/
@AutoConfiguration(after = ProjectInfoAutoConfiguration.class)
@EnableConfigurationProperties(InfoContributorProperties.class)
@EnableConfigurationProperties({ InfoContributorProperties.class, SslHealthIndicatorProperties.class })
public class InfoContributorAutoConfiguration {

/**
Expand Down Expand Up @@ -100,4 +104,18 @@ public ProcessInfoContributor processInfoContributor() {
return new ProcessInfoContributor();
}

@Bean
@ConditionalOnEnabledInfoContributor(value = "ssl", fallback = InfoContributorFallback.DISABLE)
@Order(DEFAULT_ORDER)
public SslInfoContributor sslInfoContributor(SslInfo sslInfo) {
return new SslInfoContributor(sslInfo);
}

@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledInfoContributor(value = "ssl", fallback = InfoContributorFallback.DISABLE)
public SslInfo sslInfo(SslBundles sslBundles, SslHealthIndicatorProperties sslHealthIndicatorProperties) {
return new SslInfo(sslBundles, sslHealthIndicatorProperties.getCertificateValidityWarningThreshold());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.actuate.autoconfigure.ssl;

import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
import org.springframework.boot.actuate.ssl.SslHealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.info.SslInfo;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.context.annotation.Bean;

/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link SslHealthIndicator}.
*
* @author Jonatan Ivanov
* @since 3.4.0
*/
@AutoConfiguration(before = HealthContributorAutoConfiguration.class)
@ConditionalOnEnabledHealthIndicator("ssl")
@EnableConfigurationProperties(SslHealthIndicatorProperties.class)
public class SslHealthContributorAutoConfiguration {

@Bean
@ConditionalOnMissingBean(name = "sslHealthIndicator")
public SslHealthIndicator sslHealthIndicator(SslInfo sslInfo) {
return new SslHealthIndicator(sslInfo);
}

@Bean
@ConditionalOnMissingBean
public SslInfo sslInfo(SslBundles sslBundles, SslHealthIndicatorProperties sslHealthIndicatorProperties) {
return new SslInfo(sslBundles, sslHealthIndicatorProperties.getCertificateValidityWarningThreshold());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.actuate.autoconfigure.ssl;

import java.time.Duration;

import org.springframework.boot.actuate.ssl.SslHealthIndicator;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* External configuration properties for {@link SslHealthIndicator}.
*
* @author Jonatan Ivanov
* @since 3.4.0
*/
@ConfigurationProperties(prefix = "management.health.ssl")
public class SslHealthIndicatorProperties {

/**
* If the certificate will be invalid within the time span defined by this threshold,
* it should trigger a warning.
*/
private Duration certificateValidityWarningThreshold = Duration.ofDays(14);

public Duration getCertificateValidityWarningThreshold() {
return this.certificateValidityWarningThreshold;
}

public void setCertificateValidityWarningThreshold(Duration certificateValidityWarningThreshold) {
this.certificateValidityWarningThreshold = certificateValidityWarningThreshold;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Auto-configuration for actuator ssl concerns.
*/
package org.springframework.boot.actuate.autoconfigure.ssl;
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagem
org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
org.springframework.boot.actuate.autoconfigure.session.SessionsEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.startup.StartupEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.ssl.SslHealthContributorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration
org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.actuate.info;

import org.springframework.boot.actuate.info.Info.Builder;
import org.springframework.boot.info.SslInfo;

/**
* An {@link InfoContributor} that exposes {@link SslInfo}.
*
* @author Jonatan Ivanov
* @since 3.4.0
*/
public class SslInfoContributor implements InfoContributor {

private final SslInfo sslInfo;

public SslInfoContributor(SslInfo sslInfo) {
this.sslInfo = sslInfo;
}

@Override
public void contribute(Builder builder) {
builder.withDetail("ssl", this.sslInfo);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.actuate.ssl;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health.Builder;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.info.SslInfo;
import org.springframework.boot.info.SslInfo.CertificateInfo;
import org.springframework.boot.info.SslInfo.CertificateInfo.Validity;

/**
* {@link HealthIndicator} that checks the certificates the application uses and reports
* {@link Status#OUT_OF_SERVICE} when a certificate is invalid or "WILL_EXPIRE_SOON" if it
* will expire within the configurable threshold.
*
* @author Jonatan Ivanov
* @since 3.4.0
*/
public class SslHealthIndicator extends AbstractHealthIndicator {

private static final Status WILL_EXPIRE_SOON_STATUS = new Status(Validity.Status.WILL_EXPIRE_SOON.name(),
"One of the certificates will expire within the defined threshold.");

private final SslInfo sslInfo;

public SslHealthIndicator(SslInfo sslInfo) {
this.sslInfo = sslInfo;
}

@Override
protected void doHealthCheck(Builder builder) throws Exception {
List<CertificateInfo> notValidCertificates = this.sslInfo.getBundles()
.stream()
.flatMap((bundle) -> bundle.getCertificateChains().stream())
.flatMap((certificateChain) -> certificateChain.getCertificates().stream())
.filter((certificate) -> certificate.getValidity() != null)
.filter((certificate) -> certificate.getValidity().getStatus() != Validity.Status.VALID)
.toList();

if (notValidCertificates.isEmpty()) {
builder.status(Status.UP);
}
else {
Set<Validity.Status> statuses = notValidCertificates.stream()
.map((certificate) -> certificate.getValidity().getStatus())
.collect(Collectors.toUnmodifiableSet());
if (statuses.contains(Validity.Status.EXPIRED) || statuses.contains(Validity.Status.NOT_YET_VALID)) {
builder.status(Status.OUT_OF_SERVICE);
}
else if (statuses.contains(Validity.Status.WILL_EXPIRE_SOON)) {
// TODO: Should we introduce Status.WARNING
// (returns 200 but indicates that something is not right)?
builder.status(WILL_EXPIRE_SOON_STATUS);
}
else {
builder.status(Status.OUT_OF_SERVICE);
}
builder.withDetail("certificates", notValidCertificates);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Actuator support for ssl concerns.
*/
package org.springframework.boot.actuate.ssl;
Loading

0 comments on commit 1c6695d

Please sign in to comment.