Add support for configuring TLS handshake timeouts
This commit is contained in:
@@ -950,7 +950,24 @@ spring:
|
||||
- cert2.pem
|
||||
----
|
||||
|
||||
If the Spring Cloud Gateway is not provisioned with trusted certificates the default trust store is used (which can be overriden with system property javax.net.ssl.trustStore.
|
||||
If the Spring Cloud Gateway is not provisioned with trusted certificates the default trust store is used (which can be overriden with system property javax.net.ssl.trustStore).
|
||||
|
||||
=== TLS Handshake
|
||||
|
||||
The Gateway maintains a client pool that it uses to route to backends. When communicating over https the client initiates a TLS handshake. A number of timeouts are assoicated with this handshake. These timeouts can be configured (defaults shown):
|
||||
|
||||
.application.yml
|
||||
[source,yaml]
|
||||
----
|
||||
spring:
|
||||
cloud:
|
||||
gateway:
|
||||
httpclient:
|
||||
ssl:
|
||||
handshake-timeout-millis: 10000
|
||||
close-notify-flush-timeout-millis: 3000
|
||||
close-notify-read-timeout-millis: 0
|
||||
----
|
||||
|
||||
== Configuration
|
||||
|
||||
|
||||
@@ -166,6 +166,9 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
// configure ssl
|
||||
HttpClientProperties.Ssl ssl = properties.getSsl();
|
||||
opts.sslHandshakeTimeoutMillis(ssl.getHandshakeTimeoutMillis());
|
||||
opts.sslCloseNotifyFlushTimeoutMillis(ssl.getCloseNotifyFlushTimeoutMillis());
|
||||
opts.sslCloseNotifyReadTimeoutMillis(ssl.getCloseNotifyReadTimeoutMillis());
|
||||
X509Certificate[] trustedX509Certificates = ssl
|
||||
.getTrustedX509CertificatesForTrustManager();
|
||||
if (trustedX509Certificates.length > 0) {
|
||||
|
||||
@@ -222,8 +222,13 @@ public class HttpClientProperties {
|
||||
public class Ssl {
|
||||
/** Installs the netty InsecureTrustManagerFactory. This is insecure and not suitable for production. */
|
||||
private boolean useInsecureTrustManager = false;
|
||||
|
||||
|
||||
private List<String> trustedX509Certificates = new ArrayList<>();
|
||||
|
||||
// use netty default SSL timeouts
|
||||
private long handshakeTimeoutMillis = 10000L;
|
||||
private long closeNotifyFlushTimeoutMillis = 3000L;
|
||||
private long closeNotifyReadTimeoutMillis = 0L;
|
||||
|
||||
public List<String> getTrustedX509Certificates() {
|
||||
return trustedX509Certificates;
|
||||
@@ -268,11 +273,42 @@ public class HttpClientProperties {
|
||||
this.useInsecureTrustManager = useInsecureTrustManager;
|
||||
}
|
||||
|
||||
public long getHandshakeTimeoutMillis() {
|
||||
return handshakeTimeoutMillis;
|
||||
}
|
||||
|
||||
public void setHandshakeTimeoutMillis(long handshakeTimeoutMillis) {
|
||||
this.handshakeTimeoutMillis = handshakeTimeoutMillis;
|
||||
}
|
||||
|
||||
public long getCloseNotifyFlushTimeoutMillis() {
|
||||
return closeNotifyFlushTimeoutMillis;
|
||||
}
|
||||
|
||||
public void setCloseNotifyFlushTimeoutMillis(long closeNotifyFlushTimeoutMillis) {
|
||||
this.closeNotifyFlushTimeoutMillis = closeNotifyFlushTimeoutMillis;
|
||||
}
|
||||
|
||||
public long getCloseNotifyReadTimeoutMillis() {
|
||||
return closeNotifyReadTimeoutMillis;
|
||||
}
|
||||
|
||||
public void setCloseNotifyReadTimeoutMillis(long closeNotifyReadTimeoutMillis) {
|
||||
this.closeNotifyReadTimeoutMillis = closeNotifyReadTimeoutMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Ssl {useInsecureTrustManager=" + useInsecureTrustManager
|
||||
+ ", trustedX509Certificates=" + trustedX509Certificates + "}";
|
||||
+ ", trustedX509Certificates=" + trustedX509Certificates
|
||||
+ ", handshakeTimeoutMillis=" + handshakeTimeoutMillis
|
||||
+ ", closeNotifyFlushTimeoutMillis="
|
||||
+ closeNotifyFlushTimeoutMillis
|
||||
+ ", closeNotifyReadTimeoutMillis="
|
||||
+ closeNotifyReadTimeoutMillis + "}";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2013-2017 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
|
||||
*
|
||||
* http://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.cloud.gateway.test.ssl;
|
||||
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.reactive.server.JsonPathAssertions;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
// this test works because it assumes TLS hand shake cannot be done in 1ms. It takes closer to 80ms
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT, properties = {"spring.cloud.gateway.httpclient.ssl.handshake-timeout-millis=1"})
|
||||
@DirtiesContext
|
||||
@ActiveProfiles("ssl")
|
||||
public class SSLHandshakeTimeoutTests extends SingleCertSSLTests {
|
||||
|
||||
@Test
|
||||
@Override // here we validate that it the handshake times out
|
||||
public void testSslTrust() {
|
||||
ResponseSpec responseSpec = testClient.get().uri("/ssltrust").exchange();
|
||||
responseSpec.expectStatus().is5xxServerError();
|
||||
JsonPathAssertions jsonPath = responseSpec.expectBody().jsonPath("message");
|
||||
jsonPath.isEqualTo("handshake timed out");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
package org.springframework.cloud.gateway.test.ssl;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
@@ -30,16 +29,15 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
@@ -61,20 +59,17 @@ public class SingleCertSSLTests extends BaseWebClientTests {
|
||||
opt -> opt.sslContext(sslContext));
|
||||
baseUri = "https://localhost:" + port;
|
||||
this.webClient = WebClient.builder().clientConnector(httpConnector)
|
||||
.baseUrl(baseUri).build();
|
||||
.baseUrl(baseUri).build();
|
||||
this.testClient = WebTestClient.bindToServer(httpConnector).baseUrl(baseUri).build();
|
||||
}
|
||||
catch (SSLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSslTrust() {
|
||||
ClientResponse clientResponse = webClient.get().uri("/ssltrust")
|
||||
.exchange().block();
|
||||
HttpStatus statusCode = clientResponse.statusCode();
|
||||
assertTrue(statusCode.is2xxSuccessful());
|
||||
|
||||
testClient.get().uri("/ssltrust").exchange().expectStatus().is2xxSuccessful();
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
|
||||
Reference in New Issue
Block a user