snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / vet-proto (push) Failing after 1s
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / vet-proto (push) Failing after 1s
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
This commit is contained in:
851
test/xds/xds_client_sni_test.go
Normal file
851
test/xds/xds_client_sni_test.go
Normal file
@@ -0,0 +1,851 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2026 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 xds_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
xdscreds "google.golang.org/grpc/credentials/xds"
|
||||
"google.golang.org/grpc/internal/envconfig"
|
||||
"google.golang.org/grpc/internal/stubserver"
|
||||
"google.golang.org/grpc/internal/testutils"
|
||||
"google.golang.org/grpc/internal/testutils/xds/e2e"
|
||||
"google.golang.org/grpc/internal/testutils/xds/e2e/setup"
|
||||
"google.golang.org/grpc/peer"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/resolver/manual"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
|
||||
v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||
v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||
v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
// validSNI matches the wildcard SAN (*.test.example.com) in the test server
|
||||
// certificate.
|
||||
const validSNI = "x.test.example.com"
|
||||
|
||||
// Tests the SNI and SAN validation logic by verifying that RPCs succeed when
|
||||
// AutoSNISANValidation is enabled and the SNI matches a server certificate DNS
|
||||
// SAN. Also verifies that RPCs fail with an 'Unavailable' status if the SNI is
|
||||
// present but does not match any DNS SAN in the certificate.
|
||||
func (s) TestClientSideXDS_SNISANValidation(t *testing.T) {
|
||||
testutils.SetEnvConfig(t, &envconfig.XDSSNIEnabled, true)
|
||||
|
||||
// Spin up an xDS management server.
|
||||
mgmtServer, nodeID, _, xdsResolver := setup.ManagementServerAndResolver(t)
|
||||
|
||||
// Create test backends for two clusters:
|
||||
// - backend1 configured with TLS creds, represents cluster1 (valid SNI)
|
||||
// - backend2 configured with TLS creds, represents cluster2 (invalid SNI)
|
||||
serverCreds := testutils.CreateServerTLSCredentials(t, tls.RequireAndVerifyClientCert)
|
||||
server1 := stubserver.StartTestService(t, nil, grpc.Creds(serverCreds))
|
||||
defer server1.Stop()
|
||||
server2 := stubserver.StartTestService(t, nil, grpc.Creds(serverCreds))
|
||||
defer server2.Stop()
|
||||
|
||||
const serviceName = "my-service-client-side-xds"
|
||||
const routeConfigName = "route-" + serviceName
|
||||
const clusterName1 = "cluster1-" + serviceName
|
||||
const clusterName2 = "cluster2-" + serviceName
|
||||
const endpointName1 = "endpoint1-" + serviceName
|
||||
const endpointName2 = "endpoint2-" + serviceName
|
||||
|
||||
listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(serviceName, routeConfigName)}
|
||||
|
||||
// Route configuration:
|
||||
// - "/grpc.testing.TestService/EmptyCall" --> cluster1 (valid SNI)
|
||||
// - "/grpc.testing.TestService/UnaryCall" --> cluster2 (invalid SNI)
|
||||
routes := []*v3routepb.RouteConfiguration{{
|
||||
Name: routeConfigName,
|
||||
VirtualHosts: []*v3routepb.VirtualHost{{
|
||||
Domains: []string{serviceName},
|
||||
Routes: []*v3routepb.Route{
|
||||
{
|
||||
Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/grpc.testing.TestService/EmptyCall"}},
|
||||
Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName1}}},
|
||||
},
|
||||
{
|
||||
Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/grpc.testing.TestService/UnaryCall"}},
|
||||
Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName2}}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}}
|
||||
|
||||
// Configure cluster1 with valid SNI and AutoSniSanValidation set to true.
|
||||
cluster1 := e2e.DefaultCluster(clusterName1, endpointName1, e2e.SecurityLevelMTLS)
|
||||
cluster1.TransportSocket = &v3corepb.TransportSocket{
|
||||
Name: "envoy.transport_sockets.tls",
|
||||
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
||||
TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
|
||||
Sni: validSNI,
|
||||
AutoSniSanValidation: true,
|
||||
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
||||
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
|
||||
ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{InstanceName: e2e.ClientSideCertProviderInstance},
|
||||
},
|
||||
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{InstanceName: e2e.ClientSideCertProviderInstance},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
// Configure cluster2 with invalid SNI and AutoSniSanValidation set to true.
|
||||
cluster2 := e2e.DefaultCluster(clusterName2, endpointName2, e2e.SecurityLevelMTLS)
|
||||
cluster2.TransportSocket = &v3corepb.TransportSocket{
|
||||
Name: "envoy.transport_sockets.tls",
|
||||
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
||||
TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
|
||||
Sni: "wrong.sni.domain",
|
||||
AutoSniSanValidation: true,
|
||||
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
||||
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
|
||||
ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "root",
|
||||
},
|
||||
},
|
||||
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "identity",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
clusters := []*v3clusterpb.Cluster{cluster1, cluster2}
|
||||
endpoints := []*v3endpointpb.ClusterLoadAssignment{
|
||||
e2e.DefaultEndpoint(endpointName1, "localhost", []uint32{testutils.ParsePort(t, server1.Address)}),
|
||||
e2e.DefaultEndpoint(endpointName2, "localhost", []uint32{testutils.ParsePort(t, server2.Address)}),
|
||||
}
|
||||
|
||||
resources := e2e.UpdateOptions{
|
||||
NodeID: nodeID,
|
||||
Listeners: listeners,
|
||||
Routes: routes,
|
||||
Clusters: clusters,
|
||||
Endpoints: endpoints,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
||||
defer cancel()
|
||||
if err := mgmtServer.Update(ctx, resources); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
clientCreds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(clientCreds), grpc.WithResolvers(xdsResolver))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial local test server: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
client := testgrpc.NewTestServiceClient(cc)
|
||||
// RPC to cluster1 should succeed because auto_sni_san_validation is true
|
||||
// and sni matches server cert SAN.
|
||||
peerInfo := &peer.Peer{}
|
||||
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(peerInfo)); err != nil {
|
||||
t.Fatalf("EmptyCall() failed: %v", err)
|
||||
}
|
||||
if got, want := peerInfo.Addr.String(), server1.Address; got != want {
|
||||
t.Fatalf("EmptyCall() routed to %q, want to be routed to: %q", got, want)
|
||||
}
|
||||
|
||||
// RPC to cluster2 should fail because even though auto_sni_san_validation
|
||||
// is true, sni doesn't match server cert SAN.
|
||||
const wantErr = "do not match the SNI"
|
||||
if _, err := client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != codes.Unavailable || !strings.Contains(err.Error(), wantErr) {
|
||||
t.Fatalf("UnaryCall() failed: %v, wantCode: %s, wantErr: %s", err, codes.Unavailable, wantErr)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that when AutoHostSNI is enabled, the endpoint's hostname is used for
|
||||
// the ServerName even though SNI is specified. It verifies that the TLS
|
||||
// handshake and subsequent RPC succeed because the resolved SNI i.e. the
|
||||
// hostname matches the server's certificate SAN.
|
||||
func (s) TestClientSideXDS_AutoHostSNI(t *testing.T) {
|
||||
testutils.SetEnvConfig(t, &envconfig.XDSSNIEnabled, true)
|
||||
|
||||
// Spin up an xDS management server.
|
||||
mgmtServer, nodeID, _, xdsResolver := setup.ManagementServerAndResolver(t)
|
||||
|
||||
// Create test backend
|
||||
serverCreds := testutils.CreateServerTLSCredentials(t, tls.RequireAndVerifyClientCert)
|
||||
server := stubserver.StartTestService(t, nil, grpc.Creds(serverCreds))
|
||||
defer server.Stop()
|
||||
|
||||
// Configure client side xDS resources on the management server.
|
||||
const serviceName = "my-service-client-side-xds"
|
||||
const routeConfigName = "route-" + serviceName
|
||||
const clusterName = "cluster-" + serviceName
|
||||
const endpointName = "endpoint-" + serviceName
|
||||
|
||||
listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(serviceName, routeConfigName)}
|
||||
routes := []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeConfigName, serviceName, clusterName)}
|
||||
|
||||
// Configure the cluster with AutoHostSni and AutoSniSanValidation set to
|
||||
// true with a invalid SNI.
|
||||
cluster := e2e.DefaultCluster(clusterName, endpointName, e2e.SecurityLevelMTLS)
|
||||
cluster.TransportSocket = &v3corepb.TransportSocket{
|
||||
Name: "envoy.transport_sockets.tls",
|
||||
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
||||
TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
|
||||
AutoHostSni: true,
|
||||
AutoSniSanValidation: true,
|
||||
Sni: "wrong.sni.domain",
|
||||
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
||||
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
|
||||
ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "root",
|
||||
},
|
||||
},
|
||||
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "identity",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
// Endpoints configuring Hostname to the defaultTestCertSAN to verify
|
||||
// AutoHostSni usage.
|
||||
endpoints := []*v3endpointpb.ClusterLoadAssignment{
|
||||
e2e.EndpointResourceWithOptions(e2e.EndpointOptions{
|
||||
ClusterName: endpointName,
|
||||
Host: "localhost",
|
||||
Localities: []e2e.LocalityOptions{{
|
||||
Weight: 1,
|
||||
Backends: []e2e.BackendOptions{{
|
||||
Ports: []uint32{testutils.ParsePort(t, server.Address)},
|
||||
Hostname: validSNI,
|
||||
}},
|
||||
}},
|
||||
}),
|
||||
}
|
||||
|
||||
resources := e2e.UpdateOptions{
|
||||
NodeID: nodeID,
|
||||
Listeners: listeners,
|
||||
Routes: routes,
|
||||
Clusters: []*v3clusterpb.Cluster{cluster},
|
||||
Endpoints: endpoints,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
||||
defer cancel()
|
||||
if err := mgmtServer.Update(ctx, resources); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
clientCreds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(clientCreds), grpc.WithResolvers(xdsResolver))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial local test server: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
client := testgrpc.NewTestServiceClient(cc)
|
||||
// RPC should succeed because auto_host_sni sets SNI from the endpoint
|
||||
// hostname and auto_sni_san_validation validates that the SNI matches
|
||||
// server cert SAN.
|
||||
peerInfo := &peer.Peer{}
|
||||
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(peerInfo)); err != nil {
|
||||
t.Fatalf("EmptyCall() failed: %v", err)
|
||||
}
|
||||
if got, want := peerInfo.Addr.String(), server.Address; got != want {
|
||||
t.Fatalf("EmptyCall() routed to %q, want to be routed to: %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests the scenario where multiple endpoints are configured with distinct
|
||||
// hostnames. It verifies that the client uses the specific hostname associated
|
||||
// with each endpoint during the TLS handshake by simulating an EDS failover and
|
||||
// confirming that the SNI is dynamically updated to match the active endpoint.
|
||||
func (s) TestClientSideXDS_AutoHostSNI_MultipleEndpoints(t *testing.T) {
|
||||
testutils.SetEnvConfig(t, &envconfig.XDSSNIEnabled, true)
|
||||
|
||||
// Spin up an xDS management server.
|
||||
mgmtServer, nodeID, _, xdsResolver := setup.ManagementServerAndResolver(t)
|
||||
|
||||
// Create test backends.
|
||||
serverCreds := testutils.CreateServerTLSCredentials(t, tls.RequireAndVerifyClientCert)
|
||||
server1 := stubserver.StartTestService(t, nil, grpc.Creds(serverCreds))
|
||||
defer server1.Stop()
|
||||
server2 := stubserver.StartTestService(t, nil, grpc.Creds(serverCreds))
|
||||
defer server2.Stop()
|
||||
|
||||
// Configure client side xDS resources on the management server.
|
||||
const serviceName = "my-service-client-side-xds"
|
||||
const routeConfigName = "route-" + serviceName
|
||||
const clusterName = "cluster-" + serviceName
|
||||
const endpointName = "endpoint-" + serviceName
|
||||
|
||||
listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(serviceName, routeConfigName)}
|
||||
routes := []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeConfigName, serviceName, clusterName)}
|
||||
|
||||
clusters := []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, endpointName, e2e.SecurityLevelMTLS)}
|
||||
clusters[0].TransportSocket = &v3corepb.TransportSocket{
|
||||
Name: "envoy.transport_sockets.tls",
|
||||
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
||||
TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
|
||||
AutoHostSni: true,
|
||||
AutoSniSanValidation: true,
|
||||
Sni: "wrong.sni.domain",
|
||||
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
||||
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
|
||||
ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "root",
|
||||
},
|
||||
},
|
||||
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "identity",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
// Configure only Priority 0 with an invalid hostname. RPC should fail due
|
||||
// to SAN mismatch.
|
||||
endpoints := []*v3endpointpb.ClusterLoadAssignment{
|
||||
e2e.EndpointResourceWithOptions(e2e.EndpointOptions{
|
||||
ClusterName: endpointName,
|
||||
Host: "localhost",
|
||||
Localities: []e2e.LocalityOptions{
|
||||
{
|
||||
Name: "locality-0",
|
||||
Weight: 1,
|
||||
Priority: 0,
|
||||
Backends: []e2e.BackendOptions{{
|
||||
Ports: []uint32{testutils.ParsePort(t, server1.Address)},
|
||||
Hostname: "wrong.sni.domain",
|
||||
}},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
resources := e2e.UpdateOptions{
|
||||
NodeID: nodeID,
|
||||
Listeners: listeners,
|
||||
Routes: routes,
|
||||
Clusters: clusters,
|
||||
Endpoints: endpoints,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
||||
defer cancel()
|
||||
if err := mgmtServer.Update(ctx, resources); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
clientCreds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(clientCreds), grpc.WithResolvers(xdsResolver))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial local test server: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
client := testgrpc.NewTestServiceClient(cc)
|
||||
|
||||
// RPC should fail because hostname for endpoints in Priority 0 is invalid.
|
||||
if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err == nil {
|
||||
t.Fatalf("EmptyCall() unexpectedly succeeded with invalid SNI and no failover")
|
||||
} else if !strings.Contains(err.Error(), "authentication handshake failed") {
|
||||
t.Fatalf("EmptyCall() failed with %v, want error containing 'authentication handshake failed'", err)
|
||||
}
|
||||
|
||||
// Update the same EDS resource to add Priority 1 with an endpoint with
|
||||
// valid hostname.
|
||||
endpoints = []*v3endpointpb.ClusterLoadAssignment{
|
||||
e2e.EndpointResourceWithOptions(e2e.EndpointOptions{
|
||||
ClusterName: endpointName,
|
||||
Host: "localhost",
|
||||
Localities: []e2e.LocalityOptions{
|
||||
{
|
||||
Name: "locality-0",
|
||||
Weight: 1,
|
||||
Priority: 0,
|
||||
Backends: []e2e.BackendOptions{{
|
||||
Ports: []uint32{testutils.ParsePort(t, server1.Address)},
|
||||
Hostname: "wrong.sni.domain",
|
||||
}},
|
||||
},
|
||||
{
|
||||
Name: "locality-1",
|
||||
Weight: 1,
|
||||
Priority: 1,
|
||||
Backends: []e2e.BackendOptions{{
|
||||
Ports: []uint32{testutils.ParsePort(t, server2.Address)},
|
||||
Hostname: validSNI,
|
||||
}},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
resources.Endpoints = endpoints
|
||||
if err := mgmtServer.Update(ctx, resources); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// RPC should succeed because it automatically fails over to Priority 1. Id
|
||||
// only one hostname would have been used per leaf cluster , this RPC should
|
||||
// have failed due to invalid SNI in Priority 0.
|
||||
peerInfo := &peer.Peer{}
|
||||
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(peerInfo), grpc.WaitForReady(true)); err != nil {
|
||||
t.Fatalf("EmptyCall() failed after adding Priority 1: %v", err)
|
||||
}
|
||||
if got, want := peerInfo.Addr.String(), server2.Address; got != want {
|
||||
t.Fatalf("EmptyCall() successes routed to %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestClientSideXDS_FallbackSANMatchers tests that when AutoSniSanValidation is
|
||||
// true, and no SNI is provided by the control plane and AutoHostSNI is not set,
|
||||
// the validation falls back to using the explicit SAN matchers specified in the
|
||||
// configuration. It verifies that RPCs succeed when the fallback matchers match
|
||||
// the server certificate SAN and fail when they do not.
|
||||
func (s) TestClientSideXDS_FallbackSANMatchers(t *testing.T) {
|
||||
testutils.SetEnvConfig(t, &envconfig.XDSSNIEnabled, true)
|
||||
|
||||
// Spin up an xDS management server.
|
||||
mgmtServer, nodeID, _, xdsResolver := setup.ManagementServerAndResolver(t)
|
||||
|
||||
// Create test backends.
|
||||
serverCreds := testutils.CreateServerTLSCredentials(t, tls.RequireAndVerifyClientCert)
|
||||
server1 := stubserver.StartTestService(t, nil, grpc.Creds(serverCreds))
|
||||
defer server1.Stop()
|
||||
server2 := stubserver.StartTestService(t, nil, grpc.Creds(serverCreds))
|
||||
defer server2.Stop()
|
||||
|
||||
// Configure client side xDS resources on the management server.
|
||||
const serviceName = "my-service-client-side-xds"
|
||||
const routeConfigName = "route-" + serviceName
|
||||
const clusterName1 = "cluster1-" + serviceName
|
||||
const clusterName2 = "cluster2-" + serviceName
|
||||
const endpointName1 = "endpoint1-" + serviceName
|
||||
const endpointName2 = "endpoint2-" + serviceName
|
||||
|
||||
listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(serviceName, routeConfigName)}
|
||||
|
||||
routes := []*v3routepb.RouteConfiguration{{
|
||||
Name: routeConfigName,
|
||||
VirtualHosts: []*v3routepb.VirtualHost{{
|
||||
Domains: []string{serviceName},
|
||||
Routes: []*v3routepb.Route{
|
||||
{
|
||||
Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/grpc.testing.TestService/EmptyCall"}},
|
||||
Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName1}}},
|
||||
},
|
||||
{
|
||||
Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/grpc.testing.TestService/UnaryCall"}},
|
||||
Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName2}}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}}
|
||||
|
||||
// Configure cluster1 with AutoSniSanValidation set to true and no SNI
|
||||
// provided for the handshake. The validation falls back to using the explicit
|
||||
// SAN matchers specified in the configuration which matches the server1's
|
||||
// certificate SAN.
|
||||
cluster1 := e2e.DefaultCluster(clusterName1, endpointName1, e2e.SecurityLevelMTLS)
|
||||
cluster1.TransportSocket = &v3corepb.TransportSocket{
|
||||
Name: "envoy.transport_sockets.tls",
|
||||
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
||||
TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
|
||||
AutoSniSanValidation: true,
|
||||
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
||||
ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
|
||||
CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
|
||||
DefaultValidationContext: &v3tlspb.CertificateValidationContext{
|
||||
MatchSubjectAltNames: []*v3matcherpb.StringMatcher{
|
||||
{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "*.test.example.com"}},
|
||||
},
|
||||
CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "root",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "identity",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
// Configure cluster2 with AutoSniSanValidation set to true and no SNI
|
||||
// provided for the handshake. The validation falls back to using the explicit
|
||||
// SAN matchers specified in the configuration which does not match the
|
||||
// server2's certificate SAN.
|
||||
cluster2 := e2e.DefaultCluster(clusterName2, endpointName2, e2e.SecurityLevelMTLS)
|
||||
cluster2.TransportSocket = &v3corepb.TransportSocket{
|
||||
Name: "envoy.transport_sockets.tls",
|
||||
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
||||
TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
|
||||
AutoSniSanValidation: true,
|
||||
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
||||
ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
|
||||
CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
|
||||
DefaultValidationContext: &v3tlspb.CertificateValidationContext{
|
||||
MatchSubjectAltNames: []*v3matcherpb.StringMatcher{{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "wrong.san.domain"}}},
|
||||
CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "root",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "identity",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
clusters := []*v3clusterpb.Cluster{cluster1, cluster2}
|
||||
endpoints := []*v3endpointpb.ClusterLoadAssignment{
|
||||
e2e.DefaultEndpoint(endpointName1, "localhost", []uint32{testutils.ParsePort(t, server1.Address)}),
|
||||
e2e.DefaultEndpoint(endpointName2, "localhost", []uint32{testutils.ParsePort(t, server2.Address)}),
|
||||
}
|
||||
|
||||
resources := e2e.UpdateOptions{
|
||||
NodeID: nodeID,
|
||||
Listeners: listeners,
|
||||
Routes: routes,
|
||||
Clusters: clusters,
|
||||
Endpoints: endpoints,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 50*defaultTestTimeout)
|
||||
defer cancel()
|
||||
if err := mgmtServer.Update(ctx, resources); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
clientCreds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(clientCreds), grpc.WithResolvers(xdsResolver))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial local test server: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
client := testgrpc.NewTestServiceClient(cc)
|
||||
|
||||
// RPC to cluster1 should succeed because fallback SAN mathchers are used
|
||||
// and they match server cert SAN.
|
||||
peerInfo := &peer.Peer{}
|
||||
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(peerInfo)); err != nil {
|
||||
t.Fatalf("EmptyCall() failed: %v", err)
|
||||
}
|
||||
if got, want := peerInfo.Addr.String(), server1.Address; got != want {
|
||||
t.Fatalf("EmptyCall() routed to %q, want to be routed to: %q", got, want)
|
||||
}
|
||||
|
||||
// RPC to cluster2 should fail because fallback SAN matchers are used
|
||||
// but they don't match server cert SAN.
|
||||
const wantErr = "do not match any of the accepted SANs"
|
||||
if _, err := client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != codes.Unavailable || !strings.Contains(err.Error(), wantErr) {
|
||||
t.Fatalf("UnaryCall() failed: %v, wantCode: %s, wantErr: %s", err, codes.Unavailable, wantErr)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that when the XDSSNIEnabled environment variable is set to false, SNI
|
||||
// is not used for validation even if AutoSniSanValidation is true and SNI is
|
||||
// set. It verifies that the system falls back to using explicit SAN matchers if
|
||||
// provided, and the TLS handshake succeeds when they match the server
|
||||
// certificate SAN.
|
||||
func (s) TestClientSideXDS_SNIEnvVarDisabled(t *testing.T) {
|
||||
testutils.SetEnvConfig(t, &envconfig.XDSSNIEnabled, false)
|
||||
|
||||
// Spin up an xDS management server.
|
||||
mgmtServer, nodeID, _, xdsResolver := setup.ManagementServerAndResolver(t)
|
||||
|
||||
// Create test backends.
|
||||
serverCreds := testutils.CreateServerTLSCredentials(t, tls.RequireAndVerifyClientCert)
|
||||
server1 := stubserver.StartTestService(t, nil, grpc.Creds(serverCreds))
|
||||
defer server1.Stop()
|
||||
server2 := stubserver.StartTestService(t, nil, grpc.Creds(serverCreds))
|
||||
defer server2.Stop()
|
||||
|
||||
// Configure client side xDS resources on the management server.
|
||||
const serviceName = "my-service-client-side-xds"
|
||||
const routeConfigName = "route-" + serviceName
|
||||
const clusterName1 = "cluster1-" + serviceName
|
||||
const clusterName2 = "cluster2-" + serviceName
|
||||
const endpointName1 = "endpoint1-" + serviceName
|
||||
const endpointName2 = "endpoint2-" + serviceName
|
||||
|
||||
listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(serviceName, routeConfigName)}
|
||||
|
||||
routes := []*v3routepb.RouteConfiguration{{
|
||||
Name: routeConfigName,
|
||||
VirtualHosts: []*v3routepb.VirtualHost{{
|
||||
Domains: []string{serviceName},
|
||||
Routes: []*v3routepb.Route{
|
||||
{
|
||||
Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/grpc.testing.TestService/EmptyCall"}},
|
||||
Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName1}}},
|
||||
},
|
||||
{
|
||||
Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/grpc.testing.TestService/UnaryCall"}},
|
||||
Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName2}}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}}
|
||||
|
||||
// cluster1 configuration with AutoSniSanValidation set to true and wrong SNI
|
||||
// provided for the handshake. The validation falls back to using the explicit
|
||||
// SAN matchers specified in the configuration which matches the server1's
|
||||
// certificate SAN.
|
||||
cluster1 := e2e.DefaultCluster(clusterName1, endpointName1, e2e.SecurityLevelMTLS)
|
||||
cluster1.TransportSocket = &v3corepb.TransportSocket{
|
||||
Name: "envoy.transport_sockets.tls",
|
||||
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
||||
TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
|
||||
Sni: "incorrect.sni",
|
||||
AutoSniSanValidation: true,
|
||||
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
||||
ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
|
||||
CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
|
||||
DefaultValidationContext: &v3tlspb.CertificateValidationContext{
|
||||
MatchSubjectAltNames: []*v3matcherpb.StringMatcher{
|
||||
{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "*.test.example.com"}},
|
||||
},
|
||||
CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "root",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "identity",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
// Configure cluster2 with AutoSniSanValidation set to true and correct SNI
|
||||
// provided for the handshake. The validation falls back to using the
|
||||
// explicit SAN matchers specified in the configuration which does not match
|
||||
// the server2's certificate SAN.
|
||||
cluster2 := e2e.DefaultCluster(clusterName2, endpointName2, e2e.SecurityLevelMTLS)
|
||||
cluster2.TransportSocket = &v3corepb.TransportSocket{
|
||||
Name: "envoy.transport_sockets.tls",
|
||||
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
||||
TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
|
||||
Sni: validSNI,
|
||||
AutoSniSanValidation: true,
|
||||
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
||||
ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
|
||||
CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
|
||||
DefaultValidationContext: &v3tlspb.CertificateValidationContext{
|
||||
MatchSubjectAltNames: []*v3matcherpb.StringMatcher{{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "wrong.san.domain"}}},
|
||||
CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "root",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
CertificateName: "identity",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
clusters := []*v3clusterpb.Cluster{cluster1, cluster2}
|
||||
endpoints := []*v3endpointpb.ClusterLoadAssignment{
|
||||
e2e.DefaultEndpoint(endpointName1, "localhost", []uint32{testutils.ParsePort(t, server1.Address)}),
|
||||
e2e.DefaultEndpoint(endpointName2, "localhost", []uint32{testutils.ParsePort(t, server2.Address)}),
|
||||
}
|
||||
|
||||
resources := e2e.UpdateOptions{
|
||||
NodeID: nodeID,
|
||||
Listeners: listeners,
|
||||
Routes: routes,
|
||||
Clusters: clusters,
|
||||
Endpoints: endpoints,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 50*defaultTestTimeout)
|
||||
defer cancel()
|
||||
if err := mgmtServer.Update(ctx, resources); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
clientCreds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(clientCreds), grpc.WithResolvers(xdsResolver))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial local test server: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
client := testgrpc.NewTestServiceClient(cc)
|
||||
|
||||
// RPC to cluster1 should succeed because fallback SAN mathchers are used
|
||||
// and they match server cert SAN.
|
||||
peerInfo := &peer.Peer{}
|
||||
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(peerInfo)); err != nil {
|
||||
t.Fatalf("EmptyCall() failed: %v", err)
|
||||
}
|
||||
if got, want := peerInfo.Addr.String(), server1.Address; got != want {
|
||||
t.Fatalf("EmptyCall() routed to %q, want to be routed to: %q", got, want)
|
||||
}
|
||||
|
||||
// RPC to cluster2 should fail because fallback SAN matchers are used but they
|
||||
// don't match server cert SAN.
|
||||
const wantErr = "do not match any of the accepted SANs"
|
||||
if _, err := client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != codes.Unavailable || !strings.Contains(err.Error(), wantErr) {
|
||||
t.Fatalf("UnaryCall() failed: %v, wantCode: %s, wantErr: %s", err, codes.Unavailable, wantErr)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that when AutoHostSNI is enabled for a Logical DNS cluster, the SNI is
|
||||
// resolved from the DNSHostName in the cluster configuration. It verifies that
|
||||
// the TLS handshake succeeds when the DNSHostName matches the server's
|
||||
// certificate SAN.
|
||||
func (s) TestClientSideXDS_AutoHostSNI_LogicalDNS(t *testing.T) {
|
||||
testutils.SetEnvConfig(t, &envconfig.XDSSNIEnabled, true)
|
||||
|
||||
// Spin up an xDS management server.
|
||||
mgmtServer, nodeID, _, xdsResolver := setup.ManagementServerAndResolver(t)
|
||||
|
||||
// Create test backend
|
||||
serverCreds := testutils.CreateServerTLSCredentials(t, tls.RequireAndVerifyClientCert)
|
||||
server := stubserver.StartTestService(t, nil, grpc.Creds(serverCreds))
|
||||
defer server.Stop()
|
||||
|
||||
// Replace DNS resolver with a manual resolver.
|
||||
dnsR := manual.NewBuilderWithScheme("dns")
|
||||
originalDNS := resolver.Get("dns")
|
||||
resolver.Register(dnsR)
|
||||
t.Cleanup(func() { resolver.Register(originalDNS) })
|
||||
|
||||
dnsR.UpdateState(resolver.State{Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: server.Address}}}}})
|
||||
|
||||
// Configure client side xDS resources on the management server.
|
||||
const serviceName = "my-service-client-side-xds"
|
||||
const routeConfigName = "route-" + serviceName
|
||||
const clusterName = "cluster-" + serviceName
|
||||
|
||||
listeners := []*v3listenerpb.Listener{e2e.DefaultClientListener(serviceName, routeConfigName)}
|
||||
routes := []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeConfigName, serviceName, clusterName)}
|
||||
|
||||
// Cluster of Type LogicalDNS. with DNSHostName set to match the server's cert
|
||||
// SAN.
|
||||
cluster := e2e.ClusterResourceWithOptions(e2e.ClusterOptions{
|
||||
Type: e2e.ClusterTypeLogicalDNS,
|
||||
ClusterName: clusterName,
|
||||
DNSHostName: validSNI,
|
||||
DNSPort: uint32(testutils.ParsePort(t, server.Address)),
|
||||
SecurityLevel: e2e.SecurityLevelMTLS,
|
||||
})
|
||||
cluster.TransportSocket.ConfigType.(*v3corepb.TransportSocket_TypedConfig).TypedConfig = testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
|
||||
AutoHostSni: true,
|
||||
AutoSniSanValidation: true,
|
||||
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
||||
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
|
||||
ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
},
|
||||
},
|
||||
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
||||
InstanceName: e2e.ClientSideCertProviderInstance,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
resources := e2e.UpdateOptions{
|
||||
NodeID: nodeID,
|
||||
Listeners: listeners,
|
||||
Routes: routes,
|
||||
Clusters: []*v3clusterpb.Cluster{cluster},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
||||
defer cancel()
|
||||
if err := mgmtServer.Update(ctx, resources); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
clientCreds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(clientCreds), grpc.WithResolvers(xdsResolver))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial local test server: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
client := testgrpc.NewTestServiceClient(cc)
|
||||
// RPC should succeed because DNSHostName matches the server's certificate
|
||||
// SAN.
|
||||
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
|
||||
t.Fatalf("EmptyCall() failed: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user