diff --git a/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java b/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java index 50b82a484a..837ad2ab5e 100644 --- a/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java +++ b/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java @@ -305,7 +305,11 @@ public final class SdsProtocolNegotiators { return; } else { ctx.pipeline() - .replace(this, null, new ServerSdsHandler(grpcHandler, downstreamTlsContext)); + .replace( + this, + null, + new ServerSdsHandler( + grpcHandler, downstreamTlsContext, fallbackProtocolNegotiator)); ProtocolNegotiationEvent pne = InternalProtocolNegotiationEvent.getDefault(); ctx.fireUserEventTriggered(pne); return; @@ -321,10 +325,12 @@ public final class SdsProtocolNegotiators { extends InternalProtocolNegotiators.ProtocolNegotiationHandler { private final GrpcHttp2ConnectionHandler grpcHandler; private final DownstreamTlsContext downstreamTlsContext; + @Nullable private final ProtocolNegotiator fallbackProtocolNegotiator; ServerSdsHandler( GrpcHttp2ConnectionHandler grpcHandler, - DownstreamTlsContext downstreamTlsContext) { + DownstreamTlsContext downstreamTlsContext, + ProtocolNegotiator fallbackProtocolNegotiator) { super( // superclass (InternalProtocolNegotiators.ProtocolNegotiationHandler) expects 'next' // handler but we don't have a next handler _yet_. So we "disable" superclass's behavior @@ -338,6 +344,7 @@ public final class SdsProtocolNegotiators { checkNotNull(grpcHandler, "grpcHandler"); this.grpcHandler = grpcHandler; this.downstreamTlsContext = downstreamTlsContext; + this.fallbackProtocolNegotiator = fallbackProtocolNegotiator; } @Override @@ -345,10 +352,23 @@ public final class SdsProtocolNegotiators { final BufferReadsHandler bufferReads = new BufferReadsHandler(); ctx.pipeline().addBefore(ctx.name(), null, bufferReads); - final SslContextProvider sslContextProvider = - TlsContextManagerImpl.getInstance() - .findOrCreateServerSslContextProvider(downstreamTlsContext); - + SslContextProvider sslContextProviderTemp = null; + try { + sslContextProviderTemp = + TlsContextManagerImpl.getInstance() + .findOrCreateServerSslContextProvider(downstreamTlsContext); + } catch (Exception e) { + if (fallbackProtocolNegotiator == null) { + ctx.fireExceptionCaught(new CertStoreException("No certificate source found!", e)); + return; + } + logger.log(Level.INFO, "Using fallback for {0}", ctx.channel().localAddress()); + // Delegate rest of handshake to fallback handler + ctx.pipeline().replace(this, null, fallbackProtocolNegotiator.newHandler(grpcHandler)); + ctx.pipeline().remove(bufferReads); + return; + } + final SslContextProvider sslContextProvider = sslContextProviderTemp; sslContextProvider.addCallback( new SslContextProvider.Callback() { diff --git a/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java b/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java index 8ab5c5fa04..0d58a38a7f 100644 --- a/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java @@ -89,6 +89,18 @@ public class XdsSdsClientServerTest { assertThat(unaryRpc("buddy", blockingStub)).isEqualTo("Hello buddy"); } + @Test + public void plaintextClientServer_withDefaultTlsContext() throws IOException, URISyntaxException { + DownstreamTlsContext defaultTlsContext = + EnvoyServerProtoData.DownstreamTlsContext.fromEnvoyProtoDownstreamTlsContext( + io.envoyproxy.envoy.api.v2.auth.DownstreamTlsContext.getDefaultInstance()); + buildServerWithTlsContext(/* downstreamTlsContext= */ defaultTlsContext); + + SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub = + getBlockingStub(/* upstreamTlsContext= */ null, /* overrideAuthority= */ null); + assertThat(unaryRpc("buddy", blockingStub)).isEqualTo("Hello buddy"); + } + @Test public void nullFallbackProtocolNegotiator_expectException() throws IOException, URISyntaxException { diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java index 9ecc63e22b..0744712156 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java @@ -245,6 +245,46 @@ public class SdsProtocolNegotiatorsTest { .contains("ProtocolNegotiators.ServerTlsHandler"); } + @Test + public void serverSdsHandler_defaultDownstreamTlsContext_expectFallbackProtocolNegotiator() + throws IOException { + ChannelHandler mockChannelHandler = mock(ChannelHandler.class); + ProtocolNegotiator mockProtocolNegotiator = mock(ProtocolNegotiator.class); + when(mockProtocolNegotiator.newHandler(grpcHandler)).thenReturn(mockChannelHandler); + // we need InetSocketAddress instead of EmbeddedSocketAddress as localAddress for this test + channel = + new EmbeddedChannel() { + @Override + public SocketAddress localAddress() { + return new InetSocketAddress("172.168.1.1", 80); + } + }; + pipeline = channel.pipeline(); + DownstreamTlsContext downstreamTlsContext = + DownstreamTlsContext.fromEnvoyProtoDownstreamTlsContext( + io.envoyproxy.envoy.api.v2.auth.DownstreamTlsContext.getDefaultInstance()); + + XdsClientWrapperForServerSds xdsClientWrapperForServerSds = + XdsClientWrapperForServerSdsTest.createXdsClientWrapperForServerSds( + 80, downstreamTlsContext); + SdsProtocolNegotiators.HandlerPickerHandler handlerPickerHandler = + new SdsProtocolNegotiators.HandlerPickerHandler( + grpcHandler, xdsClientWrapperForServerSds, mockProtocolNegotiator); + pipeline.addLast(handlerPickerHandler); + channelHandlerCtx = pipeline.context(handlerPickerHandler); + assertThat(channelHandlerCtx).isNotNull(); // should find HandlerPickerHandler + + // kick off protocol negotiation: should replace HandlerPickerHandler with ServerSdsHandler + pipeline.fireUserEventTriggered(InternalProtocolNegotiationEvent.getDefault()); + channelHandlerCtx = pipeline.context(handlerPickerHandler); + assertThat(channelHandlerCtx).isNull(); + channel.runPendingTasks(); // need this for tasks to execute on eventLoop + Iterator> iterator = pipeline.iterator(); + assertThat(iterator.next().getValue()).isSameInstanceAs(mockChannelHandler); + // no more handlers in the pipeline + assertThat(iterator.hasNext()).isFalse(); + } + @Test public void serverSdsHandler_nullTlsContext_expectFallbackProtocolNegotiator() { ChannelHandler mockChannelHandler = mock(ChannelHandler.class);