diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java index 8853f2db..1765b6f7 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java @@ -111,7 +111,7 @@ public class ZuulProperties { private boolean traceRequestBody = true; /** - * Flag to say that path elelents past the first semicolon can be dropped. + * Flag to say that path elements past the first semicolon can be dropped. */ private boolean removeSemicolonContent = true; @@ -125,6 +125,12 @@ public class ZuulProperties { private Set sensitiveHeaders = new LinkedHashSet<>( Arrays.asList("Cookie", "Set-Cookie", "Authorization")); + /** + * Flag to say whether the hostname for ssl connections should be verified or now. Default is true. + * This should only be used in test setups! + */ + private boolean sslHostnameValidationEnabled =true; + public Set getIgnoredHeaders() { Set ignoredHeaders = new LinkedHashSet<>(this.ignoredHeaders); if (ClassUtils.isPresent( diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java index 4b8e9020..e2917fa8 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java @@ -53,11 +53,13 @@ import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.ContentType; import org.apache.http.entity.InputStreamEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; +import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; @@ -66,7 +68,6 @@ import org.apache.http.protocol.HttpContext; import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper; import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.Host; -import org.springframework.http.HttpStatus; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; @@ -94,6 +95,8 @@ public class SimpleHostRoutingFilter extends ZuulFilter { private final Timer connectionManagerTimer = new Timer( "SimpleHostRoutingFilter.connectionManagerTimer", true); + private boolean sslHostnameValidationEnabled; + private ProxyRequestHelper helper; private Host hostProperties; private PoolingHttpClientConnectionManager connectionManager; @@ -115,6 +118,7 @@ public class SimpleHostRoutingFilter extends ZuulFilter { public SimpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties properties) { this.helper = helper; this.hostProperties = properties.getHost(); + this.sslHostnameValidationEnabled = properties.isSslHostnameValidationEnabled(); } @PostConstruct @@ -204,11 +208,18 @@ public class SimpleHostRoutingFilter extends ZuulFilter { } } }, new SecureRandom()); - final Registry registry = RegistryBuilder + RegistryBuilder registryBuilder = RegistryBuilder . create() - .register("http", PlainConnectionSocketFactory.INSTANCE) - .register("https", new SSLConnectionSocketFactory(sslContext)) - .build(); + .register("http", PlainConnectionSocketFactory.INSTANCE); + if (sslHostnameValidationEnabled) { + registryBuilder.register("https", + new SSLConnectionSocketFactory(sslContext)); + } + else { + registryBuilder.register("https", new SSLConnectionSocketFactory( + sslContext, NoopHostnameVerifier.INSTANCE)); + } + final Registry registry = registryBuilder.build(); this.connectionManager = new PoolingHttpClientConnectionManager(registry); this.connectionManager @@ -228,7 +239,11 @@ public class SimpleHostRoutingFilter extends ZuulFilter { .setConnectTimeout(CONNECTION_TIMEOUT.get()) .setCookieSpec(CookieSpecs.IGNORE_COOKIES).build(); - return HttpClients.custom().setConnectionManager(newConnectionManager()) + HttpClientBuilder httpClientBuilder = HttpClients.custom(); + if (!sslHostnameValidationEnabled) { + httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); + } + return httpClientBuilder.setConnectionManager(newConnectionManager()) .setDefaultRequestConfig(requestConfig) .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)) .setRedirectStrategy(new RedirectStrategy() { @@ -363,4 +378,11 @@ public class SimpleHostRoutingFilter extends ZuulFilter { this.helper.addIgnoredHeaders(names); } + /** + * Determines whether the filter enables the validation for ssl hostnames. + * @return + */ + boolean isSslHostnameValidationEnabled() { + return sslHostnameValidationEnabled; + } } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java index c2470fbb..20b0f5ef 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java @@ -28,6 +28,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.springframework.boot.test.EnvironmentTestUtils.addEnvironment; /** @@ -54,6 +56,21 @@ public class SimpleHostRoutingFilterTests { assertEquals(10, connMgr.getDefaultMaxPerRoute()); } + @Test + public void validateSslHostnamesByDefault() { + setupContext(); + assertTrue("Hostname verification should be enabled by default", + getFilter().isSslHostnameValidationEnabled()); + } + + @Test + public void validationOfSslHostnamesCanBeDisabledViaProperty() { + addEnvironment(this.context, "zuul.sslHostnameValidationEnabled=false"); + setupContext(); + assertFalse("Hostname verification should be disabled via property", + getFilter().isSslHostnameValidationEnabled()); + } + @Test public void defaultPropertiesAreApplied() { setupContext();