google.golang.org/grpc@v1.72.2/xds/internal/balancer/cdsbalancer/cdsbalancer_security_test.go (about)

     1  /*
     2   * Copyright 2020 gRPC authors.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cdsbalancer
    18  
    19  import (
    20  	"context"
    21  	"crypto/tls"
    22  	"crypto/x509"
    23  	"encoding/json"
    24  	"fmt"
    25  	"os"
    26  	"strings"
    27  	"testing"
    28  	"unsafe"
    29  
    30  	"github.com/google/go-cmp/cmp"
    31  	"github.com/google/uuid"
    32  	"google.golang.org/grpc"
    33  	"google.golang.org/grpc/attributes"
    34  	"google.golang.org/grpc/balancer"
    35  	"google.golang.org/grpc/connectivity"
    36  	"google.golang.org/grpc/credentials"
    37  	"google.golang.org/grpc/credentials/insecure"
    38  	"google.golang.org/grpc/credentials/tls/certprovider"
    39  	"google.golang.org/grpc/credentials/xds"
    40  	"google.golang.org/grpc/internal"
    41  	"google.golang.org/grpc/internal/balancer/stub"
    42  	xdscredsinternal "google.golang.org/grpc/internal/credentials/xds"
    43  	"google.golang.org/grpc/internal/envconfig"
    44  	"google.golang.org/grpc/internal/stubserver"
    45  	"google.golang.org/grpc/internal/testutils"
    46  	"google.golang.org/grpc/internal/testutils/xds/e2e"
    47  	"google.golang.org/grpc/internal/xds/bootstrap"
    48  	"google.golang.org/grpc/peer"
    49  	"google.golang.org/grpc/resolver"
    50  	"google.golang.org/grpc/resolver/manual"
    51  	"google.golang.org/grpc/serviceconfig"
    52  	"google.golang.org/grpc/testdata"
    53  	"google.golang.org/grpc/xds/internal/xdsclient"
    54  
    55  	v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
    56  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    57  	v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
    58  	v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    59  	testgrpc "google.golang.org/grpc/interop/grpc_testing"
    60  	testpb "google.golang.org/grpc/interop/grpc_testing"
    61  
    62  	_ "google.golang.org/grpc/credentials/tls/certprovider/pemfile" // Register the file watcher certificate provider plugin.
    63  )
    64  
    65  // testCCWrapper wraps a balancer.ClientConn and intercepts NewSubConn and
    66  // returns the xDS handshake info back to the test for inspection.
    67  type testCCWrapper struct {
    68  	balancer.ClientConn
    69  	handshakeInfoCh chan *xdscredsinternal.HandshakeInfo
    70  }
    71  
    72  // NewSubConn forwards the call to the underlying balancer.ClientConn, but
    73  // before that, it validates the following:
    74  //   - there is only one address in the addrs slice
    75  //   - the single address contains xDS handshake information, which is then
    76  //     pushed onto the handshakeInfoCh channel
    77  func (tcc *testCCWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
    78  	if len(addrs) != 1 {
    79  		return nil, fmt.Errorf("NewSubConn got %d addresses, want 1", len(addrs))
    80  	}
    81  	getHI := internal.GetXDSHandshakeInfoForTesting.(func(attr *attributes.Attributes) *unsafe.Pointer)
    82  	hi := getHI(addrs[0].Attributes)
    83  	if hi == nil {
    84  		return nil, fmt.Errorf("NewSubConn got address without xDS handshake info")
    85  	}
    86  
    87  	sc, err := tcc.ClientConn.NewSubConn(addrs, opts)
    88  	select {
    89  	case tcc.handshakeInfoCh <- (*xdscredsinternal.HandshakeInfo)(*hi):
    90  	default:
    91  	}
    92  	return sc, err
    93  }
    94  
    95  // Registers a wrapped cds LB policy for the duration of this test that retains
    96  // all the functionality of the original cds LB policy, but overrides the
    97  // NewSubConn method passed to the policy and makes the xDS handshake
    98  // information passed to NewSubConn available to the test.
    99  //
   100  // Accepts as argument a channel onto which xDS handshake information passed to
   101  // NewSubConn is written to.
   102  func registerWrappedCDSPolicyWithNewSubConnOverride(t *testing.T, ch chan *xdscredsinternal.HandshakeInfo) {
   103  	cdsBuilder := balancer.Get(cdsName)
   104  	internal.BalancerUnregister(cdsBuilder.Name())
   105  	var ccWrapper *testCCWrapper
   106  	stub.Register(cdsBuilder.Name(), stub.BalancerFuncs{
   107  		Init: func(bd *stub.BalancerData) {
   108  			ccWrapper = &testCCWrapper{
   109  				ClientConn:      bd.ClientConn,
   110  				handshakeInfoCh: ch,
   111  			}
   112  			bd.Data = cdsBuilder.Build(ccWrapper, bd.BuildOptions)
   113  		},
   114  		ParseConfig: func(lbCfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
   115  			return cdsBuilder.(balancer.ConfigParser).ParseConfig(lbCfg)
   116  		},
   117  		UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
   118  			bal := bd.Data.(balancer.Balancer)
   119  			return bal.UpdateClientConnState(ccs)
   120  		},
   121  		Close: func(bd *stub.BalancerData) {
   122  			bal := bd.Data.(balancer.Balancer)
   123  			bal.Close()
   124  		},
   125  	})
   126  	t.Cleanup(func() { balancer.Register(cdsBuilder) })
   127  }
   128  
   129  // Common setup for security tests:
   130  //   - creates an xDS client with the specified bootstrap configuration
   131  //   - creates a manual resolver that specifies cds as the top-level LB policy
   132  //   - creates a channel that uses the passed in client creds and the manual
   133  //     resolver
   134  //   - creates a test server that uses the passed in server creds
   135  //
   136  // Returns the following:
   137  // - a client channel to make RPCs
   138  // - address of the test backend server
   139  func setupForSecurityTests(t *testing.T, bootstrapContents []byte, clientCreds, serverCreds credentials.TransportCredentials) (*grpc.ClientConn, string) {
   140  	t.Helper()
   141  
   142  	config, err := bootstrap.NewConfigFromContents(bootstrapContents)
   143  	if err != nil {
   144  		t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents), err)
   145  	}
   146  	pool := xdsclient.NewPool(config)
   147  	xdsClient, xdsClose, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{
   148  		Name: t.Name(),
   149  	})
   150  	if err != nil {
   151  		t.Fatalf("Failed to create xDS client: %v", err)
   152  	}
   153  	t.Cleanup(xdsClose)
   154  
   155  	// Create a manual resolver that configures the CDS LB policy as the
   156  	// top-level LB policy on the channel.
   157  	r := manual.NewBuilderWithScheme("whatever")
   158  	jsonSC := fmt.Sprintf(`{
   159  			"loadBalancingConfig":[{
   160  				"cds_experimental":{
   161  					"cluster": "%s"
   162  				}
   163  			}]
   164  		}`, clusterName)
   165  	scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC)
   166  	state := xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, xdsClient)
   167  	r.InitialState(state)
   168  
   169  	// Create a ClientConn with the specified transport credentials.
   170  	cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(clientCreds), grpc.WithResolvers(r))
   171  	if err != nil {
   172  		t.Fatalf("grpc.NewClient() failed: %v", err)
   173  	}
   174  	cc.Connect()
   175  	t.Cleanup(func() { cc.Close() })
   176  
   177  	// Start a test service backend with the specified transport credentials.
   178  	sOpts := []grpc.ServerOption{}
   179  	if serverCreds != nil {
   180  		sOpts = append(sOpts, grpc.Creds(serverCreds))
   181  	}
   182  	server := stubserver.StartTestService(t, nil, sOpts...)
   183  	t.Cleanup(server.Stop)
   184  
   185  	return cc, server.Address
   186  }
   187  
   188  // Creates transport credentials to be used on the client side that rely on xDS
   189  // to provide the security configuration. It falls back to insecure creds if no
   190  // security information is received from the management server.
   191  func xdsClientCredsWithInsecureFallback(t *testing.T) credentials.TransportCredentials {
   192  	t.Helper()
   193  
   194  	xdsCreds, err := xds.NewClientCredentials(xds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
   195  	if err != nil {
   196  		t.Fatalf("Failed to create xDS credentials: %v", err)
   197  	}
   198  	return xdsCreds
   199  }
   200  
   201  // Creates transport credentials to be used on the server side from certificate
   202  // files in testdata/x509.
   203  //
   204  // The certificate returned by this function has a CommonName of "test-server1".
   205  func tlsServerCreds(t *testing.T) credentials.TransportCredentials {
   206  	t.Helper()
   207  
   208  	cert, err := tls.LoadX509KeyPair(testdata.Path("x509/server1_cert.pem"), testdata.Path("x509/server1_key.pem"))
   209  	if err != nil {
   210  		t.Fatalf("Failed to load server cert and key: %v", err)
   211  
   212  	}
   213  	pemData, err := os.ReadFile(testdata.Path("x509/client_ca_cert.pem"))
   214  	if err != nil {
   215  		t.Fatalf("Failed to read client CA cert: %v", err)
   216  	}
   217  	roots := x509.NewCertPool()
   218  	roots.AppendCertsFromPEM(pemData)
   219  	cfg := &tls.Config{
   220  		Certificates: []tls.Certificate{cert},
   221  		ClientCAs:    roots,
   222  	}
   223  	return credentials.NewTLS(cfg)
   224  }
   225  
   226  // Checks the AuthInfo available in the peer if it matches the expected security
   227  // level of the connection.
   228  func verifySecurityInformationFromPeer(t *testing.T, pr *peer.Peer, wantSecLevel e2e.SecurityLevel) {
   229  	// This is not a true helper in the Go sense, because it does not perform
   230  	// setup or cleanup tasks. Marking it a helper is to ensure that when the
   231  	// test fails, the line information of the caller is outputted instead of
   232  	// from here.
   233  	//
   234  	// And this function directly calls t.Fatalf() instead of returning an error
   235  	// and letting the caller decide what to do with it. This is also OK since
   236  	// all callers will simply end up calling t.Fatalf() with the returned
   237  	// error, and can't add any contextual information of value to the error
   238  	// message.
   239  	t.Helper()
   240  
   241  	switch wantSecLevel {
   242  	case e2e.SecurityLevelNone:
   243  		if pr.AuthInfo.AuthType() != "insecure" {
   244  			t.Fatalf("AuthType() is %s, want insecure", pr.AuthInfo.AuthType())
   245  		}
   246  	case e2e.SecurityLevelMTLS:
   247  		ai, ok := pr.AuthInfo.(credentials.TLSInfo)
   248  		if !ok {
   249  			t.Fatalf("AuthInfo type is %T, want %T", pr.AuthInfo, credentials.TLSInfo{})
   250  		}
   251  		if len(ai.State.PeerCertificates) != 1 {
   252  			t.Fatalf("Number of peer certificates is %d, want 1", len(ai.State.PeerCertificates))
   253  		}
   254  		cert := ai.State.PeerCertificates[0]
   255  		const wantCommonName = "test-server1"
   256  		if cn := cert.Subject.CommonName; cn != wantCommonName {
   257  			t.Fatalf("Common name in peer certificate is %s, want %s", cn, wantCommonName)
   258  		}
   259  	}
   260  }
   261  
   262  // Tests the case where xDS credentials are not in use, but the cds LB policy
   263  // receives a Cluster update with security configuration. Verifies that the
   264  // security configuration is not parsed by the cds LB policy by looking at the
   265  // xDS handshake info passed to NewSubConn.
   266  func (s) TestSecurityConfigWithoutXDSCreds(t *testing.T) {
   267  	// Register a wrapped cds LB policy for the duration of this test that writes
   268  	// the xDS handshake info passed to NewSubConn onto the given channel.
   269  	handshakeInfoCh := make(chan *xdscredsinternal.HandshakeInfo, 1)
   270  	registerWrappedCDSPolicyWithNewSubConnOverride(t, handshakeInfoCh)
   271  
   272  	// Spin up an xDS management server.
   273  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
   274  
   275  	// Create bootstrap configuration pointing to the above management server.
   276  	nodeID := uuid.New().String()
   277  	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
   278  
   279  	// Create a grpc channel with insecure creds talking to a test server with
   280  	// insecure credentials.
   281  	cc, serverAddress := setupForSecurityTests(t, bc, insecure.NewCredentials(), nil)
   282  
   283  	// Configure cluster and endpoints resources in the management server. The
   284  	// cluster resource is configured to return security configuration.
   285  	resources := e2e.UpdateOptions{
   286  		NodeID:         nodeID,
   287  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
   288  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
   289  		SkipValidation: true,
   290  	}
   291  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   292  	defer cancel()
   293  	if err := mgmtServer.Update(ctx, resources); err != nil {
   294  		t.Fatal(err)
   295  	}
   296  
   297  	// Verify that a successful RPC can be made.
   298  	client := testgrpc.NewTestServiceClient(cc)
   299  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   300  		t.Fatalf("EmptyCall() failed: %v", err)
   301  	}
   302  
   303  	// Ensure that the xDS handshake info passed to NewSubConn is empty.
   304  	var gotHI *xdscredsinternal.HandshakeInfo
   305  	select {
   306  	case gotHI = <-handshakeInfoCh:
   307  	case <-ctx.Done():
   308  		t.Fatal("Timeout when waiting to read handshake info passed to NewSubConn")
   309  	}
   310  	wantHI := xdscredsinternal.NewHandshakeInfo(nil, nil, nil, false)
   311  	if !cmp.Equal(gotHI, wantHI) {
   312  		t.Fatalf("NewSubConn got handshake info %+v, want %+v", gotHI, wantHI)
   313  	}
   314  }
   315  
   316  // Tests the case where xDS credentials are in use, but the cds LB policy
   317  // receives a Cluster update without security configuration. Verifies that the
   318  // xDS handshake info passed to NewSubConn specified the use of fallback
   319  // credentials.
   320  func (s) TestNoSecurityConfigWithXDSCreds(t *testing.T) {
   321  	// Register a wrapped cds LB policy for the duration of this test that writes
   322  	// the xDS handshake info passed to NewSubConn onto the given channel.
   323  	handshakeInfoCh := make(chan *xdscredsinternal.HandshakeInfo, 1)
   324  	registerWrappedCDSPolicyWithNewSubConnOverride(t, handshakeInfoCh)
   325  
   326  	// Spin up an xDS management server.
   327  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
   328  
   329  	// Create bootstrap configuration pointing to the above management server.
   330  	nodeID := uuid.New().String()
   331  	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
   332  
   333  	// Create a grpc channel with xDS creds talking to a test server with
   334  	// insecure credentials.
   335  	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), nil)
   336  
   337  	// Configure cluster and endpoints resources in the management server. The
   338  	// cluster resource is not configured to return any security configuration.
   339  	resources := e2e.UpdateOptions{
   340  		NodeID:         nodeID,
   341  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
   342  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
   343  		SkipValidation: true,
   344  	}
   345  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   346  	defer cancel()
   347  	if err := mgmtServer.Update(ctx, resources); err != nil {
   348  		t.Fatal(err)
   349  	}
   350  
   351  	// Verify that a successful RPC can be made.
   352  	client := testgrpc.NewTestServiceClient(cc)
   353  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   354  		t.Fatalf("EmptyCall() failed: %v", err)
   355  	}
   356  
   357  	// Ensure that the xDS handshake info passed to NewSubConn is empty.
   358  	var gotHI *xdscredsinternal.HandshakeInfo
   359  	select {
   360  	case gotHI = <-handshakeInfoCh:
   361  	case <-ctx.Done():
   362  		t.Fatal("Timeout when waiting to read handshake info passed to NewSubConn")
   363  	}
   364  	wantHI := xdscredsinternal.NewHandshakeInfo(nil, nil, nil, false)
   365  	if !cmp.Equal(gotHI, wantHI) {
   366  		t.Fatalf("NewSubConn got handshake info %+v, want %+v", gotHI, wantHI)
   367  	}
   368  	if !gotHI.UseFallbackCreds() {
   369  		t.Fatal("NewSubConn got handshake info that does not specify the use of fallback creds")
   370  	}
   371  }
   372  
   373  // Tests the case where the security config returned by the management server
   374  // cannot be resolved based on the contents of the bootstrap config. Verifies
   375  // that the cds LB policy puts the channel in TRANSIENT_FAILURE.
   376  func (s) TestSecurityConfigNotFoundInBootstrap(t *testing.T) {
   377  	// Spin up an xDS management server.
   378  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
   379  
   380  	// Create bootstrap configuration pointing to the above management server,
   381  	// and one that does not have certificate providers configuration.
   382  	nodeID := uuid.New().String()
   383  	bootstrapContents, err := bootstrap.NewContentsForTesting(bootstrap.ConfigOptionsForTesting{
   384  		Servers: []byte(fmt.Sprintf(`[{
   385  			"server_uri": %q,
   386  			"channel_creds": [{"type": "insecure"}]
   387  		}]`, mgmtServer.Address)),
   388  		Node:                               []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)),
   389  		ServerListenerResourceNameTemplate: e2e.ServerListenerResourceNameTemplate,
   390  	})
   391  	if err != nil {
   392  		t.Fatalf("Failed to create bootstrap configuration: %v", err)
   393  	}
   394  
   395  	// Create a grpc channel with xDS creds.
   396  	cc, _ := setupForSecurityTests(t, bootstrapContents, xdsClientCredsWithInsecureFallback(t), nil)
   397  
   398  	// Configure a cluster resource that contains security configuration, in the
   399  	// management server.
   400  	resources := e2e.UpdateOptions{
   401  		NodeID:         nodeID,
   402  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
   403  		SkipValidation: true,
   404  	}
   405  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   406  	defer cancel()
   407  	if err := mgmtServer.Update(ctx, resources); err != nil {
   408  		t.Fatal(err)
   409  	}
   410  
   411  	testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
   412  }
   413  
   414  // A certificate provider builder that returns a nil Provider from the starter
   415  // func passed to certprovider.NewBuildableConfig().
   416  type errCertProviderBuilder struct{}
   417  
   418  const errCertProviderName = "err-cert-provider"
   419  
   420  func (e errCertProviderBuilder) ParseConfig(any) (*certprovider.BuildableConfig, error) {
   421  	// Returning a nil Provider simulates the case where an error is encountered
   422  	// at the time of building the Provider.
   423  	bc := certprovider.NewBuildableConfig(errCertProviderName, nil, func(certprovider.BuildOptions) certprovider.Provider { return nil })
   424  	return bc, nil
   425  }
   426  
   427  func (e errCertProviderBuilder) Name() string {
   428  	return errCertProviderName
   429  }
   430  
   431  func init() {
   432  	certprovider.Register(errCertProviderBuilder{})
   433  }
   434  
   435  // Tests the case where the certprovider.Store returns an error when the cds LB
   436  // policy attempts to build a certificate provider. Verifies that the cds LB
   437  // policy puts the channel in TRANSIENT_FAILURE.
   438  func (s) TestCertproviderStoreError(t *testing.T) {
   439  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
   440  
   441  	// Create bootstrap configuration pointing to the above management server
   442  	// and one that includes certificate providers configuration for
   443  	// errCertProviderBuilder.
   444  	nodeID := uuid.New().String()
   445  	providerCfg := json.RawMessage(fmt.Sprintf(`{
   446  		"plugin_name": "%s",
   447  		"config": {}
   448  	}`, errCertProviderName))
   449  	bootstrapContents, err := bootstrap.NewContentsForTesting(bootstrap.ConfigOptionsForTesting{
   450  		Servers: []byte(fmt.Sprintf(`[{
   451  			"server_uri": %q,
   452  			"channel_creds": [{"type": "insecure"}]
   453  		}]`, mgmtServer.Address)),
   454  		Node:                               []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)),
   455  		ServerListenerResourceNameTemplate: e2e.ServerListenerResourceNameTemplate,
   456  		CertificateProviders:               map[string]json.RawMessage{e2e.ClientSideCertProviderInstance: providerCfg},
   457  	})
   458  	if err != nil {
   459  		t.Fatalf("Failed to create bootstrap configuration: %v", err)
   460  	}
   461  
   462  	// Create a grpc channel with xDS creds.
   463  	cc, _ := setupForSecurityTests(t, bootstrapContents, xdsClientCredsWithInsecureFallback(t), nil)
   464  
   465  	// Configure a cluster resource that contains security configuration, in the
   466  	// management server.
   467  	resources := e2e.UpdateOptions{
   468  		NodeID:         nodeID,
   469  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
   470  		SkipValidation: true,
   471  	}
   472  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   473  	defer cancel()
   474  	if err := mgmtServer.Update(ctx, resources); err != nil {
   475  		t.Fatal(err)
   476  	}
   477  
   478  	testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
   479  }
   480  
   481  // Tests the case where the cds LB policy receives security configuration as
   482  // part of the Cluster resource that can be successfully resolved using the
   483  // bootstrap file contents. Verifies that the connection between the client and
   484  // the server is secure.
   485  func (s) TestGoodSecurityConfig(t *testing.T) {
   486  	// Spin up an xDS management server.
   487  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
   488  
   489  	// Create bootstrap configuration pointing to the above management server
   490  	// and one that includes certificate providers configuration.
   491  	nodeID := uuid.New().String()
   492  	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
   493  
   494  	// Create a grpc channel with xDS creds talking to a test server with TLS
   495  	// credentials.
   496  	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), tlsServerCreds(t))
   497  
   498  	// Configure cluster and endpoints resources in the management server. The
   499  	// cluster resource is configured to return security configuration.
   500  	resources := e2e.UpdateOptions{
   501  		NodeID:         nodeID,
   502  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
   503  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
   504  		SkipValidation: true,
   505  	}
   506  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   507  	defer cancel()
   508  	if err := mgmtServer.Update(ctx, resources); err != nil {
   509  		t.Fatal(err)
   510  	}
   511  
   512  	// Verify that a successful RPC can be made over a secure connection.
   513  	client := testgrpc.NewTestServiceClient(cc)
   514  	peer := &peer.Peer{}
   515  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true), grpc.Peer(peer)); err != nil {
   516  		t.Fatalf("EmptyCall() failed: %v", err)
   517  	}
   518  	verifySecurityInformationFromPeer(t, peer, e2e.SecurityLevelMTLS)
   519  }
   520  
   521  // Tests the case where the cds LB policy receives security configuration as
   522  // part of the Cluster resource that contains a certificate provider instance
   523  // that is missing in the bootstrap file. Verifies that the channel moves to
   524  // TRANSIENT_FAILURE. Subsequently, the cds LB policy receives a cluster
   525  // resource that contains a certificate provider that is present in the
   526  // bootstrap file.  Verifies that the connection between the client and the
   527  // server is secure.
   528  func (s) TestSecurityConfigUpdate_BadToGood(t *testing.T) {
   529  	// Spin up an xDS management server.
   530  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
   531  
   532  	// Create bootstrap configuration pointing to the above management server.
   533  	nodeID := uuid.New().String()
   534  	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
   535  
   536  	// Create a grpc channel with xDS creds talking to a test server with TLS
   537  	// credentials.
   538  	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), tlsServerCreds(t))
   539  
   540  	// Configure cluster and endpoints resources in the management server. The
   541  	// cluster resource contains security configuration with a certificate
   542  	// provider instance that is missing in the bootstrap configuration.
   543  	cluster := e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)
   544  	cluster.TransportSocket = &v3corepb.TransportSocket{
   545  		Name: "envoy.transport_sockets.tls",
   546  		ConfigType: &v3corepb.TransportSocket_TypedConfig{
   547  			TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   548  				CommonTlsContext: &v3tlspb.CommonTlsContext{
   549  					ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
   550  						ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   551  							InstanceName: "unknown-certificate-provider-instance",
   552  						},
   553  					},
   554  				},
   555  			}),
   556  		},
   557  	}
   558  	resources := e2e.UpdateOptions{
   559  		NodeID:         nodeID,
   560  		Clusters:       []*v3clusterpb.Cluster{cluster},
   561  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
   562  		SkipValidation: true,
   563  	}
   564  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   565  	defer cancel()
   566  	if err := mgmtServer.Update(ctx, resources); err != nil {
   567  		t.Fatalf("Failed to update management server with initial resources: %v", err)
   568  	}
   569  
   570  	testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
   571  
   572  	// Update the management server with a Cluster resource that contains a
   573  	// certificate provider instance that is present in the bootstrap
   574  	// configuration.
   575  	resources = e2e.UpdateOptions{
   576  		NodeID:         nodeID,
   577  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
   578  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
   579  		SkipValidation: true,
   580  	}
   581  	if err := mgmtServer.Update(ctx, resources); err != nil {
   582  		t.Fatalf("Failed to update management server with valid resources: %v", err)
   583  	}
   584  
   585  	// Verify that a successful RPC can be made over a secure connection.
   586  	client := testgrpc.NewTestServiceClient(cc)
   587  	peer := &peer.Peer{}
   588  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true), grpc.Peer(peer)); err != nil {
   589  		t.Fatalf("EmptyCall() failed: %v", err)
   590  	}
   591  	verifySecurityInformationFromPeer(t, peer, e2e.SecurityLevelMTLS)
   592  }
   593  
   594  // Tests the case where the cds LB policy receives security configuration as
   595  // part of the Cluster resource that can be successfully resolved using the
   596  // bootstrap file contents. Verifies that the connection between the client and
   597  // the server is secure. Subsequently, the cds LB policy receives a cluster
   598  // resource without security configuration. Verifies that this results in the
   599  // use of fallback credentials, which in this case is insecure creds.
   600  func (s) TestSecurityConfigUpdate_GoodToFallback(t *testing.T) {
   601  	// Spin up an xDS management server.
   602  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
   603  
   604  	// Create bootstrap configuration pointing to the above management server.
   605  	nodeID := uuid.New().String()
   606  	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
   607  
   608  	// Create a grpc channel with xDS creds talking to a test server with TLS
   609  	// credentials.
   610  	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), tlsServerCreds(t))
   611  
   612  	// Configure cluster and endpoints resources in the management server. The
   613  	// cluster resource is configured to return security configuration.
   614  	resources := e2e.UpdateOptions{
   615  		NodeID:         nodeID,
   616  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
   617  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
   618  		SkipValidation: true,
   619  	}
   620  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   621  	defer cancel()
   622  	if err := mgmtServer.Update(ctx, resources); err != nil {
   623  		t.Fatal(err)
   624  	}
   625  
   626  	// Verify that a successful RPC can be made over a secure connection.
   627  	client := testgrpc.NewTestServiceClient(cc)
   628  	peer := &peer.Peer{}
   629  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true), grpc.Peer(peer)); err != nil {
   630  		t.Fatalf("EmptyCall() failed: %v", err)
   631  	}
   632  	verifySecurityInformationFromPeer(t, peer, e2e.SecurityLevelMTLS)
   633  
   634  	// Start a test service backend that does not expect a secure connection.
   635  	insecureServer := stubserver.StartTestService(t, nil)
   636  	t.Cleanup(insecureServer.Stop)
   637  
   638  	// Update the resources in the management server to contain no security
   639  	// configuration. This should result in the use of fallback credentials,
   640  	// which is insecure in our case.
   641  	resources = e2e.UpdateOptions{
   642  		NodeID:         nodeID,
   643  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
   644  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, insecureServer.Address)})},
   645  		SkipValidation: true,
   646  	}
   647  	if err := mgmtServer.Update(ctx, resources); err != nil {
   648  		t.Fatal(err)
   649  	}
   650  
   651  	// Wait for the connection to move to the new backend that expects
   652  	// connections without security.
   653  	for ctx.Err() == nil {
   654  		if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true), grpc.Peer(peer)); err != nil {
   655  			t.Logf("EmptyCall() failed: %v", err)
   656  		}
   657  		if peer.Addr.String() == insecureServer.Address {
   658  			break
   659  		}
   660  	}
   661  	if ctx.Err() != nil {
   662  		t.Fatal("Timed out when waiting for connection to switch to second backend")
   663  	}
   664  	verifySecurityInformationFromPeer(t, peer, e2e.SecurityLevelNone)
   665  }
   666  
   667  // Tests the case where the cds LB policy receives security configuration as
   668  // part of the Cluster resource that can be successfully resolved using the
   669  // bootstrap file contents. Verifies that the connection between the client and
   670  // the server is secure. Subsequently, the cds LB policy receives a cluster
   671  // resource that is NACKed by the xDS client. Test verifies that the cds LB
   672  // policy continues to use the previous good configuration, but the error from
   673  // the xDS client is propagated to the child policy.
   674  func (s) TestSecurityConfigUpdate_GoodToBad(t *testing.T) {
   675  	// Register a wrapped clusterresolver LB policy (child policy of the cds LB
   676  	// policy) for the duration of this test that makes the resolver error
   677  	// pushed to it available to the test.
   678  	_, resolverErrCh, _, _ := registerWrappedClusterResolverPolicy(t)
   679  
   680  	// Spin up an xDS management server.
   681  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
   682  
   683  	// Create bootstrap configuration pointing to the above management server.
   684  	nodeID := uuid.New().String()
   685  	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
   686  
   687  	// Create a grpc channel with xDS creds talking to a test server with TLS
   688  	// credentials.
   689  	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), tlsServerCreds(t))
   690  
   691  	// Configure cluster and endpoints resources in the management server. The
   692  	// cluster resource is configured to return security configuration.
   693  	resources := e2e.UpdateOptions{
   694  		NodeID:         nodeID,
   695  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
   696  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
   697  		SkipValidation: true,
   698  	}
   699  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   700  	defer cancel()
   701  	if err := mgmtServer.Update(ctx, resources); err != nil {
   702  		t.Fatal(err)
   703  	}
   704  
   705  	// Verify that a successful RPC can be made over a secure connection.
   706  	client := testgrpc.NewTestServiceClient(cc)
   707  	peer := &peer.Peer{}
   708  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true), grpc.Peer(peer)); err != nil {
   709  		t.Fatalf("EmptyCall() failed: %v", err)
   710  	}
   711  	verifySecurityInformationFromPeer(t, peer, e2e.SecurityLevelMTLS)
   712  
   713  	// Configure cluster and endpoints resources in the management server. The
   714  	// cluster resource contains security configuration with a certificate
   715  	// provider instance that is missing in the bootstrap configuration.
   716  	cluster := e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)
   717  	cluster.TransportSocket = &v3corepb.TransportSocket{
   718  		Name: "envoy.transport_sockets.tls",
   719  		ConfigType: &v3corepb.TransportSocket_TypedConfig{
   720  			TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   721  				CommonTlsContext: &v3tlspb.CommonTlsContext{
   722  					ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
   723  						ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   724  							InstanceName: "unknown-certificate-provider-instance",
   725  						},
   726  					},
   727  				},
   728  			}),
   729  		},
   730  	}
   731  	resources = e2e.UpdateOptions{
   732  		NodeID:         nodeID,
   733  		Clusters:       []*v3clusterpb.Cluster{cluster},
   734  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
   735  		SkipValidation: true,
   736  	}
   737  	if err := mgmtServer.Update(ctx, resources); err != nil {
   738  		t.Fatal(err)
   739  	}
   740  
   741  	const wantNACKErr = "instance name \"unknown-certificate-provider-instance\" missing in bootstrap configuration"
   742  	select {
   743  	case err := <-resolverErrCh:
   744  		if !strings.Contains(err.Error(), wantNACKErr) {
   745  			t.Fatalf("Child policy got resolver error: %v, want err: %v", err, wantNACKErr)
   746  		}
   747  	case <-ctx.Done():
   748  		t.Fatal("Timeout when waiting for resolver error to be pushed to the child policy")
   749  	}
   750  
   751  	// Verify that a successful RPC can be made over a secure connection.
   752  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   753  		t.Fatalf("EmptyCall() failed: %v", err)
   754  	}
   755  	verifySecurityInformationFromPeer(t, peer, e2e.SecurityLevelMTLS)
   756  }
   757  
   758  // Tests the case where the cds LB policy receives security configuration as
   759  // part of the Cluster resource that specifies the use system root certs.
   760  // Verifies that the connection between the client and the server is secure.
   761  func (s) TestSystemRootCertsSecurityConfig(t *testing.T) {
   762  	origFlag := envconfig.XDSSystemRootCertsEnabled
   763  	origSRCF := x509SystemCertPoolFunc
   764  	defer func() {
   765  		envconfig.XDSSystemRootCertsEnabled = origFlag
   766  		x509SystemCertPoolFunc = origSRCF
   767  	}()
   768  	envconfig.XDSSystemRootCertsEnabled = true
   769  
   770  	systemRootCertsFuncCalled := false
   771  	x509SystemCertPoolFunc = func() (*x509.CertPool, error) {
   772  		certData, err := os.ReadFile(testdata.Path("x509/server_ca_cert.pem"))
   773  		if err != nil {
   774  			return nil, fmt.Errorf("failed to read certificate file: %w", err)
   775  		}
   776  		certPool := x509.NewCertPool()
   777  
   778  		if ok := certPool.AppendCertsFromPEM(certData); !ok {
   779  			return nil, fmt.Errorf("failed to append certificate to cert pool")
   780  		}
   781  		systemRootCertsFuncCalled = true
   782  		return certPool, nil
   783  	}
   784  	// Spin up an xDS management server.
   785  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
   786  
   787  	// Create bootstrap configuration pointing to the above management server
   788  	// and one that includes certificate providers configuration.
   789  	nodeID := uuid.New().String()
   790  	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
   791  
   792  	// Create a grpc channel with xDS creds talking to a test server with TLS
   793  	// credentials.
   794  	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), tlsServerCreds(t))
   795  
   796  	// Configure cluster and endpoints resources in the management server. The
   797  	// cluster resource is configured to return security configuration.
   798  	resources := e2e.UpdateOptions{
   799  		NodeID:         nodeID,
   800  		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelTLSWithSystemRootCerts)},
   801  		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
   802  		SkipValidation: true,
   803  	}
   804  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   805  	defer cancel()
   806  	if err := mgmtServer.Update(ctx, resources); err != nil {
   807  		t.Fatal(err)
   808  	}
   809  
   810  	// Verify that a successful RPC can be made over a secure connection.
   811  	client := testgrpc.NewTestServiceClient(cc)
   812  	peer := &peer.Peer{}
   813  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(peer)); err != nil {
   814  		t.Fatalf("EmptyCall() failed: %v", err)
   815  	}
   816  	verifySecurityInformationFromPeer(t, peer, e2e.SecurityLevelMTLS)
   817  
   818  	if systemRootCertsFuncCalled != true {
   819  		t.Errorf("System root certs were not used during the test.")
   820  	}
   821  }