diff --git a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java index d6e4044653..e683cb48e1 100644 --- a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java @@ -51,6 +51,8 @@ import io.envoyproxy.envoy.config.route.v3.RetryPolicy.RetryBackOff; import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager; import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds; +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.DownstreamTlsContext; import io.envoyproxy.envoy.type.v3.FractionalPercent; import io.envoyproxy.envoy.type.v3.FractionalPercent.DenominatorType; @@ -363,7 +365,11 @@ final class ClientXdsClient extends AbstractXdsClient { } EnvoyServerProtoData.DownstreamTlsContext downstreamTlsContext = null; - if (TRANSPORT_SOCKET_NAME_TLS.equals(proto.getTransportSocket().getName())) { + if (proto.hasTransportSocket()) { + if (!TRANSPORT_SOCKET_NAME_TLS.equals(proto.getTransportSocket().getName())) { + throw new ResourceInvalidException("transport-socket with name " + + proto.getTransportSocket().getName() + " not supported."); + } DownstreamTlsContext downstreamTlsContextProto; try { downstreamTlsContextProto = @@ -374,7 +380,7 @@ final class ClientXdsClient extends AbstractXdsClient { } downstreamTlsContext = EnvoyServerProtoData.DownstreamTlsContext.fromEnvoyProtoDownstreamTlsContext( - downstreamTlsContextProto); + validateDownstreamTlsContext(downstreamTlsContextProto)); } String name = proto.getName(); @@ -392,6 +398,182 @@ final class ClientXdsClient extends AbstractXdsClient { ); } + @VisibleForTesting + static io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + validateDownstreamTlsContext( + io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + downstreamTlsContext) + throws ResourceInvalidException { + if (downstreamTlsContext.hasCommonTlsContext()) { + validateCommonTlsContext(downstreamTlsContext.getCommonTlsContext(), true); + } else { + throw new ResourceInvalidException( + "common-tls-context is required in downstream-tls-context"); + } + if (downstreamTlsContext.hasRequireSni()) { + throw new ResourceInvalidException( + "downstream-tls-context with require-sni is not supported"); + } + if (downstreamTlsContext.hasSessionTicketKeys()) { + throw new ResourceInvalidException( + "downstream-tls-context with session_ticket_keys is not supported"); + } + if (downstreamTlsContext.hasSessionTicketKeysSdsSecretConfig()) { + throw new ResourceInvalidException( + "downstream-tls-context with session_ticket_keys_sds_secret_config is not supported"); + } + if (downstreamTlsContext.hasDisableStatelessSessionResumption()) { + throw new ResourceInvalidException( + "downstream-tls-context with disable_stateless_session_resumption is not supported"); + } + if (downstreamTlsContext.hasSessionTimeout()) { + throw new ResourceInvalidException( + "downstream-tls-context with session_timeout is not supported"); + } + DownstreamTlsContext.OcspStaplePolicy ocspStaplePolicy = downstreamTlsContext + .getOcspStaplePolicy(); + if (ocspStaplePolicy != null + && ocspStaplePolicy != DownstreamTlsContext.OcspStaplePolicy.UNRECOGNIZED + && ocspStaplePolicy != DownstreamTlsContext.OcspStaplePolicy.LENIENT_STAPLING) { + throw new ResourceInvalidException( + "downstream-tls-context with ocsp_staple_policy value " + ocspStaplePolicy.name() + + " is not supported"); + } + return downstreamTlsContext; + } + + @VisibleForTesting + static io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + validateUpstreamTlsContext( + io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext upstreamTlsContext) + throws ResourceInvalidException { + if (upstreamTlsContext.hasCommonTlsContext()) { + validateCommonTlsContext(upstreamTlsContext.getCommonTlsContext(), false); + } else { + throw new ResourceInvalidException("common-tls-context is required in upstream-tls-context"); + } + if (!Strings.isNullOrEmpty(upstreamTlsContext.getSni())) { + throw new ResourceInvalidException("upstream-tls-context with sni is not supported"); + } + if (upstreamTlsContext.getAllowRenegotiation()) { + throw new ResourceInvalidException( + "upstream-tls-context with allow_renegotiation is not supported"); + } + if (upstreamTlsContext.hasMaxSessionKeys()) { + throw new ResourceInvalidException( + "upstream-tls-context with max_session_keys is not supported"); + } + return upstreamTlsContext; + } + + @VisibleForTesting + static void validateCommonTlsContext( + CommonTlsContext commonTlsContext, boolean server) throws ResourceInvalidException { + if (commonTlsContext.hasCustomHandshaker()) { + throw new ResourceInvalidException( + "common-tls-context with custom_handshaker is not supported"); + } + if (commonTlsContext.hasTlsParams()) { + throw new ResourceInvalidException("common-tls-context with tls_params is not supported"); + } + if (commonTlsContext.hasValidationContext()) { + throw new ResourceInvalidException( + "common-tls-context with validation_context is not supported"); + } + if (commonTlsContext.hasValidationContextSdsSecretConfig()) { + throw new ResourceInvalidException( + "common-tls-context with validation_context_sds_secret_config is not supported"); + } + if (commonTlsContext.hasValidationContextCertificateProvider()) { + throw new ResourceInvalidException( + "common-tls-context with validation_context_certificate_provider is not supported"); + } + if (commonTlsContext.hasValidationContextCertificateProviderInstance()) { + throw new ResourceInvalidException( + "common-tls-context with validation_context_certificate_provider_instance is not" + + " supported"); + } + if (!commonTlsContext.hasTlsCertificateCertificateProviderInstance()) { + if (server) { + throw new ResourceInvalidException( + "tls_certificate_certificate_provider_instance is required in downstream-tls-context"); + } + if (commonTlsContext.getTlsCertificatesCount() > 0) { + throw new ResourceInvalidException( + "common-tls-context with tls_certificates is not supported"); + } + if (commonTlsContext.getTlsCertificateSdsSecretConfigsCount() > 0) { + throw new ResourceInvalidException( + "common-tls-context with tls_certificate_sds_secret_configs is not supported"); + } + if (commonTlsContext.hasTlsCertificateCertificateProvider()) { + throw new ResourceInvalidException( + "common-tls-context with tls_certificate_certificate_provider is not supported"); + } + } + if (!commonTlsContext.hasCombinedValidationContext()) { + if (!server) { + throw new ResourceInvalidException( + "combined_validation_context is required in upstream-tls-context"); + } + } else { + CommonTlsContext.CombinedCertificateValidationContext combinedCertificateValidationContext + = commonTlsContext.getCombinedValidationContext(); + if (!combinedCertificateValidationContext.hasValidationContextCertificateProviderInstance()) { + throw new ResourceInvalidException( + "validation_context_certificate_provider_instance is required in" + + " combined_validation_context"); + } + if (combinedCertificateValidationContext.hasDefaultValidationContext()) { + if (server) { + throw new ResourceInvalidException( + "default_validation_context only allowed in upstream_tls_context"); + } + CertificateValidationContext certificateValidationContext + = combinedCertificateValidationContext.getDefaultValidationContext(); + if (certificateValidationContext.hasTrustedCa()) { + throw new ResourceInvalidException( + "trusted_ca in default_validation_context is not supported"); + } + if (certificateValidationContext.hasWatchedDirectory()) { + throw new ResourceInvalidException( + "watched_directory in default_validation_context is not supported"); + } + if (certificateValidationContext.getVerifyCertificateSpkiCount() > 0) { + throw new ResourceInvalidException( + "verify_certificate_spki in default_validation_context is not supported"); + } + if (certificateValidationContext.getVerifyCertificateHashCount() > 0) { + throw new ResourceInvalidException( + "verify_certificate_hash in default_validation_context is not supported"); + } + if (certificateValidationContext.hasRequireSignedCertificateTimestamp()) { + throw new ResourceInvalidException( + "require_signed_certificate_timestamp in default_validation_context is not " + + "supported"); + } + if (certificateValidationContext.hasCrl()) { + throw new ResourceInvalidException("crl in default_validation_context is not supported"); + } + if (certificateValidationContext.getAllowExpiredCertificate()) { + throw new ResourceInvalidException( + "allow_expired_certificate in default_validation_context is not supported"); + } + CertificateValidationContext.TrustChainVerification trustChainVerification + = certificateValidationContext.getTrustChainVerification(); + if (trustChainVerification != null && trustChainVerification + != CertificateValidationContext.TrustChainVerification.VERIFY_TRUST_CHAIN) { + throw new ResourceInvalidException( + "Only VERIFY_TRUST_CHAIN for trust_chain_verification supported"); + } + if (certificateValidationContext.hasCustomValidatorConfig()) { + throw new ResourceInvalidException( + "custom_validator_config in default_validation_context is not supported"); + } + } + } + } + private static void checkForUniqueness(Set uniqueSet, FilterChainMatch filterChainMatch) throws ResourceInvalidException { if (uniqueSet != null) { @@ -1336,14 +1518,18 @@ final class ClientXdsClient extends AbstractXdsClient { } } } - if (cluster.hasTransportSocket() - && TRANSPORT_SOCKET_NAME_TLS.equals(cluster.getTransportSocket().getName())) { + if (cluster.hasTransportSocket()) { + if (!TRANSPORT_SOCKET_NAME_TLS.equals(cluster.getTransportSocket().getName())) { + return StructOrError.fromError("transport-socket with name " + + cluster.getTransportSocket().getName() + " not supported."); + } try { upstreamTlsContext = UpstreamTlsContext.fromEnvoyProtoUpstreamTlsContext( + validateUpstreamTlsContext( unpackCompatibleType(cluster.getTransportSocket().getTypedConfig(), io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext.class, - TYPE_URL_UPSTREAM_TLS_CONTEXT, TYPE_URL_UPSTREAM_TLS_CONTEXT_V2)); - } catch (InvalidProtocolBufferException e) { + TYPE_URL_UPSTREAM_TLS_CONTEXT, TYPE_URL_UPSTREAM_TLS_CONTEXT_V2))); + } catch (InvalidProtocolBufferException | ResourceInvalidException e) { return StructOrError.fromError( "Cluster " + clusterName + ": malformed UpstreamTlsContext: " + e); } diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java index ac62b6e4b7..47ec59fe35 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableMap; import com.google.protobuf.Any; import com.google.protobuf.BoolValue; +import com.google.protobuf.Duration; import com.google.protobuf.StringValue; import com.google.protobuf.UInt32Value; import com.google.protobuf.UInt64Value; @@ -36,12 +37,15 @@ import io.envoyproxy.envoy.config.core.v3.Address; import io.envoyproxy.envoy.config.core.v3.AggregatedConfigSource; import io.envoyproxy.envoy.config.core.v3.CidrRange; import io.envoyproxy.envoy.config.core.v3.ConfigSource; +import io.envoyproxy.envoy.config.core.v3.DataSource; import io.envoyproxy.envoy.config.core.v3.HttpProtocolOptions; import io.envoyproxy.envoy.config.core.v3.Locality; import io.envoyproxy.envoy.config.core.v3.RuntimeFractionalPercent; import io.envoyproxy.envoy.config.core.v3.SocketAddress; import io.envoyproxy.envoy.config.core.v3.TrafficDirection; import io.envoyproxy.envoy.config.core.v3.TransportSocket; +import io.envoyproxy.envoy.config.core.v3.TypedExtensionConfig; +import io.envoyproxy.envoy.config.core.v3.WatchedDirectory; import io.envoyproxy.envoy.config.endpoint.v3.Endpoint; import io.envoyproxy.envoy.config.listener.v3.Filter; import io.envoyproxy.envoy.config.listener.v3.FilterChain; @@ -73,6 +77,14 @@ import io.envoyproxy.envoy.extensions.filters.http.router.v3.Router; import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager; import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter; import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds; +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.DownstreamTlsContext; +import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.SdsSecretConfig; +import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.TlsCertificate; +import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.TlsParameters; +import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.TlsSessionTicketKeys; +import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext; import io.envoyproxy.envoy.type.matcher.v3.RegexMatchAndSubstitute; import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher; import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher.GoogleRE2; @@ -1233,7 +1245,6 @@ public class ClientXdsClientDataTest { FilterChain.newBuilder() .setName("filter-chain-1") .setFilterChainMatch(filterChainMatch1) - .setTransportSocket(TransportSocket.getDefaultInstance()) .addFilters(filter1) .build(); Filter filter2 = buildHttpConnectionManagerFilter( @@ -1251,7 +1262,6 @@ public class ClientXdsClientDataTest { FilterChain.newBuilder() .setName("filter-chain-2") .setFilterChainMatch(filterChainMatch2) - .setTransportSocket(TransportSocket.getDefaultInstance()) .addFilters(filter2) .build(); Listener listener = @@ -1282,7 +1292,6 @@ public class ClientXdsClientDataTest { FilterChain.newBuilder() .setName("filter-chain-1") .setFilterChainMatch(filterChainMatch1) - .setTransportSocket(TransportSocket.getDefaultInstance()) .addFilters(filter1) .build(); Filter filter2 = buildHttpConnectionManagerFilter( @@ -1300,7 +1309,6 @@ public class ClientXdsClientDataTest { FilterChain.newBuilder() .setName("filter-chain-2") .setFilterChainMatch(filterChainMatch2) - .setTransportSocket(TransportSocket.getDefaultInstance()) .addFilters(filter2) .build(); Listener listener = @@ -1332,7 +1340,6 @@ public class ClientXdsClientDataTest { FilterChain.newBuilder() .setName("filter-chain-1") .setFilterChainMatch(filterChainMatch1) - .setTransportSocket(TransportSocket.getDefaultInstance()) .addFilters(filter1) .build(); Filter filter2 = buildHttpConnectionManagerFilter( @@ -1351,7 +1358,6 @@ public class ClientXdsClientDataTest { FilterChain.newBuilder() .setName("filter-chain-2") .setFilterChainMatch(filterChainMatch2) - .setTransportSocket(TransportSocket.getDefaultInstance()) .addFilters(filter2) .build(); Listener listener = @@ -1467,6 +1473,519 @@ public class ClientXdsClientDataTest { assertThat(parsedFilterChain1.getName()).isNotEqualTo(parsedFilterChain2.getName()); } + + @Test + public void validateCommonTlsContext_tlsParams() throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setTlsParams(TlsParameters.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("common-tls-context with tls_params is not supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_customHandshaker() throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCustomHandshaker(TypedExtensionConfig.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("common-tls-context with custom_handshaker is not supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_validationContext() throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setValidationContext(CertificateValidationContext.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("common-tls-context with validation_context is not supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_validationContextSdsSecretConfig() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setValidationContextSdsSecretConfig(SdsSecretConfig.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "common-tls-context with validation_context_sds_secret_config is not supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_validationContextCertificateProvider() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setValidationContextCertificateProvider( + CommonTlsContext.CertificateProvider.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "common-tls-context with validation_context_certificate_provider is not supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_validationContextCertificateProviderInstance() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "common-tls-context with validation_context_certificate_provider_instance is not " + + "supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_tlsCertificateProviderInstance_isRequiredForServer() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "tls_certificate_certificate_provider_instance is required in downstream-tls-context"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, true); + } + + @Test + public void validateCommonTlsContext_tlsCertificatesCount() throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .addTlsCertificates(TlsCertificate.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("common-tls-context with tls_certificates is not supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_tlsCertificateSdsSecretConfigsCount() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .addTlsCertificateSdsSecretConfigs(SdsSecretConfig.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "common-tls-context with tls_certificate_sds_secret_configs is not supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_tlsCertificateCertificateProvider() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setTlsCertificateCertificateProvider( + CommonTlsContext.CertificateProvider.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "common-tls-context with tls_certificate_certificate_provider is not supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_combinedValidationContext_isRequiredForClient() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("combined_validation_context is required in upstream-tls-context"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_combinedValidationContextWithoutCertProviderInstance() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "validation_context_certificate_provider_instance is required in " + + "combined_validation_context"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_combinedValContextWithDefaultValContextForServer() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .setDefaultValidationContext(CertificateValidationContext.getDefaultInstance())) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("default_validation_context only allowed in upstream_tls_context"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, true); + } + + @Test + public void validateCommonTlsContext_combinedValContextWithDefaultValidationContextTrustedCa() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .setDefaultValidationContext(CertificateValidationContext.newBuilder() + .setTrustedCa(DataSource.getDefaultInstance()))) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("trusted_ca in default_validation_context is not supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_combinedValContextWithDefaultValContextWatchedDirectory() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .setDefaultValidationContext(CertificateValidationContext.newBuilder() + .setWatchedDirectory(WatchedDirectory.getDefaultInstance()))) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("watched_directory in default_validation_context is not supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_combinedValContextWithDefaultValContextVerifyCertSpki() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .setDefaultValidationContext( + CertificateValidationContext.newBuilder().addVerifyCertificateSpki("foo"))) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("verify_certificate_spki in default_validation_context is not " + + "supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_combinedValContextWithDefaultValContextVerifyCertHash() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .setDefaultValidationContext( + CertificateValidationContext.newBuilder().addVerifyCertificateHash("foo"))) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("verify_certificate_hash in default_validation_context is not " + + "supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_combinedValContextDfltValContextRequireSignedCertTimestamp() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .setDefaultValidationContext(CertificateValidationContext.newBuilder() + .setRequireSignedCertificateTimestamp(BoolValue.of(true)))) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "require_signed_certificate_timestamp in default_validation_context is not " + + "supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_combinedValidationContextWithDefaultValidationContextCrl() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .setDefaultValidationContext(CertificateValidationContext.newBuilder() + .setCrl(DataSource.getDefaultInstance()))) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("crl in default_validation_context is not supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_combinedValContextWithDefaultValContextAllowExpiredCert() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .setDefaultValidationContext( + CertificateValidationContext.newBuilder().setAllowExpiredCertificate(true))) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown + .expectMessage("allow_expired_certificate in default_validation_context is not " + + "supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_combinedValContextWithDfltValContextTrustChainVerification() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .setDefaultValidationContext(CertificateValidationContext.newBuilder() + .setTrustChainVerification( + CertificateValidationContext.TrustChainVerification.ACCEPT_UNTRUSTED))) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("Only VERIFY_TRUST_CHAIN for trust_chain_verification supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateCommonTlsContext_combinedValContextWithDfltValContextCustomValidatorConfig() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .setDefaultValidationContext(CertificateValidationContext.newBuilder() + .setCustomValidatorConfig(TypedExtensionConfig.getDefaultInstance()))) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("custom_validator_config in default_validation_context is not " + + "supported"); + ClientXdsClient.validateCommonTlsContext(commonTlsContext, false); + } + + @Test + public void validateDownstreamTlsContext_noCommonTlsContext() throws ResourceInvalidException { + DownstreamTlsContext downstreamTlsContext = DownstreamTlsContext.getDefaultInstance(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("common-tls-context is required in downstream-tls-context"); + ClientXdsClient.validateDownstreamTlsContext(downstreamTlsContext); + } + + @Test + public void validateDownstreamTlsContext_hasRequireSni() throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance())) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + DownstreamTlsContext downstreamTlsContext = DownstreamTlsContext.newBuilder() + .setCommonTlsContext(commonTlsContext) + .setRequireSni(BoolValue.of(true)) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("downstream-tls-context with require-sni is not supported"); + ClientXdsClient.validateDownstreamTlsContext(downstreamTlsContext); + } + + @Test + public void validateDownstreamTlsContext_hasSessionTikcetKeys() throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance())) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + DownstreamTlsContext downstreamTlsContext = DownstreamTlsContext.newBuilder() + .setCommonTlsContext(commonTlsContext) + .setSessionTicketKeys(TlsSessionTicketKeys.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("downstream-tls-context with session_ticket_keys is not supported"); + ClientXdsClient.validateDownstreamTlsContext(downstreamTlsContext); + } + + @Test + public void validateDownstreamTlsContext_hasSessionTikcetKeysSdsSecretConfig() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance())) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + DownstreamTlsContext downstreamTlsContext = DownstreamTlsContext.newBuilder() + .setCommonTlsContext(commonTlsContext) + .setSessionTicketKeysSdsSecretConfig(SdsSecretConfig.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "downstream-tls-context with session_ticket_keys_sds_secret_config is not supported"); + ClientXdsClient.validateDownstreamTlsContext(downstreamTlsContext); + } + + @Test + public void validateDownstreamTlsContext_hasDisableStatelessSessionResumption() + throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance())) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + DownstreamTlsContext downstreamTlsContext = DownstreamTlsContext.newBuilder() + .setCommonTlsContext(commonTlsContext) + .setDisableStatelessSessionResumption(true) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "downstream-tls-context with disable_stateless_session_resumption is not supported"); + ClientXdsClient.validateDownstreamTlsContext(downstreamTlsContext); + } + + @Test + public void validateDownstreamTlsContext_hasSessionTimeout() throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance())) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + DownstreamTlsContext downstreamTlsContext = DownstreamTlsContext.newBuilder() + .setCommonTlsContext(commonTlsContext) + .setSessionTimeout(Duration.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("downstream-tls-context with session_timeout is not supported"); + ClientXdsClient.validateDownstreamTlsContext(downstreamTlsContext); + } + + @Test + public void validateDownstreamTlsContext_hasOcspStaplePolicy() throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance())) + .setTlsCertificateCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance()) + .build(); + DownstreamTlsContext downstreamTlsContext = DownstreamTlsContext.newBuilder() + .setCommonTlsContext(commonTlsContext) + .setOcspStaplePolicy(DownstreamTlsContext.OcspStaplePolicy.STRICT_STAPLING) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage( + "downstream-tls-context with ocsp_staple_policy value STRICT_STAPLING is not supported"); + ClientXdsClient.validateDownstreamTlsContext(downstreamTlsContext); + } + + @Test + public void validateUpstreamTlsContext_noCommonTlsContext() throws ResourceInvalidException { + UpstreamTlsContext upstreamTlsContext = UpstreamTlsContext.getDefaultInstance(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("common-tls-context is required in upstream-tls-context"); + ClientXdsClient.validateUpstreamTlsContext(upstreamTlsContext); + } + + @Test + public void validateUpstreamTlsContext_sni() throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance())) + .build(); + UpstreamTlsContext upstreamTlsContext = UpstreamTlsContext.newBuilder() + .setCommonTlsContext(commonTlsContext) + .setSni("foo") + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("upstream-tls-context with sni is not supported"); + ClientXdsClient.validateUpstreamTlsContext(upstreamTlsContext); + } + + @Test + public void validateUpstreamTlsContext_allowRenegotiation() throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance())) + .build(); + UpstreamTlsContext upstreamTlsContext = UpstreamTlsContext.newBuilder() + .setCommonTlsContext(commonTlsContext) + .setAllowRenegotiation(true) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("upstream-tls-context with allow_renegotiation is not supported"); + ClientXdsClient.validateUpstreamTlsContext(upstreamTlsContext); + } + + @Test + public void validateUpstreamTlsContext_maxSessionKeys() throws ResourceInvalidException { + CommonTlsContext commonTlsContext = CommonTlsContext.newBuilder() + .setCombinedValidationContext( + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance( + CommonTlsContext.CertificateProviderInstance.getDefaultInstance())) + .build(); + UpstreamTlsContext upstreamTlsContext = UpstreamTlsContext.newBuilder() + .setCommonTlsContext(commonTlsContext) + .setMaxSessionKeys(UInt32Value.getDefaultInstance()) + .build(); + thrown.expect(ResourceInvalidException.class); + thrown.expectMessage("upstream-tls-context with max_session_keys is not supported"); + ClientXdsClient.validateUpstreamTlsContext(upstreamTlsContext); + } + private static Filter buildHttpConnectionManagerFilter(HttpFilter... httpFilters) { return Filter.newBuilder() .setName("envoy.http_connection_manager") diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java index f998396d03..913eb208ad 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java @@ -39,7 +39,7 @@ import com.google.protobuf.Any; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; import io.envoyproxy.envoy.config.route.v3.FilterConfig; -import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.SdsSecretConfig; +import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext; import io.grpc.BindableService; import io.grpc.Context; import io.grpc.Context.CancellableContext; @@ -193,7 +193,9 @@ public abstract class ClientXdsClientTestBase { // CDS test resources. private final Any testClusterRoundRobin = - Any.pack(mf.buildEdsCluster(CDS_RESOURCE, null, "round_robin", null, false, null, null)); + Any.pack(mf.buildEdsCluster(CDS_RESOURCE, null, "round_robin", null, false, null, + "envoy.transport_sockets.tls", null + )); // EDS test resources. private final Message lbEndpointHealthy = @@ -990,9 +992,10 @@ public abstract class ClientXdsClientTestBase { Message hcmFilter = mf.buildHttpConnectionManagerFilter( RDS_RESOURCE, null, Collections.emptyList()); Message downstreamTlsContext = CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( - "google-sds-config-default", "ROOTCA"); + "google-sds-config-default", "ROOTCA", false); Message filterChain = mf.buildFilterChain( - Collections.emptyList(), downstreamTlsContext, hcmFilter); + Collections.emptyList(), downstreamTlsContext, "envoy.transport_sockets.tls", + hcmFilter); Any packedListener = Any.pack(mf.buildListenerWithFilterChain(LISTENER_RESOURCE, 7000, "0.0.0.0", filterChain)); @@ -1024,7 +1027,8 @@ public abstract class ClientXdsClientTestBase { "route-bar.googleapis.com", mf.buildOpaqueVirtualHosts(VHOST_SIZE)), Collections.emptyList()); filterChain = mf.buildFilterChain( - Collections.emptyList(), downstreamTlsContext, hcmFilter); + Collections.emptyList(), downstreamTlsContext, "envoy.transport_sockets.tls", + hcmFilter); packedListener = Any.pack(mf.buildListenerWithFilterChain(LISTENER_RESOURCE, 7000, "0.0.0.0", filterChain)); call.sendResponse(LDS, packedListener, VERSION_2, "0001"); @@ -1090,9 +1094,9 @@ public abstract class ClientXdsClientTestBase { List clusters = ImmutableList.of( Any.pack(mf.buildEdsCluster("cluster-bar.googleapis.com", null, "round_robin", null, - false, null, null)), + false, null, "envoy.transport_sockets.tls", null)), Any.pack(mf.buildEdsCluster("cluster-baz.googleapis.com", null, "round_robin", null, - false, null, null))); + false, null, "envoy.transport_sockets.tls", null))); call.sendResponse(CDS, clusters, VERSION_1, "0000"); // Client sent an ACK CDS request. @@ -1165,9 +1169,15 @@ public abstract class ClientXdsClientTestBase { // CDS -> {A, B, C}, version 1 ImmutableMap resourcesV1 = ImmutableMap.of( - "A", Any.pack(mf.buildEdsCluster("A", "A.1", "round_robin", null, false, null, null)), - "B", Any.pack(mf.buildEdsCluster("B", "B.1", "round_robin", null, false, null, null)), - "C", Any.pack(mf.buildEdsCluster("C", "C.1", "round_robin", null, false, null, null))); + "A", Any.pack(mf.buildEdsCluster("A", "A.1", "round_robin", null, false, null, + "envoy.transport_sockets.tls", null + )), + "B", Any.pack(mf.buildEdsCluster("B", "B.1", "round_robin", null, false, null, + "envoy.transport_sockets.tls", null + )), + "C", Any.pack(mf.buildEdsCluster("C", "C.1", "round_robin", null, false, null, + "envoy.transport_sockets.tls", null + ))); call.sendResponse(CDS, resourcesV1.values().asList(), VERSION_1, "0000"); // {A, B, C} -> ACK, version 1 verifyResourceMetadataAcked(CDS, "A", resourcesV1.get("A"), VERSION_1, TIME_INCREMENT); @@ -1178,7 +1188,9 @@ public abstract class ClientXdsClientTestBase { // CDS -> {A, B}, version 2 // Failed to parse endpoint B ImmutableMap resourcesV2 = ImmutableMap.of( - "A", Any.pack(mf.buildEdsCluster("A", "A.2", "round_robin", null, false, null, null)), + "A", Any.pack(mf.buildEdsCluster("A", "A.2", "round_robin", null, false, null, + "envoy.transport_sockets.tls", null + )), "B", Any.pack(mf.buildClusterInvalid("B"))); call.sendResponse(CDS, resourcesV2.values().asList(), VERSION_2, "0001"); // {A, B} -> NACK, version 1, rejected version 2, rejected reason: Failed to parse B @@ -1193,8 +1205,12 @@ public abstract class ClientXdsClientTestBase { // CDS -> {B, C} version 3 ImmutableMap resourcesV3 = ImmutableMap.of( - "B", Any.pack(mf.buildEdsCluster("B", "B.3", "round_robin", null, false, null, null)), - "C", Any.pack(mf.buildEdsCluster("C", "C.3", "round_robin", null, false, null, null))); + "B", Any.pack(mf.buildEdsCluster("B", "B.3", "round_robin", null, false, null, + "envoy.transport_sockets.tls", null + )), + "C", Any.pack(mf.buildEdsCluster("C", "C.3", "round_robin", null, false, null, + "envoy.transport_sockets.tls", null + ))); call.sendResponse(CDS, resourcesV3.values().asList(), VERSION_3, "0002"); // {A} -> NACK, version 1, rejected version 2, rejected reason: Failed to parse B // {B, C} -> ACK, version 3 @@ -1231,7 +1247,9 @@ public abstract class ClientXdsClientTestBase { DiscoveryRpcCall call = startResourceWatcher(CDS, CDS_RESOURCE, cdsResourceWatcher); Message ringHashConfig = mf.buildRingHashLbConfig("xx_hash", 10L, 100L); Any clusterRingHash = Any.pack( - mf.buildEdsCluster(CDS_RESOURCE, null, "ring_hash", ringHashConfig, false, null, null)); + mf.buildEdsCluster(CDS_RESOURCE, null, "ring_hash", ringHashConfig, false, null, + "envoy.transport_sockets.tls", null + )); call.sendResponse(ResourceType.CDS, clusterRingHash, VERSION_1, "0000"); // Client sent an ACK CDS request. @@ -1278,7 +1296,7 @@ public abstract class ClientXdsClientTestBase { DiscoveryRpcCall call = startResourceWatcher(CDS, CDS_RESOURCE, cdsResourceWatcher); Any clusterCircuitBreakers = Any.pack( mf.buildEdsCluster(CDS_RESOURCE, null, "round_robin", null, false, null, - mf.buildCircuitBreakers(50, 200))); + "envoy.transport_sockets.tls", mf.buildCircuitBreakers(50, 200))); call.sendResponse(CDS, clusterCircuitBreakers, VERSION_1, "0000"); // Client sent an ACK CDS request. @@ -1302,41 +1320,81 @@ public abstract class ClientXdsClientTestBase { */ @Test public void cdsResponseWithUpstreamTlsContext() { + Assume.assumeTrue(useProtocolV3()); DiscoveryRpcCall call = startResourceWatcher(CDS, CDS_RESOURCE, cdsResourceWatcher); // Management server sends back CDS response with UpstreamTlsContext. Any clusterEds = Any.pack(mf.buildEdsCluster(CDS_RESOURCE, "eds-cluster-foo.googleapis.com", "round_robin", null, true, - mf.buildUpstreamTlsContext("secret1", "unix:/var/uds2"), null)); + mf.buildUpstreamTlsContext("secret1", "cert1"), "envoy.transport_sockets.tls", null)); List clusters = ImmutableList.of( Any.pack(mf.buildLogicalDnsCluster("cluster-bar.googleapis.com", "dns-service-bar.googleapis.com", 443, "round_robin", null, false, null, null)), clusterEds, Any.pack(mf.buildEdsCluster("cluster-baz.googleapis.com", null, "round_robin", null, false, - null, null))); + null, "envoy.transport_sockets.tls", null))); call.sendResponse(CDS, clusters, VERSION_1, "0000"); // Client sent an ACK CDS request. call.verifyRequest(CDS, CDS_RESOURCE, VERSION_1, "0000", NODE); verify(cdsResourceWatcher, times(1)).onChanged(cdsUpdateCaptor.capture()); CdsUpdate cdsUpdate = cdsUpdateCaptor.getValue(); - SdsSecretConfig validationContextSdsSecretConfig = - cdsUpdate.upstreamTlsContext().getCommonTlsContext().getValidationContextSdsSecretConfig(); - assertThat(validationContextSdsSecretConfig.getName()).isEqualTo("secret1"); - assertThat( - Iterables.getOnlyElement( - validationContextSdsSecretConfig - .getSdsConfig() - .getApiConfigSource() - .getGrpcServicesList()) - .getGoogleGrpc() - .getTargetUri()) - .isEqualTo("unix:/var/uds2"); + CommonTlsContext.CertificateProviderInstance certificateProviderInstance = + cdsUpdate.upstreamTlsContext().getCommonTlsContext().getCombinedValidationContext() + .getValidationContextCertificateProviderInstance(); + assertThat(certificateProviderInstance.getInstanceName()).isEqualTo("secret1"); + assertThat(certificateProviderInstance.getCertificateName()).isEqualTo("cert1"); verifyResourceMetadataAcked(CDS, CDS_RESOURCE, clusterEds, VERSION_1, TIME_INCREMENT); verifySubscribedResourcesMetadataSizes(0, 1, 0, 0); } + /** + * CDS response containing bad UpstreamTlsContext for a cluster. + */ + @Test + public void cdsResponseErrorHandling_badUpstreamTlsContext() { + Assume.assumeTrue(useProtocolV3()); + DiscoveryRpcCall call = startResourceWatcher(CDS, CDS_RESOURCE, cdsResourceWatcher); + + // Management server sends back CDS response with UpstreamTlsContext. + List clusters = ImmutableList.of(Any + .pack(mf.buildEdsCluster(CDS_RESOURCE, "eds-cluster-foo.googleapis.com", "round_robin", + null, true, + mf.buildUpstreamTlsContext(null, null), "envoy.transport_sockets.tls", null))); + call.sendResponse(CDS, clusters, VERSION_1, "0000"); + + // The response NACKed with errors indicating indices of the failed resources. + call.verifyRequestNack(CDS, CDS_RESOURCE, "", "0000", NODE, ImmutableList.of( + "CDS response Cluster 'cluster.googleapis.com' validation error: " + + "Cluster cluster.googleapis.com: malformed UpstreamTlsContext: " + + "io.grpc.xds.ClientXdsClient$ResourceInvalidException: " + + "combined_validation_context is required in upstream-tls-context")); + verifyNoInteractions(cdsResourceWatcher); + } + + /** + * CDS response containing UpstreamTlsContext with bad transportSocketName for a cluster. + */ + @Test + public void cdsResponseErrorHandling_badTransportSocketName() { + Assume.assumeTrue(useProtocolV3()); + DiscoveryRpcCall call = startResourceWatcher(CDS, CDS_RESOURCE, cdsResourceWatcher); + + // Management server sends back CDS response with UpstreamTlsContext. + List clusters = ImmutableList.of(Any + .pack(mf.buildEdsCluster(CDS_RESOURCE, "eds-cluster-foo.googleapis.com", "round_robin", + null, true, + mf.buildUpstreamTlsContext("secret1", "cert1"), "envoy.transport_sockets.bad", null))); + call.sendResponse(CDS, clusters, VERSION_1, "0000"); + + // The response NACKed with errors indicating indices of the failed resources. + call.verifyRequestNack(CDS, CDS_RESOURCE, "", "0000", NODE, ImmutableList.of( + "CDS response Cluster 'cluster.googleapis.com' validation error: " + + "transport-socket with name envoy.transport_sockets.bad not supported.")); + verifyNoInteractions(cdsResourceWatcher); + } + @Test public void cachedCdsResource_data() { DiscoveryRpcCall call = startResourceWatcher(CDS, CDS_RESOURCE, cdsResourceWatcher); @@ -1403,7 +1461,9 @@ public abstract class ClientXdsClientTestBase { // Updated CDS response. String edsService = "eds-service-bar.googleapis.com"; Any clusterEds = Any.pack( - mf.buildEdsCluster(CDS_RESOURCE, edsService, "round_robin", null, true, null, null)); + mf.buildEdsCluster(CDS_RESOURCE, edsService, "round_robin", null, true, null, + "envoy.transport_sockets.tls", null + )); call.sendResponse(CDS, clusterEds, VERSION_2, "0001"); call.verifyRequest(CDS, CDS_RESOURCE, VERSION_2, "0001", NODE); verify(cdsResourceWatcher, times(2)).onChanged(cdsUpdateCaptor.capture()); @@ -1477,7 +1537,7 @@ public abstract class ClientXdsClientTestBase { Any.pack(mf.buildLogicalDnsCluster(CDS_RESOURCE, dnsHostAddr, dnsHostPort, "round_robin", null, false, null, null)), Any.pack(mf.buildEdsCluster(cdsResourceTwo, edsService, "round_robin", null, true, null, - null))); + "envoy.transport_sockets.tls", null))); call.sendResponse(CDS, clusters, VERSION_1, "0000"); verify(cdsResourceWatcher).onChanged(cdsUpdateCaptor.capture()); CdsUpdate cdsUpdate = cdsUpdateCaptor.getValue(); @@ -1736,9 +1796,11 @@ public abstract class ClientXdsClientTestBase { DiscoveryRpcCall call = resourceDiscoveryCalls.poll(); List clusters = ImmutableList.of( - Any.pack(mf.buildEdsCluster(resource, null, "round_robin", null, true, null, null)), + Any.pack(mf.buildEdsCluster(resource, null, "round_robin", null, true, null, + "envoy.transport_sockets.tls", null + )), Any.pack(mf.buildEdsCluster(CDS_RESOURCE, EDS_RESOURCE, "round_robin", null, false, null, - null))); + "envoy.transport_sockets.tls", null))); call.sendResponse(CDS, clusters, VERSION_1, "0000"); verify(cdsWatcher).onChanged(cdsUpdateCaptor.capture()); CdsUpdate cdsUpdate = cdsUpdateCaptor.getValue(); @@ -1784,8 +1846,10 @@ public abstract class ClientXdsClientTestBase { clusters = ImmutableList.of( Any.pack(mf.buildEdsCluster(resource, null, "round_robin", null, true, null, - null)), // no change - Any.pack(mf.buildEdsCluster(CDS_RESOURCE, null, "round_robin", null, false, null, null))); + "envoy.transport_sockets.tls", null)), // no change + Any.pack(mf.buildEdsCluster(CDS_RESOURCE, null, "round_robin", null, false, null, + "envoy.transport_sockets.tls", null + ))); call.sendResponse(CDS, clusters, VERSION_2, "0001"); verify(cdsResourceWatcher, times(2)).onChanged(cdsUpdateCaptor.capture()); assertThat(cdsUpdateCaptor.getValue().edsServiceName()).isNull(); @@ -2100,9 +2164,10 @@ public abstract class ClientXdsClientTestBase { Message hcmFilter = mf.buildHttpConnectionManagerFilter( "route-foo.googleapis.com", null, Collections.emptyList()); Message downstreamTlsContext = CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( - "google-sds-config-default", "ROOTCA"); + "google-sds-config-default", "ROOTCA", false); Message filterChain = mf.buildFilterChain( - Collections.emptyList(), downstreamTlsContext, hcmFilter); + Collections.emptyList(), downstreamTlsContext, "envoy.transport_sockets.tls", + hcmFilter); Message listener = mf.buildListenerWithFilterChain(LISTENER_RESOURCE, 7000, "0.0.0.0", filterChain); List listeners = ImmutableList.of(Any.pack(listener)); @@ -2133,9 +2198,10 @@ public abstract class ClientXdsClientTestBase { Message hcmFilter = mf.buildHttpConnectionManagerFilter( "route-foo.googleapis.com", null, Collections.emptyList()); Message downstreamTlsContext = CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( - "google-sds-config-default", "ROOTCA"); + "google-sds-config-default", "ROOTCA", false); Message filterChain = mf.buildFilterChain( - Collections.singletonList("managed-mtls"), downstreamTlsContext, hcmFilter); + Collections.singletonList("managed-mtls"), downstreamTlsContext, + "envoy.transport_sockets.tls", hcmFilter); Message listener = mf.buildListenerWithFilterChain( "grpc/server?xds.resource.listening_address=0.0.0.0:8000", 7000, "0.0.0.0", filterChain); List listeners = ImmutableList.of(Any.pack(listener)); @@ -2150,6 +2216,53 @@ public abstract class ClientXdsClientTestBase { assertThat(fakeClock.getPendingTasks(LDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).isEmpty(); } + @Test + public void serverSideListenerResponseErrorHandling_badDownstreamTlsContext() { + Assume.assumeTrue(useProtocolV3()); + ClientXdsClientTestBase.DiscoveryRpcCall call = + startResourceWatcher(LDS, LISTENER_RESOURCE, ldsResourceWatcher); + Message hcmFilter = mf.buildHttpConnectionManagerFilter( + "route-foo.googleapis.com", null, Collections.emptyList()); + Message downstreamTlsContext = CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( + null, null,false); + Message filterChain = mf.buildFilterChain( + Collections.emptyList(), downstreamTlsContext, "envoy.transport_sockets.tls", + hcmFilter); + Message listener = + mf.buildListenerWithFilterChain(LISTENER_RESOURCE, 7000, "0.0.0.0", filterChain); + List listeners = ImmutableList.of(Any.pack(listener)); + call.sendResponse(ResourceType.LDS, listeners, "0", "0000"); + // The response NACKed with errors indicating indices of the failed resources. + call.verifyRequestNack(LDS, LISTENER_RESOURCE, "", "0000", NODE, ImmutableList.of( + "LDS response Listener \'grpc/server?xds.resource.listening_address=0.0.0.0:7000\' " + + "validation error: common-tls-context is required in downstream-tls-context")); + verifyNoInteractions(ldsResourceWatcher); + } + + @Test + public void serverSideListenerResponseErrorHandling_badTransportSocketName() { + Assume.assumeTrue(useProtocolV3()); + ClientXdsClientTestBase.DiscoveryRpcCall call = + startResourceWatcher(LDS, LISTENER_RESOURCE, ldsResourceWatcher); + Message hcmFilter = mf.buildHttpConnectionManagerFilter( + "route-foo.googleapis.com", null, Collections.emptyList()); + Message downstreamTlsContext = CommonTlsContextTestsUtil.buildTestDownstreamTlsContext( + "cert1", "cert2",false); + Message filterChain = mf.buildFilterChain( + Collections.emptyList(), downstreamTlsContext, "envoy.transport_sockets.bad1", + hcmFilter); + Message listener = + mf.buildListenerWithFilterChain(LISTENER_RESOURCE, 7000, "0.0.0.0", filterChain); + List listeners = ImmutableList.of(Any.pack(listener)); + call.sendResponse(ResourceType.LDS, listeners, "0", "0000"); + // The response NACKed with errors indicating indices of the failed resources. + call.verifyRequestNack(LDS, LISTENER_RESOURCE, "", "0000", NODE, ImmutableList.of( + "LDS response Listener \'grpc/server?xds.resource.listening_address=0.0.0.0:7000\' " + + "validation error: " + + "transport-socket with name envoy.transport_sockets.bad1 not supported.")); + verifyNoInteractions(ldsResourceWatcher); + } + private DiscoveryRpcCall startResourceWatcher( ResourceType type, String name, ResourceWatcher watcher) { FakeClock.TaskFilter timeoutTaskFilter; @@ -2268,7 +2381,8 @@ public abstract class ClientXdsClientTestBase { protected abstract Message buildEdsCluster(String clusterName, @Nullable String edsServiceName, String lbPolicy, @Nullable Message ringHashLbConfig, boolean enableLrs, - @Nullable Message upstreamTlsContext, @Nullable Message circuitBreakers); + @Nullable Message upstreamTlsContext, String transportSocketName, + @Nullable Message circuitBreakers); protected abstract Message buildLogicalDnsCluster(String clusterName, String dnsHostAddr, int dnsHostPort, String lbPolicy, @Nullable Message ringHashLbConfig, boolean enableLrs, @@ -2280,7 +2394,7 @@ public abstract class ClientXdsClientTestBase { protected abstract Message buildRingHashLbConfig(String hashFunction, long minRingSize, long maxRingSize); - protected abstract Message buildUpstreamTlsContext(String secretName, String targetUri); + protected abstract Message buildUpstreamTlsContext(String instanceName, String certName); protected abstract Message buildCircuitBreakers(int highPriorityMaxRequests, int defaultPriorityMaxRequests); @@ -2305,7 +2419,8 @@ public abstract class ClientXdsClientTestBase { protected abstract Message buildDropOverload(String category, int dropPerMillion); protected abstract Message buildFilterChain( - List alpn, Message tlsContext, Message... filters); + List alpn, Message tlsContext, String transportSocketName, + Message... filters); protected abstract Message buildListenerWithFilterChain( String name, int portValue, String address, Message... filterChains); diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientV2Test.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientV2Test.java index 8edeb97f70..409613aecf 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientV2Test.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientV2Test.java @@ -393,7 +393,8 @@ public class ClientXdsClientV2Test extends ClientXdsClientTestBase { @Override protected Message buildEdsCluster(String clusterName, @Nullable String edsServiceName, String lbPolicy, @Nullable Message ringHashLbConfig, boolean enableLrs, - @Nullable Message upstreamTlsContext, @Nullable Message circuitBreakers) { + @Nullable Message upstreamTlsContext, String transportSocketName, + @Nullable Message circuitBreakers) { Cluster.Builder builder = initClusterBuilder(clusterName, lbPolicy, ringHashLbConfig, enableLrs, upstreamTlsContext, circuitBreakers); builder.setType(DiscoveryType.EDS); @@ -493,10 +494,10 @@ public class ClientXdsClientV2Test extends ClientXdsClientTestBase { } @Override - protected Message buildUpstreamTlsContext(String secretName, String targetUri) { + protected Message buildUpstreamTlsContext(String instanceName, String certName) { GrpcService grpcService = GrpcService.newBuilder() - .setGoogleGrpc(GoogleGrpc.newBuilder().setTargetUri(targetUri)) + .setGoogleGrpc(GoogleGrpc.newBuilder().setTargetUri(certName)) .build(); ConfigSource sdsConfig = ConfigSource.newBuilder() @@ -504,7 +505,7 @@ public class ClientXdsClientV2Test extends ClientXdsClientTestBase { .build(); SdsSecretConfig validationContextSdsSecretConfig = SdsSecretConfig.newBuilder() - .setName(secretName) + .setName(instanceName) .setSdsConfig(sdsConfig) .build(); return UpstreamTlsContext.newBuilder() @@ -618,7 +619,8 @@ public class ClientXdsClientV2Test extends ClientXdsClientTestBase { } @Override - protected Message buildFilterChain(List alpn, Message tlsContext, Message... filters) { + protected Message buildFilterChain(List alpn, Message tlsContext, + String transportSocketName, Message... filters) { throw new UnsupportedOperationException(); } diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientV3Test.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientV3Test.java index 031ae716df..0da6bf7bde 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientV3Test.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientV3Test.java @@ -42,10 +42,7 @@ import io.envoyproxy.envoy.config.cluster.v3.Cluster.RingHashLbConfig; import io.envoyproxy.envoy.config.cluster.v3.Cluster.RingHashLbConfig.HashFunction; import io.envoyproxy.envoy.config.core.v3.Address; import io.envoyproxy.envoy.config.core.v3.AggregatedConfigSource; -import io.envoyproxy.envoy.config.core.v3.ApiConfigSource; import io.envoyproxy.envoy.config.core.v3.ConfigSource; -import io.envoyproxy.envoy.config.core.v3.GrpcService; -import io.envoyproxy.envoy.config.core.v3.GrpcService.GoogleGrpc; import io.envoyproxy.envoy.config.core.v3.HealthStatus; import io.envoyproxy.envoy.config.core.v3.Locality; import io.envoyproxy.envoy.config.core.v3.Node; @@ -81,7 +78,6 @@ import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3 import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter; import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds; import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext; -import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.SdsSecretConfig; import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext; import io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceImplBase; import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; @@ -437,9 +433,10 @@ public class ClientXdsClientV3Test extends ClientXdsClientTestBase { @Override protected Message buildEdsCluster(String clusterName, @Nullable String edsServiceName, String lbPolicy, @Nullable Message ringHashLbConfig, boolean enableLrs, - @Nullable Message upstreamTlsContext, @Nullable Message circuitBreakers) { + @Nullable Message upstreamTlsContext, String transportSocketName, + @Nullable Message circuitBreakers) { Cluster.Builder builder = initClusterBuilder(clusterName, lbPolicy, ringHashLbConfig, - enableLrs, upstreamTlsContext, circuitBreakers); + enableLrs, upstreamTlsContext, transportSocketName, circuitBreakers); builder.setType(DiscoveryType.EDS); EdsClusterConfig.Builder edsClusterConfigBuilder = EdsClusterConfig.newBuilder(); edsClusterConfigBuilder.setEdsConfig( @@ -456,7 +453,7 @@ public class ClientXdsClientV3Test extends ClientXdsClientTestBase { int dnsHostPort, String lbPolicy, @Nullable Message ringHashLbConfig, boolean enableLrs, @Nullable Message upstreamTlsContext, @Nullable Message circuitBreakers) { Cluster.Builder builder = initClusterBuilder(clusterName, lbPolicy, ringHashLbConfig, - enableLrs, upstreamTlsContext, circuitBreakers); + enableLrs, upstreamTlsContext, "envoy.transport_sockets.tls", circuitBreakers); builder.setType(DiscoveryType.LOGICAL_DNS); builder.setLoadAssignment( ClusterLoadAssignment.newBuilder().addEndpoints( @@ -492,7 +489,8 @@ public class ClientXdsClientV3Test extends ClientXdsClientTestBase { private Cluster.Builder initClusterBuilder(String clusterName, String lbPolicy, @Nullable Message ringHashLbConfig, boolean enableLrs, - @Nullable Message upstreamTlsContext, @Nullable Message circuitBreakers) { + @Nullable Message upstreamTlsContext, String transportSocketName, + @Nullable Message circuitBreakers) { Cluster.Builder builder = Cluster.newBuilder(); builder.setName(clusterName); if (lbPolicy.equals("round_robin")) { @@ -511,7 +509,7 @@ public class ClientXdsClientV3Test extends ClientXdsClientTestBase { if (upstreamTlsContext != null) { builder.setTransportSocket( TransportSocket.newBuilder() - .setName("envoy.transport_sockets.tls") + .setName(transportSocketName) .setTypedConfig(Any.pack(upstreamTlsContext))); } if (circuitBreakers != null) { @@ -537,24 +535,22 @@ public class ClientXdsClientV3Test extends ClientXdsClientTestBase { } @Override - protected Message buildUpstreamTlsContext(String secretName, String targetUri) { - GrpcService grpcService = - GrpcService.newBuilder() - .setGoogleGrpc(GoogleGrpc.newBuilder().setTargetUri(targetUri)) - .build(); - ConfigSource sdsConfig = - ConfigSource.newBuilder() - .setApiConfigSource(ApiConfigSource.newBuilder().addGrpcServices(grpcService)) - .build(); - SdsSecretConfig validationContextSdsSecretConfig = - SdsSecretConfig.newBuilder() - .setName(secretName) - .setSdsConfig(sdsConfig) - .build(); + protected Message buildUpstreamTlsContext(String instanceName, String certName) { + CommonTlsContext.Builder commonTlsContextBuilder = CommonTlsContext.newBuilder(); + if (instanceName != null && certName != null) { + CommonTlsContext.CertificateProviderInstance providerInstance = + CommonTlsContext.CertificateProviderInstance.newBuilder() + .setInstanceName(instanceName) + .setCertificateName(certName) + .build(); + CommonTlsContext.CombinedCertificateValidationContext combined = + CommonTlsContext.CombinedCertificateValidationContext.newBuilder() + .setValidationContextCertificateProviderInstance(providerInstance) + .build(); + commonTlsContextBuilder.setCombinedValidationContext(combined); + } return UpstreamTlsContext.newBuilder() - .setCommonTlsContext( - CommonTlsContext.newBuilder() - .setValidationContextSdsSecretConfig(validationContextSdsSecretConfig)) + .setCommonTlsContext(commonTlsContextBuilder) .build(); } @@ -664,7 +660,8 @@ public class ClientXdsClientV3Test extends ClientXdsClientTestBase { @SuppressWarnings("deprecation") @Override protected FilterChain buildFilterChain( - List alpn, Message tlsContext, Message... filters) { + List alpn, Message tlsContext, String transportSocketName, + Message... filters) { FilterChainMatch filterChainMatch = FilterChainMatch.newBuilder().addAllApplicationProtocols(alpn).build(); Filter[] filterArray = new Filter[filters.length]; @@ -677,7 +674,7 @@ public class ClientXdsClientV3Test extends ClientXdsClientTestBase { tlsContext == null ? TransportSocket.getDefaultInstance() : TransportSocket.newBuilder() - .setName("envoy.transport_sockets.tls") + .setName(transportSocketName) .setTypedConfig(Any.pack(tlsContext)) .build()) .addAllFilters(Arrays.asList(filterArray)) 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 35e2f35818..2914e5f393 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 @@ -84,12 +84,14 @@ public class CommonTlsContextTestsUtil { : CertificateValidationContext.newBuilder() .addAllMatchSubjectAltNames(matchSubjectAltNames) .build(); - if (validationCertificateProviderInstance != null && certValidationContext != null) { + if (validationCertificateProviderInstance != null) { CombinedCertificateValidationContext.Builder combinedBuilder = CombinedCertificateValidationContext.newBuilder() - .setDefaultValidationContext(certValidationContext) .setValidationContextCertificateProviderInstance( validationCertificateProviderInstance); + if (certValidationContext != null) { + combinedBuilder = combinedBuilder.setDefaultValidationContext(certValidationContext); + } builder.setCombinedValidationContext(combinedBuilder); } else if (validationCertificateProviderInstance != null) { builder @@ -106,12 +108,14 @@ public class CommonTlsContextTestsUtil { /** Helper method to build DownstreamTlsContext for multiple test classes. */ static DownstreamTlsContext buildDownstreamTlsContext( CommonTlsContext commonTlsContext, boolean requireClientCert) { - DownstreamTlsContext downstreamTlsContext = + DownstreamTlsContext.Builder downstreamTlsContextBuilder = DownstreamTlsContext.newBuilder() - .setCommonTlsContext(commonTlsContext) - .setRequireClientCertificate(BoolValue.of(requireClientCert)) - .build(); - return downstreamTlsContext; + .setRequireClientCertificate(BoolValue.of(requireClientCert)); + if (commonTlsContext != null) { + downstreamTlsContextBuilder = downstreamTlsContextBuilder + .setCommonTlsContext(commonTlsContext); + } + return downstreamTlsContextBuilder.build(); } /** Helper method to build DownstreamTlsContext for multiple test classes. */ @@ -137,23 +141,25 @@ public class CommonTlsContextTestsUtil { /** Helper method for creating DownstreamTlsContext values with names. */ public static DownstreamTlsContext buildTestDownstreamTlsContext( - String certName, String validationContextCertName) { - return buildDownstreamTlsContext( - buildCommonTlsContextWithAdditionalValues( - "cert-instance-name", certName, - "val-cert-instance-name", validationContextCertName, - Arrays.asList( - StringMatcher.newBuilder() - .setExact("spiffe://grpc-sds-testing.svc.id.goog/ns/default/sa/bob") - .build()), - Arrays.asList("managed-tls")), - /* requireClientCert= */ false); + String certName, String validationContextCertName, boolean useSans) { + CommonTlsContext commonTlsContext = null; + if (certName != null || validationContextCertName != null || useSans) { + commonTlsContext = buildCommonTlsContextWithAdditionalValues( + "cert-instance-name", certName, + "val-cert-instance-name", validationContextCertName, + useSans ? Arrays.asList( + StringMatcher.newBuilder() + .setExact("spiffe://grpc-sds-testing.svc.id.goog/ns/default/sa/bob") + .build()) : null, + Arrays.asList("managed-tls")); + } + return buildDownstreamTlsContext(commonTlsContext, /* requireClientCert= */ false); } public static EnvoyServerProtoData.DownstreamTlsContext buildTestInternalDownstreamTlsContext( String certName, String validationContextName) { return EnvoyServerProtoData.DownstreamTlsContext.fromEnvoyProtoDownstreamTlsContext( - buildTestDownstreamTlsContext(certName, validationContextName)); + buildTestDownstreamTlsContext(certName, validationContextName, true)); } public static String getTempFileNameForResourcesFile(String resFile) throws IOException {