diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
index 2de8b044da..5723543a5c 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
@@ -127,6 +127,11 @@
micrometer-registry-statsd
true
+
+ io.projectreactor.ipc
+ reactor-netty
+ true
+
io.searchbox
jest
@@ -379,11 +384,6 @@
json-path
test
-
- io.projectreactor.ipc
- reactor-netty
- test
-
io.undertow
undertow-core
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java
index 0e4edfa56c..5ccda4dd9b 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java
@@ -111,9 +111,9 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
}
private CloudFoundrySecurityInterceptor getSecurityInterceptor(
- WebClient.Builder restTemplateBuilder, Environment environment) {
+ WebClient.Builder webClientBuilder, Environment environment) {
ReactiveCloudFoundrySecurityService cloudfoundrySecurityService = getCloudFoundrySecurityService(
- restTemplateBuilder, environment);
+ webClientBuilder, environment);
ReactiveTokenValidator tokenValidator = new ReactiveTokenValidator(
cloudfoundrySecurityService);
return new CloudFoundrySecurityInterceptor(tokenValidator,
@@ -124,9 +124,11 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
private ReactiveCloudFoundrySecurityService getCloudFoundrySecurityService(
WebClient.Builder webClientBuilder, Environment environment) {
String cloudControllerUrl = environment.getProperty("vcap.application.cf_api");
+ boolean skipSslValidation = environment.getProperty(
+ "management.cloudfoundry.skip-ssl-validation", Boolean.class, false);
return (cloudControllerUrl == null ? null
: new ReactiveCloudFoundrySecurityService(webClientBuilder,
- cloudControllerUrl));
+ cloudControllerUrl, skipSslValidation));
}
private CorsConfiguration getCorsConfiguration() {
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundrySecurityService.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundrySecurityService.java
index 01562c13e3..10526a0f1e 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundrySecurityService.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundrySecurityService.java
@@ -20,6 +20,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import io.netty.handler.ssl.SslProvider;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
@@ -27,6 +29,7 @@ import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryA
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatus;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
@@ -50,13 +53,24 @@ class ReactiveCloudFoundrySecurityService {
private Mono uaaUrl;
ReactiveCloudFoundrySecurityService(WebClient.Builder webClientBuilder,
- String cloudControllerUrl) {
+ String cloudControllerUrl, boolean skipSslValidation) {
Assert.notNull(webClientBuilder, "Webclient must not be null");
Assert.notNull(cloudControllerUrl, "CloudControllerUrl must not be null");
+ if (skipSslValidation) {
+ webClientBuilder.clientConnector(buildTrustAllSslConnector());
+ }
this.webClient = webClientBuilder.build();
this.cloudControllerUrl = cloudControllerUrl;
}
+ protected ReactorClientHttpConnector buildTrustAllSslConnector() {
+ return new ReactorClientHttpConnector(
+ (options) -> options.sslSupport((sslContextBuilder) -> {
+ sslContextBuilder.sslProvider(SslProvider.JDK)
+ .trustManager(InsecureTrustManagerFactory.INSTANCE);
+ }));
+ }
+
/**
* Return a Mono of the access level that should be granted to the given token.
* @param token the token
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java
index d1f64f573a..3486c712b9 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfigurationTests.java
@@ -21,9 +21,14 @@ import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
+import javax.net.ssl.SSLException;
+
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import reactor.ipc.netty.http.HttpResources;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
@@ -56,8 +61,10 @@ import org.springframework.security.web.server.WebFilterChainProxy;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
import static org.mockito.Mockito.mock;
/**
@@ -69,6 +76,9 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
private AnnotationConfigReactiveWebApplicationContext context;
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
@Before
public void setup() {
this.context = new AnnotationConfigReactiveWebApplicationContext();
@@ -76,6 +86,7 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
@After
public void close() {
+ HttpResources.reset();
if (this.context != null) {
this.context.close();
}
@@ -233,6 +244,38 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
.isInstanceOf(CloudFoundryReactiveHealthEndpointWebExtension.class);
}
+ @Test
+ public void skipSslValidation() {
+ setupContextWithCloudEnabled();
+ TestPropertyValues
+ .of("management.cloudfoundry.skip-ssl-validation:true")
+ .applyTo(this.context);
+ this.context.refresh();
+ CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping();
+ Object interceptor = ReflectionTestUtils.getField(handlerMapping,
+ "securityInterceptor");
+ Object interceptorSecurityService = ReflectionTestUtils.getField(interceptor,
+ "cloudFoundrySecurityService");
+ WebClient webClient = (WebClient) ReflectionTestUtils
+ .getField(interceptorSecurityService, "webClient");
+ webClient.get().uri("https://self-signed.badssl.com/").exchange().block();
+ }
+
+ @Test
+ public void sslValidationNotSkippedByDefault() {
+ setupContextWithCloudEnabled();
+ this.context.refresh();
+ CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping();
+ Object interceptor = ReflectionTestUtils.getField(handlerMapping,
+ "securityInterceptor");
+ Object interceptorSecurityService = ReflectionTestUtils.getField(interceptor,
+ "cloudFoundrySecurityService");
+ WebClient webClient = (WebClient) ReflectionTestUtils
+ .getField(interceptorSecurityService, "webClient");
+ this.thrown.expectCause(instanceOf(SSLException.class));
+ webClient.get().uri("https://self-signed.badssl.com/").exchange().block();
+ }
+
private void setupContextWithCloudEnabled() {
TestPropertyValues
.of("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundrySecurityServiceTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundrySecurityServiceTests.java
index 99dbe13349..f1f5614eee 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundrySecurityServiceTests.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundrySecurityServiceTests.java
@@ -59,7 +59,7 @@ public class ReactiveCloudFoundrySecurityServiceTests {
this.server = new MockWebServer();
this.builder = WebClient.builder().baseUrl(this.server.url("/").toString());
this.securityService = new ReactiveCloudFoundrySecurityService(this.builder,
- CLOUD_CONTROLLER);
+ CLOUD_CONTROLLER, false);
}
@After