From ee9109eceddd57db955ec1bff803c918e8ce7b75 Mon Sep 17 00:00:00 2001 From: sanjaypujare Date: Mon, 17 Aug 2020 17:06:29 -0700 Subject: [PATCH] xds: add CertProviderServerSslContextProvider support (#7331) --- .../CertProviderServerSslContextProvider.java | 126 ++++++++ .../sds/SdsServerSslContextProvider.java | 7 +- .../SecretVolumeServerSslContextProvider.java | 4 +- .../xds/internal/sds/SslContextProvider.java | 7 +- ...tProviderClientSslContextProviderTest.java | 3 +- ...tProviderServerSslContextProviderTest.java | 278 ++++++++++++++++++ .../sds/CommonTlsContextTestsUtil.java | 21 ++ 7 files changed, 439 insertions(+), 7 deletions(-) create mode 100644 xds/src/main/java/io/grpc/xds/internal/certprovider/CertProviderServerSslContextProvider.java create mode 100644 xds/src/test/java/io/grpc/xds/internal/certprovider/CertProviderServerSslContextProviderTest.java diff --git a/xds/src/main/java/io/grpc/xds/internal/certprovider/CertProviderServerSslContextProvider.java b/xds/src/main/java/io/grpc/xds/internal/certprovider/CertProviderServerSslContextProvider.java new file mode 100644 index 0000000000..5d1a6399b8 --- /dev/null +++ b/xds/src/main/java/io/grpc/xds/internal/certprovider/CertProviderServerSslContextProvider.java @@ -0,0 +1,126 @@ +/* + * Copyright 2020 The gRPC 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 io.grpc.xds.internal.certprovider; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.VisibleForTesting; +import io.envoyproxy.envoy.config.core.v3.Node; +import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext; +import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext; +import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext.CombinedCertificateValidationContext; +import io.grpc.netty.GrpcSslContexts; +import io.grpc.xds.Bootstrapper.CertificateProviderInfo; +import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext; +import io.grpc.xds.internal.sds.trust.SdsTrustManagerFactory; +import io.netty.handler.ssl.SslContextBuilder; + +import java.io.IOException; +import java.security.cert.CertStoreException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Map; + +/** A server SslContext provider using CertificateProviderInstance to fetch secrets. */ +final class CertProviderServerSslContextProvider extends CertProviderSslContextProvider { + + private CertProviderServerSslContextProvider( + Node node, + Map certProviders, + CommonTlsContext.CertificateProviderInstance certInstance, + CommonTlsContext.CertificateProviderInstance rootCertInstance, + CertificateValidationContext staticCertValidationContext, + DownstreamTlsContext downstreamTlsContext, + CertificateProviderStore certificateProviderStore) { + super( + node, + certProviders, + checkNotNull(certInstance, "Server SSL requires certInstance"), + rootCertInstance, + staticCertValidationContext, + downstreamTlsContext, + certificateProviderStore); + } + + @Override + protected final SslContextBuilder getSslContextBuilder( + CertificateValidationContext certificateValidationContextdationContext) + throws CertStoreException, CertificateException, IOException { + SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(savedKey, savedCertChain); + setClientAuthValues( + sslContextBuilder, + isMtls() + ? new SdsTrustManagerFactory( + savedTrustedRoots.toArray(new X509Certificate[0]), + certificateValidationContextdationContext) + : null); + sslContextBuilder = GrpcSslContexts.configure(sslContextBuilder); + return sslContextBuilder; + } + + /** Creates CertProviderServerSslContextProvider. */ + static final class Factory { + private static final Factory DEFAULT_INSTANCE = + new Factory(CertificateProviderStore.getInstance()); + private final CertificateProviderStore certificateProviderStore; + + @VisibleForTesting Factory(CertificateProviderStore certificateProviderStore) { + this.certificateProviderStore = certificateProviderStore; + } + + static Factory getInstance() { + return DEFAULT_INSTANCE; + } + + CertProviderServerSslContextProvider getProvider( + DownstreamTlsContext downstreamTlsContext, + Node node, + Map certProviders) { + checkNotNull(downstreamTlsContext, "downstreamTlsContext"); + CommonTlsContext commonTlsContext = downstreamTlsContext.getCommonTlsContext(); + CommonTlsContext.CertificateProviderInstance rootCertInstance = null; + CertificateValidationContext staticCertValidationContext = null; + if (commonTlsContext.hasCombinedValidationContext()) { + CombinedCertificateValidationContext combinedValidationContext = + commonTlsContext.getCombinedValidationContext(); + if (combinedValidationContext.hasValidationContextCertificateProviderInstance()) { + rootCertInstance = + combinedValidationContext.getValidationContextCertificateProviderInstance(); + } + if (combinedValidationContext.hasDefaultValidationContext()) { + staticCertValidationContext = combinedValidationContext.getDefaultValidationContext(); + } + } else if (commonTlsContext.hasValidationContextCertificateProviderInstance()) { + rootCertInstance = commonTlsContext.getValidationContextCertificateProviderInstance(); + } else if (commonTlsContext.hasValidationContext()) { + staticCertValidationContext = commonTlsContext.getValidationContext(); + } + CommonTlsContext.CertificateProviderInstance certInstance = null; + if (commonTlsContext.hasTlsCertificateCertificateProviderInstance()) { + certInstance = commonTlsContext.getTlsCertificateCertificateProviderInstance(); + } + return new CertProviderServerSslContextProvider( + node, + certProviders, + certInstance, + rootCertInstance, + staticCertValidationContext, + downstreamTlsContext, + certificateProviderStore); + } + } +} diff --git a/xds/src/main/java/io/grpc/xds/internal/sds/SdsServerSslContextProvider.java b/xds/src/main/java/io/grpc/xds/internal/sds/SdsServerSslContextProvider.java index 27afaa455e..649309dc67 100644 --- a/xds/src/main/java/io/grpc/xds/internal/sds/SdsServerSslContextProvider.java +++ b/xds/src/main/java/io/grpc/xds/internal/sds/SdsServerSslContextProvider.java @@ -24,6 +24,7 @@ import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext; import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.SdsSecretConfig; import io.grpc.netty.GrpcSslContexts; import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext; +import io.grpc.xds.internal.sds.trust.SdsTrustManagerFactory; import io.netty.handler.ssl.SslContextBuilder; import java.io.IOException; import java.security.cert.CertStoreException; @@ -85,7 +86,11 @@ final class SdsServerSslContextProvider extends SdsSslContextProvider { tlsCertificate.hasPassword() ? tlsCertificate.getPassword().getInlineString() : null); - setClientAuthValues(sslContextBuilder, localCertValidationContext); + setClientAuthValues( + sslContextBuilder, + localCertValidationContext != null + ? new SdsTrustManagerFactory(localCertValidationContext) + : null); return sslContextBuilder; } } diff --git a/xds/src/main/java/io/grpc/xds/internal/sds/SecretVolumeServerSslContextProvider.java b/xds/src/main/java/io/grpc/xds/internal/sds/SecretVolumeServerSslContextProvider.java index 3282fc555c..943b385c6a 100644 --- a/xds/src/main/java/io/grpc/xds/internal/sds/SecretVolumeServerSslContextProvider.java +++ b/xds/src/main/java/io/grpc/xds/internal/sds/SecretVolumeServerSslContextProvider.java @@ -27,6 +27,7 @@ import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext; import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.TlsCertificate; import io.grpc.netty.GrpcSslContexts; import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext; +import io.grpc.xds.internal.sds.trust.SdsTrustManagerFactory; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import java.io.File; @@ -108,7 +109,8 @@ final class SecretVolumeServerSslContextProvider extends SslContextProvider { SslContextBuilder sslContextBuilder = GrpcSslContexts.forServer( new File(certificateChain), new File(privateKey), privateKeyPassword); - setClientAuthValues(sslContextBuilder, certContext); + setClientAuthValues( + sslContextBuilder, certContext != null ? new SdsTrustManagerFactory(certContext) : null); return sslContextBuilder.build(); } } diff --git a/xds/src/main/java/io/grpc/xds/internal/sds/SslContextProvider.java b/xds/src/main/java/io/grpc/xds/internal/sds/SslContextProvider.java index 08e93d3a4e..84060f45ab 100644 --- a/xds/src/main/java/io/grpc/xds/internal/sds/SslContextProvider.java +++ b/xds/src/main/java/io/grpc/xds/internal/sds/SslContextProvider.java @@ -19,7 +19,6 @@ package io.grpc.xds.internal.sds; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext; import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext; import io.grpc.xds.EnvoyServerProtoData.BaseTlsContext; import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext; @@ -70,11 +69,11 @@ public abstract class SslContextProvider implements Closeable { } protected void setClientAuthValues( - SslContextBuilder sslContextBuilder, CertificateValidationContext localCertValidationContext) + SslContextBuilder sslContextBuilder, SdsTrustManagerFactory sdsTrustManagerFactory) throws CertificateException, IOException, CertStoreException { DownstreamTlsContext downstreamTlsContext = getDownstreamTlsContext(); - if (localCertValidationContext != null) { - sslContextBuilder.trustManager(new SdsTrustManagerFactory(localCertValidationContext)); + if (sdsTrustManagerFactory != null) { + sslContextBuilder.trustManager(sdsTrustManagerFactory); sslContextBuilder.clientAuth( downstreamTlsContext.isRequireClientCertificate() ? ClientAuth.REQUIRE diff --git a/xds/src/test/java/io/grpc/xds/internal/certprovider/CertProviderClientSslContextProviderTest.java b/xds/src/test/java/io/grpc/xds/internal/certprovider/CertProviderClientSslContextProviderTest.java index 6e9ae15ee5..f7d8d69bfa 100644 --- a/xds/src/test/java/io/grpc/xds/internal/certprovider/CertProviderClientSslContextProviderTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/certprovider/CertProviderClientSslContextProviderTest.java @@ -28,6 +28,7 @@ import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FI import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.doChecksOnSslContext; import static org.junit.Assert.fail; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.MoreExecutors; import io.envoyproxy.envoy.config.core.v3.DataSource; @@ -270,7 +271,7 @@ public class CertProviderClientSslContextProviderTest { static class QueuedExecutor implements Executor { /** A list of Runnables to be run in order. */ - private final Queue runQueue = new ConcurrentLinkedQueue<>(); + @VisibleForTesting final Queue runQueue = new ConcurrentLinkedQueue<>(); @Override public synchronized void execute(Runnable r) { diff --git a/xds/src/test/java/io/grpc/xds/internal/certprovider/CertProviderServerSslContextProviderTest.java b/xds/src/test/java/io/grpc/xds/internal/certprovider/CertProviderServerSslContextProviderTest.java new file mode 100644 index 0000000000..3475c6e151 --- /dev/null +++ b/xds/src/test/java/io/grpc/xds/internal/certprovider/CertProviderServerSslContextProviderTest.java @@ -0,0 +1,278 @@ +/* + * Copyright 2020 The gRPC 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 io.grpc.xds.internal.certprovider; + +import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.certprovider.CertProviderClientSslContextProviderTest.QueuedExecutor; +import static io.grpc.xds.internal.certprovider.CommonCertProviderTestUtils.getCertFromResourceName; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.doChecksOnSslContext; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.MoreExecutors; +import io.envoyproxy.envoy.config.core.v3.DataSource; +import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext; +import io.grpc.xds.Bootstrapper; +import io.grpc.xds.EnvoyServerProtoData; +import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil; +import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.TestCallback; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link CertProviderServerSslContextProvider}. */ +@RunWith(JUnit4.class) +public class CertProviderServerSslContextProviderTest { + + CertificateProviderRegistry certificateProviderRegistry; + CertificateProviderStore certificateProviderStore; + private CertProviderServerSslContextProvider.Factory certProviderServerSslContextProviderFactory; + + @Before + public void setUp() throws Exception { + certificateProviderRegistry = new CertificateProviderRegistry(); + certificateProviderStore = new CertificateProviderStore(certificateProviderRegistry); + certProviderServerSslContextProviderFactory = + new CertProviderServerSslContextProvider.Factory(certificateProviderStore); + } + + /** Helper method to build CertProviderServerSslContextProvider. */ + private CertProviderServerSslContextProvider getSslContextProvider( + String certInstanceName, + String rootInstanceName, + Bootstrapper.BootstrapInfo bootstrapInfo, + Iterable alpnProtocols, + CertificateValidationContext staticCertValidationContext, + boolean requireClientCert) { + EnvoyServerProtoData.DownstreamTlsContext downstreamTlsContext = + CommonTlsContextTestsUtil.buildDownstreamTlsContextForCertProviderInstance( + certInstanceName, + "cert-default", + rootInstanceName, + "root-default", + alpnProtocols, + staticCertValidationContext, + requireClientCert); + return certProviderServerSslContextProviderFactory.getProvider( + downstreamTlsContext, + bootstrapInfo.getNode().toEnvoyProtoNode(), + bootstrapInfo.getCertProviders()); + } + + @Test + public void testProviderForServer_mtls() throws Exception { + final CertificateProvider.DistributorWatcher[] watcherCaptor = + new CertificateProvider.DistributorWatcher[1]; + TestCertificateProvider.createAndRegisterProviderProvider( + certificateProviderRegistry, watcherCaptor, "testca", 0); + CertProviderServerSslContextProvider provider = + getSslContextProvider( + "gcp_id", + "gcp_id", + CommonCertProviderTestUtils.getTestBootstrapInfo(), + /* alpnProtocols= */ null, + /* staticCertValidationContext= */ null, + /* requireClientCert= */ true); + + assertThat(provider.savedKey).isNull(); + assertThat(provider.savedCertChain).isNull(); + assertThat(provider.savedTrustedRoots).isNull(); + assertThat(provider.getSslContext()).isNull(); + + // now generate cert update + watcherCaptor[0].updateCertificate( + CommonCertProviderTestUtils.getPrivateKey(SERVER_0_KEY_FILE), + ImmutableList.of(getCertFromResourceName(SERVER_0_PEM_FILE))); + assertThat(provider.savedKey).isNotNull(); + assertThat(provider.savedCertChain).isNotNull(); + assertThat(provider.getSslContext()).isNull(); + + // now generate root cert update + watcherCaptor[0].updateTrustedRoots(ImmutableList.of(getCertFromResourceName(CA_PEM_FILE))); + assertThat(provider.getSslContext()).isNotNull(); + assertThat(provider.savedKey).isNull(); + assertThat(provider.savedCertChain).isNull(); + assertThat(provider.savedTrustedRoots).isNull(); + + TestCallback testCallback = + CommonTlsContextTestsUtil.getValueThruCallback(provider); + + doChecksOnSslContext(true, testCallback.updatedSslContext, /* expectedApnProtos= */ null); + TestCallback testCallback1 = + CommonTlsContextTestsUtil.getValueThruCallback(provider); + assertThat(testCallback1.updatedSslContext).isSameInstanceAs(testCallback.updatedSslContext); + + // just do root cert update: sslContext should still be the same + watcherCaptor[0].updateTrustedRoots( + ImmutableList.of(getCertFromResourceName(CLIENT_PEM_FILE))); + assertThat(provider.savedKey).isNull(); + assertThat(provider.savedCertChain).isNull(); + assertThat(provider.savedTrustedRoots).isNotNull(); + testCallback1 = CommonTlsContextTestsUtil.getValueThruCallback(provider); + assertThat(testCallback1.updatedSslContext).isSameInstanceAs(testCallback.updatedSslContext); + + // now update id cert: sslContext should be updated i.e.different from the previous one + watcherCaptor[0].updateCertificate( + CommonCertProviderTestUtils.getPrivateKey(SERVER_1_KEY_FILE), + ImmutableList.of(getCertFromResourceName(SERVER_1_PEM_FILE))); + assertThat(provider.savedKey).isNull(); + assertThat(provider.savedCertChain).isNull(); + assertThat(provider.savedTrustedRoots).isNull(); + assertThat(provider.getSslContext()).isNotNull(); + testCallback1 = CommonTlsContextTestsUtil.getValueThruCallback(provider); + assertThat(testCallback1.updatedSslContext).isNotSameInstanceAs(testCallback.updatedSslContext); + } + + @Test + public void testProviderForServer_queueExecutor() throws Exception { + final CertificateProvider.DistributorWatcher[] watcherCaptor = + new CertificateProvider.DistributorWatcher[1]; + TestCertificateProvider.createAndRegisterProviderProvider( + certificateProviderRegistry, watcherCaptor, "testca", 0); + CertProviderServerSslContextProvider provider = + getSslContextProvider( + "gcp_id", + "gcp_id", + CommonCertProviderTestUtils.getTestBootstrapInfo(), + /* alpnProtocols= */ null, + /* staticCertValidationContext= */ null, + /* requireClientCert= */ true); + QueuedExecutor queuedExecutor = new QueuedExecutor(); + + TestCallback testCallback = + CommonTlsContextTestsUtil.getValueThruCallback(provider, queuedExecutor); + assertThat(queuedExecutor.runQueue).isEmpty(); + + // now generate cert update + watcherCaptor[0].updateCertificate( + CommonCertProviderTestUtils.getPrivateKey(SERVER_0_KEY_FILE), + ImmutableList.of(getCertFromResourceName(SERVER_0_PEM_FILE))); + assertThat(queuedExecutor.runQueue).isEmpty(); // still empty + + // now generate root cert update + watcherCaptor[0].updateTrustedRoots(ImmutableList.of(getCertFromResourceName(CA_PEM_FILE))); + assertThat(queuedExecutor.runQueue).hasSize(1); + queuedExecutor.drain(); + + doChecksOnSslContext(true, testCallback.updatedSslContext, /* expectedApnProtos= */ null); + } + + @Test + public void testProviderForServer_tls() throws Exception { + final CertificateProvider.DistributorWatcher[] watcherCaptor = + new CertificateProvider.DistributorWatcher[1]; + TestCertificateProvider.createAndRegisterProviderProvider( + certificateProviderRegistry, watcherCaptor, "testca", 0); + CertProviderServerSslContextProvider provider = + getSslContextProvider( + "gcp_id", + /* rootInstanceName= */ null, + CommonCertProviderTestUtils.getTestBootstrapInfo(), + /* alpnProtocols= */ null, + /* staticCertValidationContext= */ null, + /* requireClientCert= */ false); + + assertThat(provider.savedKey).isNull(); + assertThat(provider.savedCertChain).isNull(); + assertThat(provider.savedTrustedRoots).isNull(); + assertThat(provider.getSslContext()).isNull(); + + // now generate cert update + watcherCaptor[0].updateCertificate( + CommonCertProviderTestUtils.getPrivateKey(SERVER_0_KEY_FILE), + ImmutableList.of(getCertFromResourceName(SERVER_0_PEM_FILE))); + + assertThat(provider.getSslContext()).isNotNull(); + assertThat(provider.savedKey).isNull(); + assertThat(provider.savedCertChain).isNull(); + assertThat(provider.savedTrustedRoots).isNull(); + + TestCallback testCallback = + CommonTlsContextTestsUtil.getValueThruCallback(provider); + + doChecksOnSslContext(true, testCallback.updatedSslContext, /* expectedApnProtos= */ null); + } + + @Test + public void testProviderForServer_sslContextException_onError() throws Exception { + CertificateValidationContext staticCertValidationContext = + CertificateValidationContext.newBuilder() + .setTrustedCa(DataSource.newBuilder().setInlineString("foo")) + .build(); + + final CertificateProvider.DistributorWatcher[] watcherCaptor = + new CertificateProvider.DistributorWatcher[1]; + TestCertificateProvider.createAndRegisterProviderProvider( + certificateProviderRegistry, watcherCaptor, "testca", 0); + CertProviderServerSslContextProvider provider = + getSslContextProvider( + /* certInstanceName= */ "gcp_id", + /* rootInstanceName= */ "gcp_id", + CommonCertProviderTestUtils.getTestBootstrapInfo(), + /* alpnProtocols= */null, + staticCertValidationContext, + /* requireClientCert= */ true); + + // now generate cert update + watcherCaptor[0].updateCertificate( + CommonCertProviderTestUtils.getPrivateKey(SERVER_0_KEY_FILE), + ImmutableList.of(getCertFromResourceName(SERVER_0_PEM_FILE))); + + TestCallback testCallback = new TestCallback(MoreExecutors.directExecutor()); + provider.addCallback(testCallback); + try { + watcherCaptor[0].updateTrustedRoots(ImmutableList.of(getCertFromResourceName(CA_PEM_FILE))); + fail("exception expected"); + } catch (RuntimeException expected) { + assertThat(expected) + .hasMessageThat() + .contains("only static certificateValidationContext expected"); + } + assertThat(testCallback.updatedThrowable).isNotNull(); + assertThat(testCallback.updatedThrowable) + .hasCauseThat() + .hasMessageThat() + .contains("only static certificateValidationContext expected"); + } + + @Test + public void testProviderForServer_certInstanceNull_expectError() throws Exception { + final CertificateProvider.DistributorWatcher[] watcherCaptor = + new CertificateProvider.DistributorWatcher[1]; + TestCertificateProvider.createAndRegisterProviderProvider( + certificateProviderRegistry, watcherCaptor, "testca", 0); + try { + getSslContextProvider( + /* certInstanceName= */ null, + /* rootInstanceName= */ null, + CommonCertProviderTestUtils.getTestBootstrapInfo(), + /* alpnProtocols= */ null, + /* staticCertValidationContext= */ null, + /* requireClientCert= */ false); + fail("exception expected"); + } catch (NullPointerException expected) { + assertThat(expected).hasMessageThat().contains("Server SSL requires certInstance"); + } + } +} diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/CommonTlsContextTestsUtil.java b/xds/src/test/java/io/grpc/xds/internal/sds/CommonTlsContextTestsUtil.java index 5f769ae950..8c150d0ccd 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/CommonTlsContextTestsUtil.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/CommonTlsContextTestsUtil.java @@ -533,6 +533,27 @@ public class CommonTlsContextTestsUtil { staticCertValidationContext)); } + /** Helper method to build DownstreamTlsContext for CertProvider tests. */ + public static EnvoyServerProtoData.DownstreamTlsContext + buildDownstreamTlsContextForCertProviderInstance( + @Nullable String certInstanceName, + @Nullable String certName, + @Nullable String rootInstanceName, + @Nullable String rootCertName, + Iterable alpnProtocols, + CertificateValidationContext staticCertValidationContext, + boolean requireClientCert) { + return buildInternalDownstreamTlsContext( + buildCommonTlsContextForCertProviderInstance( + certInstanceName, + certName, + rootInstanceName, + rootCertName, + alpnProtocols, + staticCertValidationContext), requireClientCert); + } + + /** Perform some simple checks on sslContext. */ public static void doChecksOnSslContext(boolean server, SslContext sslContext, List expectedApnProtos) {