add ability to use sslBundles
Fixes gh-2981
This commit is contained in:
@@ -36,6 +36,8 @@ import io.netty.handler.ssl.SslContextBuilder;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
/**
|
||||
@@ -43,6 +45,7 @@ import org.springframework.util.ResourceUtils;
|
||||
* configuration (can be the same as T).
|
||||
*
|
||||
* @author Abel Salgado Romero
|
||||
* @author Dominic Niemann
|
||||
*/
|
||||
public abstract class AbstractSslConfigurer<T, S> {
|
||||
|
||||
@@ -50,8 +53,11 @@ public abstract class AbstractSslConfigurer<T, S> {
|
||||
|
||||
private final HttpClientProperties.Ssl ssl;
|
||||
|
||||
protected AbstractSslConfigurer(HttpClientProperties.Ssl sslProperties) {
|
||||
private final SslBundles bundles;
|
||||
|
||||
protected AbstractSslConfigurer(HttpClientProperties.Ssl sslProperties, SslBundles bundles) {
|
||||
this.ssl = sslProperties;
|
||||
this.bundles = bundles;
|
||||
}
|
||||
|
||||
abstract public S configureSsl(T client) throws SSLException;
|
||||
@@ -60,6 +66,16 @@ public abstract class AbstractSslConfigurer<T, S> {
|
||||
return ssl;
|
||||
}
|
||||
|
||||
protected SslBundle getBundle() {
|
||||
if(ssl.getSslBundle() == null || ssl.getSslBundle().length() > 0) {
|
||||
return null;
|
||||
}
|
||||
if(bundles.getBundleNames().contains(ssl.getSslBundle())) {
|
||||
return bundles.getBundle(ssl.getSslBundle());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected X509Certificate[] getTrustedX509CertificatesForTrustManager() {
|
||||
|
||||
try {
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfig
|
||||
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
|
||||
import org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint;
|
||||
import org.springframework.cloud.gateway.actuate.GatewayLegacyControllerEndpoint;
|
||||
@@ -187,6 +188,7 @@ import org.springframework.web.reactive.socket.server.upgrade.ReactorNettyReques
|
||||
* @author Mete Alpaslan Katırcıoğlu
|
||||
* @author Alberto C. Ríos
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Dominic Niemann
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
|
||||
@@ -353,13 +355,13 @@ public class GatewayAutoConfiguration {
|
||||
@ConditionalOnEnabledFilter(JsonToGrpcGatewayFilterFactory.class)
|
||||
@ConditionalOnMissingBean(GrpcSslConfigurer.class)
|
||||
@ConditionalOnClass(name = "io.grpc.Channel")
|
||||
public GrpcSslConfigurer grpcSslConfigurer(HttpClientProperties properties)
|
||||
public GrpcSslConfigurer grpcSslConfigurer(HttpClientProperties properties, SslBundles bundles)
|
||||
throws KeyStoreException, NoSuchAlgorithmException {
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory
|
||||
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(KeyStore.getInstance(KeyStore.getDefaultType()));
|
||||
|
||||
return new GrpcSslConfigurer(properties.getSsl());
|
||||
return new GrpcSslConfigurer(properties.getSsl(), bundles);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -755,8 +757,9 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public HttpClientSslConfigurer httpClientSslConfigurer(ServerProperties serverProperties,
|
||||
HttpClientProperties httpClientProperties) {
|
||||
return new HttpClientSslConfigurer(httpClientProperties.getSsl(), serverProperties) {
|
||||
HttpClientProperties httpClientProperties,
|
||||
SslBundles bundles) {
|
||||
return new HttpClientSslConfigurer(httpClientProperties.getSsl(), serverProperties, bundles) {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -25,13 +25,17 @@ import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
|
||||
/**
|
||||
* @author Alberto C. Ríos
|
||||
* @author Dominic Niemann
|
||||
*/
|
||||
public class GrpcSslConfigurer extends AbstractSslConfigurer<NettyChannelBuilder, ManagedChannel> {
|
||||
|
||||
public GrpcSslConfigurer(HttpClientProperties.Ssl sslProperties) {
|
||||
super(sslProperties);
|
||||
public GrpcSslConfigurer(HttpClientProperties.Ssl sslProperties, SslBundles bundles) {
|
||||
super(sslProperties, bundles);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,6 +49,7 @@ public class GrpcSslConfigurer extends AbstractSslConfigurer<NettyChannelBuilder
|
||||
|
||||
final HttpClientProperties.Ssl ssl = getSslProperties();
|
||||
boolean useInsecureTrustManager = ssl.isUseInsecureTrustManager();
|
||||
SslBundle bundle = getBundle();
|
||||
if (useInsecureTrustManager) {
|
||||
sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE.getTrustManagers()[0]);
|
||||
}
|
||||
@@ -52,8 +57,17 @@ public class GrpcSslConfigurer extends AbstractSslConfigurer<NettyChannelBuilder
|
||||
if (!useInsecureTrustManager && ssl.getTrustedX509Certificates().size() > 0) {
|
||||
sslContextBuilder.trustManager(getTrustedX509CertificatesForTrustManager());
|
||||
}
|
||||
else if (bundle != null) {
|
||||
sslContextBuilder.trustManager(bundle.getManagers().getTrustManagerFactory());
|
||||
}
|
||||
|
||||
return sslContextBuilder.keyManager(getKeyManagerFactory()).build();
|
||||
if (bundle != null) {
|
||||
sslContextBuilder.keyManager(bundle.getManagers().getKeyManagerFactory());
|
||||
}
|
||||
else {
|
||||
sslContextBuilder.keyManager(getKeyManagerFactory());
|
||||
}
|
||||
return sslContextBuilder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -419,6 +419,9 @@ public class HttpClientProperties {
|
||||
/** Trusted certificates for verifying the remote endpoint's certificate. */
|
||||
private List<String> trustedX509Certificates = new ArrayList<>();
|
||||
|
||||
/** The name of the SSL bundle to use. */
|
||||
private String sslBundle;
|
||||
|
||||
// use netty default SSL timeouts
|
||||
/** SSL handshake timeout. Default to 10000 ms */
|
||||
private Duration handshakeTimeout = Duration.ofMillis(10000);
|
||||
@@ -524,6 +527,14 @@ public class HttpClientProperties {
|
||||
this.closeNotifyReadTimeout = closeNotifyReadTimeout;
|
||||
}
|
||||
|
||||
public String getSslBundle() {
|
||||
return sslBundle;
|
||||
}
|
||||
|
||||
public void setSslBundle(String sslBundle) {
|
||||
this.sslBundle = sslBundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("useInsecureTrustManager", useInsecureTrustManager)
|
||||
|
||||
@@ -25,20 +25,22 @@ import reactor.netty.http.client.HttpClient;
|
||||
import reactor.netty.tcp.SslProvider;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
|
||||
public class HttpClientSslConfigurer extends AbstractSslConfigurer<HttpClient, HttpClient> {
|
||||
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
public HttpClientSslConfigurer(HttpClientProperties.Ssl sslProperties, ServerProperties serverProperties) {
|
||||
super(sslProperties);
|
||||
public HttpClientSslConfigurer(HttpClientProperties.Ssl sslProperties, ServerProperties serverProperties, SslBundles bundles) {
|
||||
super(sslProperties, bundles);
|
||||
this.serverProperties = serverProperties;
|
||||
}
|
||||
|
||||
public HttpClient configureSsl(HttpClient client) {
|
||||
final HttpClientProperties.Ssl ssl = getSslProperties();
|
||||
|
||||
if ((ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0)
|
||||
if (getBundle() != null || (ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0)
|
||||
|| getTrustedX509CertificatesForTrustManager().length > 0 || ssl.isUseInsecureTrustManager()) {
|
||||
client = client.secure(sslContextSpec -> {
|
||||
// configure ssl
|
||||
@@ -53,15 +55,24 @@ public class HttpClientSslConfigurer extends AbstractSslConfigurer<HttpClient, H
|
||||
? Http2SslContextSpec.forClient() : Http11SslContextSpec.forClient();
|
||||
clientSslContext.configure(sslContextBuilder -> {
|
||||
X509Certificate[] trustedX509Certificates = getTrustedX509CertificatesForTrustManager();
|
||||
SslBundle bundle = getBundle();
|
||||
if (trustedX509Certificates.length > 0) {
|
||||
setTrustManager(sslContextBuilder, trustedX509Certificates);
|
||||
}
|
||||
else if (ssl.isUseInsecureTrustManager()) {
|
||||
setTrustManager(sslContextBuilder, InsecureTrustManagerFactory.INSTANCE);
|
||||
}
|
||||
else if (bundle != null) {
|
||||
setTrustManager(sslContextBuilder, bundle.getManagers().getTrustManagerFactory());
|
||||
}
|
||||
|
||||
try {
|
||||
sslContextBuilder.keyManager(getKeyManagerFactory());
|
||||
if(bundle != null) {
|
||||
sslContextBuilder.keyManager(bundle.getManagers().getKeyManagerFactory());
|
||||
}
|
||||
else {
|
||||
sslContextBuilder.keyManager(getKeyManagerFactory());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error(e);
|
||||
|
||||
@@ -36,6 +36,7 @@ import reactor.netty.resources.ConnectionProvider;
|
||||
import reactor.netty.tcp.SslProvider;
|
||||
import reactor.netty.transport.ProxyProvider;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
|
||||
@@ -45,9 +46,12 @@ import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.ssl.SslBundleRegistrar;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.ssl.DefaultSslBundleRegistry;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint;
|
||||
import org.springframework.cloud.gateway.actuate.GatewayLegacyControllerEndpoint;
|
||||
@@ -115,6 +119,7 @@ public class GatewayAutoConfigurationTests {
|
||||
SimpleMetricsExportAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
HttpClientCustomizedConfig.class, ServerPropertiesConfig.class))
|
||||
.withPropertyValues("spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager=true",
|
||||
"spring.cloud.gateway.httpclient.ssl.ssl-bundle=mybundle",
|
||||
"spring.cloud.gateway.httpclient.connect-timeout=10",
|
||||
"spring.cloud.gateway.httpclient.response-timeout=10s",
|
||||
"spring.cloud.gateway.httpclient.pool.eviction-interval=10s",
|
||||
@@ -136,6 +141,7 @@ public class GatewayAutoConfigurationTests {
|
||||
assertThat(properties.getPool().getEvictionInterval()).hasSeconds(10);
|
||||
assertThat(properties.getPool().isMetrics()).isEqualTo(true);
|
||||
assertThat(properties.getPool().getLeasingStrategy()).isEqualTo(LeasingStrategy.LIFO);
|
||||
assertThat(properties.getSsl().getSslBundle()).isEqualTo("mybundle");
|
||||
|
||||
assertThat(httpClient.configuration().isAcceptGzip()).isTrue();
|
||||
assertThat(httpClient.configuration().loggingHandler()).isNotNull();
|
||||
@@ -345,10 +351,20 @@ public class GatewayAutoConfigurationTests {
|
||||
@Bean
|
||||
@Primary
|
||||
CustomSslConfigurer customSslContextFactory(ServerProperties serverProperties,
|
||||
HttpClientProperties httpClientProperties) {
|
||||
return new CustomSslConfigurer(httpClientProperties.getSsl(), serverProperties);
|
||||
HttpClientProperties httpClientProperties,
|
||||
SslBundles bundles) {
|
||||
return new CustomSslConfigurer(httpClientProperties.getSsl(), serverProperties, bundles);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
SslBundles sslBundleRegistry(ObjectProvider<SslBundleRegistrar> sslBundleRegistrars) {
|
||||
DefaultSslBundleRegistry registry = new DefaultSslBundleRegistry();
|
||||
sslBundleRegistrars.orderedStream().forEach((registrar) -> {
|
||||
registrar.registerBundles(registry);
|
||||
});
|
||||
return registry;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class CustomHttpClientFactory extends HttpClientFactory {
|
||||
@@ -392,8 +408,8 @@ public class GatewayAutoConfigurationTests {
|
||||
|
||||
boolean insecureTrustManagerSet;
|
||||
|
||||
protected CustomSslConfigurer(HttpClientProperties.Ssl sslProperties, ServerProperties serverProperties) {
|
||||
super(sslProperties, serverProperties);
|
||||
protected CustomSslConfigurer(HttpClientProperties.Ssl sslProperties, ServerProperties serverProperties, SslBundles bundles) {
|
||||
super(sslProperties, serverProperties, bundles);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user