mirror of https://github.com/grpc/grpc-java.git
netty: fix server and client handlers to check the correct alpn list (#6603)
This commit is contained in:
parent
5b726c07fe
commit
461d30adfb
|
@ -56,7 +56,7 @@ public class GrpcSslContexts {
|
|||
/*
|
||||
* List of ALPN/NPN protocols in order of preference.
|
||||
*/
|
||||
static final List<String> NEXT_PROTOCOL_VERSIONS =
|
||||
private static final List<String> NEXT_PROTOCOL_VERSIONS =
|
||||
Collections.unmodifiableList(Arrays.asList(HTTP2_VERSION));
|
||||
|
||||
/*
|
||||
|
|
|
@ -18,7 +18,6 @@ package io.grpc.netty;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static io.grpc.netty.GrpcSslContexts.NEXT_PROTOCOL_VERSIONS;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
@ -190,7 +189,8 @@ final class ProtocolNegotiators {
|
|||
return;
|
||||
}
|
||||
SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
|
||||
if (!NEXT_PROTOCOL_VERSIONS.contains(sslHandler.applicationProtocol())) {
|
||||
if (!sslContext.applicationProtocolNegotiator().protocols().contains(
|
||||
sslHandler.applicationProtocol())) {
|
||||
logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed for new client.", null);
|
||||
ctx.fireExceptionCaught(unavailableException(
|
||||
"Failed protocol negotiation: Unable to find compatible protocol"));
|
||||
|
@ -359,7 +359,8 @@ final class ProtocolNegotiators {
|
|||
SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
|
||||
if (handshakeEvent.isSuccess()) {
|
||||
SslHandler handler = ctx.pipeline().get(SslHandler.class);
|
||||
if (NEXT_PROTOCOL_VERSIONS.contains(handler.applicationProtocol())) {
|
||||
if (sslContext.applicationProtocolNegotiator().protocols()
|
||||
.contains(handler.applicationProtocol())) {
|
||||
// Successfully negotiated the protocol.
|
||||
logSslEngineDetails(Level.FINER, ctx, "TLS negotiation succeeded.", null);
|
||||
propagateTlsComplete(ctx, handler.engine().getSession());
|
||||
|
|
|
@ -35,6 +35,7 @@ import io.grpc.InternalChannelz.Security;
|
|||
import io.grpc.SecurityLevel;
|
||||
import io.grpc.internal.GrpcAttributes;
|
||||
import io.grpc.internal.testing.TestUtils;
|
||||
import io.grpc.netty.ProtocolNegotiators.ClientTlsHandler;
|
||||
import io.grpc.netty.ProtocolNegotiators.ClientTlsProtocolNegotiator;
|
||||
import io.grpc.netty.ProtocolNegotiators.HostPort;
|
||||
import io.grpc.netty.ProtocolNegotiators.ServerTlsHandler;
|
||||
|
@ -76,6 +77,7 @@ import io.netty.handler.codec.http2.Http2ConnectionEncoder;
|
|||
import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
|
||||
import io.netty.handler.codec.http2.Http2Settings;
|
||||
import io.netty.handler.proxy.ProxyConnectException;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
|
@ -85,6 +87,8 @@ import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
@ -349,6 +353,186 @@ public class ProtocolNegotiatorsTest {
|
|||
assertNotNull(grpcHandlerCtx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serverTlsHandler_userEventTriggeredSslEvent_supportedProtocolCustom()
|
||||
throws Exception {
|
||||
SslHandler goodSslHandler = new SslHandler(engine, false) {
|
||||
@Override
|
||||
public String applicationProtocol() {
|
||||
return "managed_mtls";
|
||||
}
|
||||
};
|
||||
|
||||
File serverCert = TestUtils.loadCert("server1.pem");
|
||||
File key = TestUtils.loadCert("server1.key");
|
||||
List<String> alpnList = Arrays.asList("managed_mtls", "h2");
|
||||
ApplicationProtocolConfig apn = new ApplicationProtocolConfig(
|
||||
ApplicationProtocolConfig.Protocol.ALPN,
|
||||
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
|
||||
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
|
||||
alpnList);
|
||||
|
||||
sslContext = GrpcSslContexts.forServer(serverCert, key)
|
||||
.ciphers(TestUtils.preferredTestCiphers(), SupportedCipherSuiteFilter.INSTANCE)
|
||||
.applicationProtocolConfig(apn).build();
|
||||
|
||||
ChannelHandler handler = new ServerTlsHandler(grpcHandler, sslContext, null);
|
||||
pipeline.addLast(handler);
|
||||
|
||||
pipeline.replace(SslHandler.class, null, goodSslHandler);
|
||||
channelHandlerCtx = pipeline.context(handler);
|
||||
Object sslEvent = SslHandshakeCompletionEvent.SUCCESS;
|
||||
|
||||
pipeline.fireUserEventTriggered(sslEvent);
|
||||
|
||||
assertTrue(channel.isOpen());
|
||||
ChannelHandlerContext grpcHandlerCtx = pipeline.context(grpcHandler);
|
||||
assertNotNull(grpcHandlerCtx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serverTlsHandler_userEventTriggeredSslEvent_unsupportedProtocolCustom()
|
||||
throws Exception {
|
||||
SslHandler badSslHandler = new SslHandler(engine, false) {
|
||||
@Override
|
||||
public String applicationProtocol() {
|
||||
return "badprotocol";
|
||||
}
|
||||
};
|
||||
|
||||
File serverCert = TestUtils.loadCert("server1.pem");
|
||||
File key = TestUtils.loadCert("server1.key");
|
||||
List<String> alpnList = Arrays.asList("managed_mtls", "h2");
|
||||
ApplicationProtocolConfig apn = new ApplicationProtocolConfig(
|
||||
ApplicationProtocolConfig.Protocol.ALPN,
|
||||
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
|
||||
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
|
||||
alpnList);
|
||||
|
||||
sslContext = GrpcSslContexts.forServer(serverCert, key)
|
||||
.ciphers(TestUtils.preferredTestCiphers(), SupportedCipherSuiteFilter.INSTANCE)
|
||||
.applicationProtocolConfig(apn).build();
|
||||
ChannelHandler handler = new ServerTlsHandler(grpcHandler, sslContext, null);
|
||||
pipeline.addLast(handler);
|
||||
|
||||
final AtomicReference<Throwable> error = new AtomicReference<>();
|
||||
ChannelHandler errorCapture = new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
error.set(cause);
|
||||
}
|
||||
};
|
||||
|
||||
pipeline.addLast(errorCapture);
|
||||
|
||||
pipeline.replace(SslHandler.class, null, badSslHandler);
|
||||
channelHandlerCtx = pipeline.context(handler);
|
||||
Object sslEvent = SslHandshakeCompletionEvent.SUCCESS;
|
||||
|
||||
pipeline.fireUserEventTriggered(sslEvent);
|
||||
|
||||
// No h2 protocol was specified, so there should be an error, (normally handled by WBAEH)
|
||||
assertThat(error.get()).hasMessageThat().contains("Unable to find compatible protocol");
|
||||
ChannelHandlerContext grpcHandlerCtx = pipeline.context(grpcHandler);
|
||||
assertNull(grpcHandlerCtx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientTlsHandler_userEventTriggeredSslEvent_supportedProtocolH2() throws Exception {
|
||||
SslHandler goodSslHandler = new SslHandler(engine, false) {
|
||||
@Override
|
||||
public String applicationProtocol() {
|
||||
return "h2";
|
||||
}
|
||||
};
|
||||
DefaultEventLoopGroup elg = new DefaultEventLoopGroup(1);
|
||||
|
||||
ClientTlsHandler handler = new ClientTlsHandler(grpcHandler, sslContext, "authority", elg);
|
||||
pipeline.addLast(handler);
|
||||
pipeline.replace(SslHandler.class, null, goodSslHandler);
|
||||
pipeline.fireUserEventTriggered(ProtocolNegotiationEvent.DEFAULT);
|
||||
channelHandlerCtx = pipeline.context(handler);
|
||||
Object sslEvent = SslHandshakeCompletionEvent.SUCCESS;
|
||||
|
||||
pipeline.fireUserEventTriggered(sslEvent);
|
||||
|
||||
ChannelHandlerContext grpcHandlerCtx = pipeline.context(grpcHandler);
|
||||
assertNotNull(grpcHandlerCtx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientTlsHandler_userEventTriggeredSslEvent_supportedProtocolCustom()
|
||||
throws Exception {
|
||||
SslHandler goodSslHandler = new SslHandler(engine, false) {
|
||||
@Override
|
||||
public String applicationProtocol() {
|
||||
return "managed_mtls";
|
||||
}
|
||||
};
|
||||
DefaultEventLoopGroup elg = new DefaultEventLoopGroup(1);
|
||||
|
||||
File clientCert = TestUtils.loadCert("client.pem");
|
||||
File key = TestUtils.loadCert("client.key");
|
||||
List<String> alpnList = Arrays.asList("managed_mtls", "h2");
|
||||
ApplicationProtocolConfig apn = new ApplicationProtocolConfig(
|
||||
ApplicationProtocolConfig.Protocol.ALPN,
|
||||
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
|
||||
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
|
||||
alpnList);
|
||||
|
||||
sslContext = GrpcSslContexts.forClient()
|
||||
.keyManager(clientCert, key)
|
||||
.ciphers(TestUtils.preferredTestCiphers(), SupportedCipherSuiteFilter.INSTANCE)
|
||||
.applicationProtocolConfig(apn).build();
|
||||
|
||||
ClientTlsHandler handler = new ClientTlsHandler(grpcHandler, sslContext, "authority", elg);
|
||||
pipeline.addLast(handler);
|
||||
pipeline.replace(SslHandler.class, null, goodSslHandler);
|
||||
pipeline.fireUserEventTriggered(ProtocolNegotiationEvent.DEFAULT);
|
||||
channelHandlerCtx = pipeline.context(handler);
|
||||
Object sslEvent = SslHandshakeCompletionEvent.SUCCESS;
|
||||
|
||||
pipeline.fireUserEventTriggered(sslEvent);
|
||||
|
||||
ChannelHandlerContext grpcHandlerCtx = pipeline.context(grpcHandler);
|
||||
assertNotNull(grpcHandlerCtx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientTlsHandler_userEventTriggeredSslEvent_unsupportedProtocol() throws Exception {
|
||||
SslHandler goodSslHandler = new SslHandler(engine, false) {
|
||||
@Override
|
||||
public String applicationProtocol() {
|
||||
return "badproto";
|
||||
}
|
||||
};
|
||||
DefaultEventLoopGroup elg = new DefaultEventLoopGroup(1);
|
||||
|
||||
ClientTlsHandler handler = new ClientTlsHandler(grpcHandler, sslContext, "authority", elg);
|
||||
pipeline.addLast(handler);
|
||||
|
||||
final AtomicReference<Throwable> error = new AtomicReference<>();
|
||||
ChannelHandler errorCapture = new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
error.set(cause);
|
||||
}
|
||||
};
|
||||
|
||||
pipeline.addLast(errorCapture);
|
||||
pipeline.replace(SslHandler.class, null, goodSslHandler);
|
||||
pipeline.fireUserEventTriggered(ProtocolNegotiationEvent.DEFAULT);
|
||||
channelHandlerCtx = pipeline.context(handler);
|
||||
Object sslEvent = SslHandshakeCompletionEvent.SUCCESS;
|
||||
|
||||
pipeline.fireUserEventTriggered(sslEvent);
|
||||
|
||||
// Bad protocol was specified, so there should be an error, (normally handled by WBAEH)
|
||||
assertThat(error.get()).hasMessageThat().contains("Unable to find compatible protocol");
|
||||
ChannelHandlerContext grpcHandlerCtx = pipeline.context(grpcHandler);
|
||||
assertNull(grpcHandlerCtx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void engineLog() {
|
||||
ChannelHandler handler = new ServerTlsHandler(grpcHandler, sslContext, null);
|
||||
|
|
Loading…
Reference in New Issue