Commit a0ee0601 authored by Brian Clozel's avatar Brian Clozel

Fix SSL configuration with Reactor Netty

Prior to this commit, the SslServerCustomizer would use a Reactor Netty
API that lets users customize the SSL configuration, but later override
some of the choices with defaults.

This commits moves from the new deprecated Reactor Netty API and instead
uses a new variant that builds the defaults and lets developers override
them if they want to.

Fixes gh-25913
parent c6205f10
...@@ -20,10 +20,10 @@ import java.util.HashMap; ...@@ -20,10 +20,10 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.netty.http.Http11SslContextSpec;
import reactor.netty.http.client.HttpClient; import reactor.netty.http.client.HttpClient;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
...@@ -66,14 +66,13 @@ class ReactiveCloudFoundrySecurityService { ...@@ -66,14 +66,13 @@ class ReactiveCloudFoundrySecurityService {
} }
protected ReactorClientHttpConnector buildTrustAllSslConnector() { protected ReactorClientHttpConnector buildTrustAllSslConnector() {
HttpClient client = HttpClient.create() HttpClient client = HttpClient.create().secure((spec) -> spec.sslContext(createSslContextSpec()));
.secure((sslContextSpec) -> sslContextSpec.sslContext(createSslContext()));
return new ReactorClientHttpConnector(client); return new ReactorClientHttpConnector(client);
} }
private SslContextBuilder createSslContext() { private Http11SslContextSpec createSslContextSpec() {
return SslContextBuilder.forClient().sslProvider(SslProvider.JDK) return Http11SslContextSpec.forClient().configure(
.trustManager(InsecureTrustManagerFactory.INSTANCE); (builder) -> builder.sslProvider(SslProvider.JDK).trustManager(InsecureTrustManagerFactory.INSTANCE));
} }
/** /**
......
...@@ -1322,7 +1322,7 @@ bom { ...@@ -1322,7 +1322,7 @@ bom {
] ]
} }
} }
library("Reactor Bom", "2020.0.5") { library("Reactor Bom", "2020.0.6-SNAPSHOT") {
group("io.projectreactor") { group("io.projectreactor") {
imports = [ imports = [
"reactor-bom" "reactor-bom"
......
...@@ -31,6 +31,7 @@ import io.rsocket.transport.netty.server.TcpServerTransport; ...@@ -31,6 +31,7 @@ import io.rsocket.transport.netty.server.TcpServerTransport;
import io.rsocket.transport.netty.server.WebsocketServerTransport; import io.rsocket.transport.netty.server.WebsocketServerTransport;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.netty.http.server.HttpServer; import reactor.netty.http.server.HttpServer;
import reactor.netty.tcp.AbstractProtocolSslContextSpec;
import reactor.netty.tcp.TcpServer; import reactor.netty.tcp.TcpServer;
import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.context.properties.PropertyMapper;
...@@ -38,7 +39,6 @@ import org.springframework.boot.rsocket.server.ConfigurableRSocketServerFactory; ...@@ -38,7 +39,6 @@ import org.springframework.boot.rsocket.server.ConfigurableRSocketServerFactory;
import org.springframework.boot.rsocket.server.RSocketServer; import org.springframework.boot.rsocket.server.RSocketServer;
import org.springframework.boot.rsocket.server.RSocketServerCustomizer; import org.springframework.boot.rsocket.server.RSocketServerCustomizer;
import org.springframework.boot.rsocket.server.RSocketServerFactory; import org.springframework.boot.rsocket.server.RSocketServerFactory;
import org.springframework.boot.web.embedded.netty.SslServerCustomizer;
import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.SslStoreProvider; import org.springframework.boot.web.server.SslStoreProvider;
import org.springframework.http.client.reactive.ReactorResourceFactory; import org.springframework.http.client.reactive.ReactorResourceFactory;
...@@ -171,12 +171,18 @@ public class NettyRSocketServerFactory implements RSocketServerFactory, Configur ...@@ -171,12 +171,18 @@ public class NettyRSocketServerFactory implements RSocketServerFactory, Configur
httpServer = httpServer.runOn(this.resourceFactory.getLoopResources()); httpServer = httpServer.runOn(this.resourceFactory.getLoopResources());
} }
if (this.ssl != null && this.ssl.isEnabled()) { if (this.ssl != null && this.ssl.isEnabled()) {
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(this.ssl, null, this.sslStoreProvider); httpServer = customizeSslConfiguration(httpServer);
httpServer = sslServerCustomizer.apply(httpServer);
} }
return WebsocketServerTransport.create(httpServer.bindAddress(this::getListenAddress)); return WebsocketServerTransport.create(httpServer.bindAddress(this::getListenAddress));
} }
@SuppressWarnings("deprecation")
private HttpServer customizeSslConfiguration(HttpServer httpServer) {
org.springframework.boot.web.embedded.netty.SslServerCustomizer sslServerCustomizer = new org.springframework.boot.web.embedded.netty.SslServerCustomizer(
this.ssl, null, this.sslStoreProvider);
return sslServerCustomizer.apply(httpServer);
}
private ServerTransport<CloseableChannel> createTcpTransport() { private ServerTransport<CloseableChannel> createTcpTransport() {
TcpServer tcpServer = TcpServer.create(); TcpServer tcpServer = TcpServer.create();
if (this.resourceFactory != null) { if (this.resourceFactory != null) {
...@@ -196,19 +202,17 @@ public class NettyRSocketServerFactory implements RSocketServerFactory, Configur ...@@ -196,19 +202,17 @@ public class NettyRSocketServerFactory implements RSocketServerFactory, Configur
return new InetSocketAddress(this.port); return new InetSocketAddress(this.port);
} }
private static final class TcpSslServerCustomizer extends SslServerCustomizer { @SuppressWarnings("deprecation")
private static final class TcpSslServerCustomizer
extends org.springframework.boot.web.embedded.netty.SslServerCustomizer {
private TcpSslServerCustomizer(Ssl ssl, SslStoreProvider sslStoreProvider) { private TcpSslServerCustomizer(Ssl ssl, SslStoreProvider sslStoreProvider) {
super(ssl, null, sslStoreProvider); super(ssl, null, sslStoreProvider);
} }
private TcpServer apply(TcpServer server) { private TcpServer apply(TcpServer server) {
try { AbstractProtocolSslContextSpec<?> sslContextSpec = createSslContextSpec();
return server.secure((contextSpec) -> contextSpec.sslContext(getContextBuilder())); return server.secure((spec) -> spec.sslContext(sslContextSpec));
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
} }
} }
......
...@@ -166,9 +166,7 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact ...@@ -166,9 +166,7 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
server = server.bindAddress(this::getListenAddress); server = server.bindAddress(this::getListenAddress);
} }
if (getSsl() != null && getSsl().isEnabled()) { if (getSsl() != null && getSsl().isEnabled()) {
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(getSsl(), getHttp2(), server = customizeSslConfiguration(server);
getSslStoreProvider());
server = sslServerCustomizer.apply(server);
} }
if (getCompression() != null && getCompression().getEnabled()) { if (getCompression() != null && getCompression().getEnabled()) {
CompressionCustomizer compressionCustomizer = new CompressionCustomizer(getCompression()); CompressionCustomizer compressionCustomizer = new CompressionCustomizer(getCompression());
...@@ -178,6 +176,12 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact ...@@ -178,6 +176,12 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
return applyCustomizers(server); return applyCustomizers(server);
} }
@SuppressWarnings("deprecation")
private HttpServer customizeSslConfiguration(HttpServer httpServer) {
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(getSsl(), getHttp2(), getSslStoreProvider());
return sslServerCustomizer.apply(httpServer);
}
private HttpProtocol[] listProtocols() { private HttpProtocol[] listProtocols() {
if (getHttp2() != null && getHttp2().isEnabled() && getSsl() != null && getSsl().isEnabled()) { if (getHttp2() != null && getHttp2().isEnabled() && getSsl() != null && getSsl().isEnabled()) {
return new HttpProtocol[] { HttpProtocol.H2, HttpProtocol.HTTP11 }; return new HttpProtocol[] { HttpProtocol.H2, HttpProtocol.HTTP11 };
......
...@@ -38,9 +38,10 @@ import javax.net.ssl.TrustManagerFactory; ...@@ -38,9 +38,10 @@ import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedKeyManager;
import io.netty.handler.ssl.ClientAuth; import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContextBuilder; import reactor.netty.http.Http11SslContextSpec;
import reactor.netty.http.Http2SslContextSpec;
import reactor.netty.http.server.HttpServer; import reactor.netty.http.server.HttpServer;
import reactor.netty.tcp.SslProvider; import reactor.netty.tcp.AbstractProtocolSslContextSpec;
import org.springframework.boot.web.server.Http2; import org.springframework.boot.web.server.Http2;
import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.Ssl;
...@@ -57,7 +58,9 @@ import org.springframework.util.ResourceUtils; ...@@ -57,7 +58,9 @@ import org.springframework.util.ResourceUtils;
* @author Raheela Aslam * @author Raheela Aslam
* @author Chris Bono * @author Chris Bono
* @since 2.0.0 * @since 2.0.0
* @deprecated this class is meant for Spring Boot internal use only.
*/ */
@Deprecated
public class SslServerCustomizer implements NettyServerCustomizer { public class SslServerCustomizer implements NettyServerCustomizer {
private final Ssl ssl; private final Ssl ssl;
...@@ -74,38 +77,37 @@ public class SslServerCustomizer implements NettyServerCustomizer { ...@@ -74,38 +77,37 @@ public class SslServerCustomizer implements NettyServerCustomizer {
@Override @Override
public HttpServer apply(HttpServer server) { public HttpServer apply(HttpServer server) {
try { AbstractProtocolSslContextSpec<?> sslContextSpec = createSslContextSpec();
return server.secure((contextSpec) -> { return server.secure((spec) -> spec.sslContext(sslContextSpec));
SslProvider.DefaultConfigurationSpec spec = contextSpec.sslContext(getContextBuilder());
if (this.http2 != null && this.http2.isEnabled()) {
spec.defaultConfiguration(SslProvider.DefaultConfigurationType.H2);
}
});
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
} }
protected SslContextBuilder getContextBuilder() { protected AbstractProtocolSslContextSpec<?> createSslContextSpec() {
SslContextBuilder builder = SslContextBuilder.forServer(getKeyManagerFactory(this.ssl, this.sslStoreProvider)) AbstractProtocolSslContextSpec<?> sslContextSpec;
.trustManager(getTrustManagerFactory(this.ssl, this.sslStoreProvider)); if (this.http2 != null && this.http2.isEnabled()) {
if (this.ssl.getEnabledProtocols() != null) { sslContextSpec = Http2SslContextSpec.forServer(getKeyManagerFactory(this.ssl, this.sslStoreProvider));
builder.protocols(this.ssl.getEnabledProtocols());
}
if (this.ssl.getCiphers() != null) {
builder.ciphers(Arrays.asList(this.ssl.getCiphers()));
} }
if (this.ssl.getClientAuth() == Ssl.ClientAuth.NEED) { else {
builder.clientAuth(ClientAuth.REQUIRE); sslContextSpec = Http11SslContextSpec.forServer(getKeyManagerFactory(this.ssl, this.sslStoreProvider));
} }
else if (this.ssl.getClientAuth() == Ssl.ClientAuth.WANT) { sslContextSpec.configure((builder) -> {
builder.clientAuth(ClientAuth.OPTIONAL); builder.trustManager(getTrustManagerFactory(this.ssl, this.sslStoreProvider));
} if (this.ssl.getEnabledProtocols() != null) {
return builder; builder.protocols(this.ssl.getEnabledProtocols());
}
if (this.ssl.getCiphers() != null) {
builder.ciphers(Arrays.asList(this.ssl.getCiphers()));
}
if (this.ssl.getClientAuth() == Ssl.ClientAuth.NEED) {
builder.clientAuth(ClientAuth.REQUIRE);
}
else if (this.ssl.getClientAuth() == Ssl.ClientAuth.WANT) {
builder.clientAuth(ClientAuth.OPTIONAL);
}
});
return sslContextSpec;
} }
protected KeyManagerFactory getKeyManagerFactory(Ssl ssl, SslStoreProvider sslStoreProvider) { KeyManagerFactory getKeyManagerFactory(Ssl ssl, SslStoreProvider sslStoreProvider) {
try { try {
KeyStore keyStore = getKeyStore(ssl, sslStoreProvider); KeyStore keyStore = getKeyStore(ssl, sslStoreProvider);
SslConfigurationValidator.validateKeyAlias(keyStore, ssl.getKeyAlias()); SslConfigurationValidator.validateKeyAlias(keyStore, ssl.getKeyAlias());
...@@ -133,7 +135,7 @@ public class SslServerCustomizer implements NettyServerCustomizer { ...@@ -133,7 +135,7 @@ public class SslServerCustomizer implements NettyServerCustomizer {
ssl.getKeyStorePassword()); ssl.getKeyStorePassword());
} }
protected TrustManagerFactory getTrustManagerFactory(Ssl ssl, SslStoreProvider sslStoreProvider) { TrustManagerFactory getTrustManagerFactory(Ssl ssl, SslStoreProvider sslStoreProvider) {
try { try {
KeyStore store = getTrustStore(ssl, sslStoreProvider); KeyStore store = getTrustStore(ssl, sslStoreProvider);
TrustManagerFactory trustManagerFactory = TrustManagerFactory TrustManagerFactory trustManagerFactory = TrustManagerFactory
......
...@@ -22,7 +22,6 @@ import java.util.Arrays; ...@@ -22,7 +22,6 @@ import java.util.Arrays;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import io.netty.buffer.PooledByteBufAllocator; import io.netty.buffer.PooledByteBufAllocator;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.rsocket.ConnectionSetupPayload; import io.rsocket.ConnectionSetupPayload;
...@@ -37,6 +36,7 @@ import org.junit.jupiter.api.AfterEach; ...@@ -37,6 +36,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.InOrder; import org.mockito.InOrder;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.netty.http.Http11SslContextSpec;
import reactor.netty.http.client.HttpClient; import reactor.netty.http.client.HttpClient;
import reactor.netty.tcp.TcpClient; import reactor.netty.tcp.TcpClient;
import reactor.test.StepVerifier; import reactor.test.StepVerifier;
...@@ -224,9 +224,9 @@ class NettyRSocketServerFactoryTests { ...@@ -224,9 +224,9 @@ class NettyRSocketServerFactoryTests {
private HttpClient createSecureHttpClient() { private HttpClient createSecureHttpClient() {
HttpClient httpClient = createHttpClient(); HttpClient httpClient = createHttpClient();
SslContextBuilder builder = SslContextBuilder.forClient().sslProvider(SslProvider.JDK) Http11SslContextSpec sslContextSpec = Http11SslContextSpec.forClient().configure(
.trustManager(InsecureTrustManagerFactory.INSTANCE); (builder) -> builder.sslProvider(SslProvider.JDK).trustManager(InsecureTrustManagerFactory.INSTANCE));
return httpClient.secure((spec) -> spec.sslContext(builder)); return httpClient.secure((spec) -> spec.sslContext(sslContextSpec));
} }
private HttpClient createHttpClient() { private HttpClient createHttpClient() {
...@@ -237,9 +237,9 @@ class NettyRSocketServerFactoryTests { ...@@ -237,9 +237,9 @@ class NettyRSocketServerFactoryTests {
private TcpClient createSecureTcpClient() { private TcpClient createSecureTcpClient() {
TcpClient tcpClient = createTcpClient(); TcpClient tcpClient = createTcpClient();
SslContextBuilder builder = SslContextBuilder.forClient().sslProvider(SslProvider.JDK) Http11SslContextSpec sslContextSpec = Http11SslContextSpec.forClient().configure(
.trustManager(InsecureTrustManagerFactory.INSTANCE); (builder) -> builder.sslProvider(SslProvider.JDK).trustManager(InsecureTrustManagerFactory.INSTANCE));
return tcpClient.secure((spec) -> spec.sslContext(builder)); return tcpClient.secure((spec) -> spec.sslContext(sslContextSpec));
} }
private TcpClient createTcpClient() { private TcpClient createTcpClient() {
......
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -31,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; ...@@ -31,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Raheela Aslam * @author Raheela Aslam
*/ */
@SuppressWarnings("deprecation")
class SslServerCustomizerTests { class SslServerCustomizerTests {
@Test @Test
......
...@@ -37,7 +37,6 @@ import io.netty.channel.ChannelHandlerContext; ...@@ -37,7 +37,6 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
...@@ -47,6 +46,7 @@ import org.junit.jupiter.api.Test; ...@@ -47,6 +46,7 @@ import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks; import reactor.core.publisher.Sinks;
import reactor.netty.NettyPipeline; import reactor.netty.NettyPipeline;
import reactor.netty.http.Http11SslContextSpec;
import reactor.netty.http.client.HttpClient; import reactor.netty.http.client.HttpClient;
import reactor.test.StepVerifier; import reactor.test.StepVerifier;
...@@ -195,10 +195,9 @@ public abstract class AbstractReactiveWebServerFactoryTests { ...@@ -195,10 +195,9 @@ public abstract class AbstractReactiveWebServerFactoryTests {
} }
protected ReactorClientHttpConnector buildTrustAllSslConnector() { protected ReactorClientHttpConnector buildTrustAllSslConnector() {
SslContextBuilder builder = SslContextBuilder.forClient().sslProvider(SslProvider.JDK) Http11SslContextSpec sslContextSpec = Http11SslContextSpec.forClient().configure(
.trustManager(InsecureTrustManagerFactory.INSTANCE); (builder) -> builder.sslProvider(SslProvider.JDK).trustManager(InsecureTrustManagerFactory.INSTANCE));
HttpClient client = HttpClient.create().wiretap(true) HttpClient client = HttpClient.create().wiretap(true).secure((spec) -> spec.sslContext(sslContextSpec));
.secure((sslContextSpec) -> sslContextSpec.sslContext(builder));
return new ReactorClientHttpConnector(client); return new ReactorClientHttpConnector(client);
} }
...@@ -232,10 +231,11 @@ public abstract class AbstractReactiveWebServerFactoryTests { ...@@ -232,10 +231,11 @@ public abstract class AbstractReactiveWebServerFactoryTests {
KeyManagerFactory clientKeyManagerFactory = KeyManagerFactory KeyManagerFactory clientKeyManagerFactory = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm()); .getInstance(KeyManagerFactory.getDefaultAlgorithm());
clientKeyManagerFactory.init(clientKeyStore, "password".toCharArray()); clientKeyManagerFactory.init(clientKeyStore, "password".toCharArray());
SslContextBuilder builder = SslContextBuilder.forClient().sslProvider(SslProvider.JDK)
.trustManager(InsecureTrustManagerFactory.INSTANCE).keyManager(clientKeyManagerFactory); Http11SslContextSpec sslContextSpec = Http11SslContextSpec.forClient()
HttpClient client = HttpClient.create().wiretap(true) .configure((builder) -> builder.sslProvider(SslProvider.JDK)
.secure((sslContextSpec) -> sslContextSpec.sslContext(builder)); .trustManager(InsecureTrustManagerFactory.INSTANCE).keyManager(clientKeyManagerFactory));
HttpClient client = HttpClient.create().wiretap(true).secure((spec) -> spec.sslContext(sslContextSpec));
return new ReactorClientHttpConnector(client); return new ReactorClientHttpConnector(client);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment