google.golang.org/grpc@v1.72.2/test/xds/xds_server_integration_test.go (about)

     1  /*
     2   *
     3   * Copyright 2020 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package xds_test
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"io"
    25  	"net"
    26  	"strconv"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/google/uuid"
    31  	"google.golang.org/grpc"
    32  	"google.golang.org/grpc/codes"
    33  	"google.golang.org/grpc/credentials/insecure"
    34  	xdscreds "google.golang.org/grpc/credentials/xds"
    35  	"google.golang.org/grpc/internal"
    36  	"google.golang.org/grpc/internal/grpcsync"
    37  	"google.golang.org/grpc/internal/stubserver"
    38  	"google.golang.org/grpc/internal/testutils"
    39  	"google.golang.org/grpc/internal/testutils/xds/e2e"
    40  	"google.golang.org/grpc/internal/testutils/xds/e2e/setup"
    41  	"google.golang.org/grpc/resolver"
    42  	"google.golang.org/grpc/status"
    43  	"google.golang.org/grpc/xds"
    44  
    45  	testgrpc "google.golang.org/grpc/interop/grpc_testing"
    46  	testpb "google.golang.org/grpc/interop/grpc_testing"
    47  )
    48  
    49  func testModeChangeServerOption(t *testing.T) grpc.ServerOption {
    50  	// Create a server option to get notified about serving mode changes. We don't
    51  	// do anything other than throwing a log entry here. But this is required,
    52  	// since the server code emits a log entry at the default level (which is
    53  	// ERROR) if no callback is registered for serving mode changes. Our
    54  	// testLogger fails the test if there is any log entry at ERROR level. It does
    55  	// provide an ExpectError()  method, but that takes a string and it would be
    56  	// painful to construct the exact error message expected here. Instead this
    57  	// works just fine.
    58  	return xds.ServingModeCallback(func(addr net.Addr, args xds.ServingModeChangeArgs) {
    59  		t.Logf("Serving mode for listener %q changed to %q, err: %v", addr.String(), args.Mode, args.Err)
    60  	})
    61  }
    62  
    63  // acceptNotifyingListener wraps a listener and notifies users when a server
    64  // calls the Listener.Accept() method. This can be used to ensure that the
    65  // server is ready before requests are sent to it.
    66  type acceptNotifyingListener struct {
    67  	net.Listener
    68  	serverReady grpcsync.Event
    69  }
    70  
    71  func (l *acceptNotifyingListener) Accept() (net.Conn, error) {
    72  	l.serverReady.Fire()
    73  	return l.Listener.Accept()
    74  }
    75  
    76  // setupGRPCServer performs the following:
    77  //   - spin up an xDS-enabled gRPC server, configure it with xdsCredentials and
    78  //     register the test service on it
    79  //   - create a local TCP listener and start serving on it
    80  //
    81  // Returns the following:
    82  // - local listener on which the xDS-enabled gRPC server is serving on
    83  // - cleanup function to be invoked by the tests when done
    84  func setupGRPCServer(t *testing.T, bootstrapContents []byte) (net.Listener, func()) {
    85  	t.Helper()
    86  
    87  	// Configure xDS credentials to be used on the server-side.
    88  	creds, err := xdscreds.NewServerCredentials(xdscreds.ServerOptions{
    89  		FallbackCreds: insecure.NewCredentials(),
    90  	})
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	// Initialize a test gRPC server, assign it to the stub server, and start
    96  	// the test service.
    97  	stub := &stubserver.StubServer{
    98  		EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
    99  			return &testpb.Empty{}, nil
   100  		},
   101  		UnaryCallF: func(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
   102  			return &testpb.SimpleResponse{}, nil
   103  		},
   104  		FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error {
   105  			for {
   106  				_, err := stream.Recv() // hangs here forever if stream doesn't shut down...doesn't receive EOF without any errors
   107  				if err == io.EOF {
   108  					return nil
   109  				}
   110  			}
   111  		},
   112  	}
   113  
   114  	if stub.S, err = xds.NewGRPCServer(grpc.Creds(creds), testModeChangeServerOption(t), xds.BootstrapContentsForTesting(bootstrapContents)); err != nil {
   115  		t.Fatalf("Failed to create an xDS enabled gRPC server: %v", err)
   116  	}
   117  
   118  	stubserver.StartTestService(t, stub)
   119  
   120  	// Create a local listener and pass it to Serve().
   121  	lis, err := testutils.LocalTCPListener()
   122  	if err != nil {
   123  		t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
   124  	}
   125  
   126  	readyLis := &acceptNotifyingListener{
   127  		Listener:    lis,
   128  		serverReady: *grpcsync.NewEvent(),
   129  	}
   130  
   131  	go func() {
   132  		if err := stub.S.Serve(readyLis); err != nil {
   133  			t.Errorf("Serve() failed: %v", err)
   134  		}
   135  	}()
   136  
   137  	// Wait for the server to start running.
   138  	select {
   139  	case <-readyLis.serverReady.Done():
   140  	case <-time.After(defaultTestTimeout):
   141  		t.Fatalf("Timed out while waiting for the backend server to start serving")
   142  	}
   143  
   144  	return lis, func() {
   145  		stub.S.Stop()
   146  	}
   147  }
   148  
   149  func hostPortFromListener(lis net.Listener) (string, uint32, error) {
   150  	host, p, err := net.SplitHostPort(lis.Addr().String())
   151  	if err != nil {
   152  		return "", 0, fmt.Errorf("net.SplitHostPort(%s) failed: %v", lis.Addr().String(), err)
   153  	}
   154  	port, err := strconv.ParseInt(p, 10, 32)
   155  	if err != nil {
   156  		return "", 0, fmt.Errorf("strconv.ParseInt(%s, 10, 32) failed: %v", p, err)
   157  	}
   158  	return host, uint32(port), nil
   159  }
   160  
   161  // TestServerSideXDS_Fallback is an e2e test which verifies xDS credentials
   162  // fallback functionality.
   163  //
   164  // The following sequence of events happen as part of this test:
   165  //   - An xDS-enabled gRPC server is created and xDS credentials are configured.
   166  //   - xDS is enabled on the client by the use of the xds:/// scheme, and xDS
   167  //     credentials are configured.
   168  //   - Control plane is configured to not send any security configuration to both
   169  //     the client and the server. This results in both of them using the
   170  //     configured fallback credentials (which is insecure creds in this case).
   171  func (s) TestServerSideXDS_Fallback(t *testing.T) {
   172  	managementServer, nodeID, bootstrapContents, xdsResolver := setup.ManagementServerAndResolver(t)
   173  
   174  	lis, cleanup2 := setupGRPCServer(t, bootstrapContents)
   175  	defer cleanup2()
   176  
   177  	// Grab the host and port of the server and create client side xDS resources
   178  	// corresponding to it. This contains default resources with no security
   179  	// configuration in the Cluster resources.
   180  	host, port, err := hostPortFromListener(lis)
   181  	if err != nil {
   182  		t.Fatalf("failed to retrieve host and port of server: %v", err)
   183  	}
   184  	const serviceName = "my-service-fallback"
   185  	resources := e2e.DefaultClientResources(e2e.ResourceParams{
   186  		DialTarget: serviceName,
   187  		NodeID:     nodeID,
   188  		Host:       host,
   189  		Port:       port,
   190  		SecLevel:   e2e.SecurityLevelNone,
   191  	})
   192  
   193  	// Create an inbound xDS listener resource for the server side that does not
   194  	// contain any security configuration. This should force the server-side
   195  	// xdsCredentials to use fallback.
   196  	inboundLis := e2e.DefaultServerListener(host, port, e2e.SecurityLevelNone, "routeName")
   197  	resources.Listeners = append(resources.Listeners, inboundLis)
   198  
   199  	// Setup the management server with client and server-side resources.
   200  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   201  	defer cancel()
   202  	if err := managementServer.Update(ctx, resources); err != nil {
   203  		t.Fatal(err)
   204  	}
   205  
   206  	// Create client-side xDS credentials with an insecure fallback.
   207  	creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{
   208  		FallbackCreds: insecure.NewCredentials(),
   209  	})
   210  	if err != nil {
   211  		t.Fatal(err)
   212  	}
   213  
   214  	// Create a ClientConn with the xds scheme and make a successful RPC.
   215  	cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(creds), grpc.WithResolvers(xdsResolver))
   216  	if err != nil {
   217  		t.Fatalf("failed to create a client for server: %v", err)
   218  	}
   219  	defer cc.Close()
   220  
   221  	client := testgrpc.NewTestServiceClient(cc)
   222  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   223  		t.Errorf("rpc EmptyCall() failed: %v", err)
   224  	}
   225  }
   226  
   227  // TestServerSideXDS_FileWatcherCerts is an e2e test which verifies xDS
   228  // credentials with file watcher certificate provider.
   229  //
   230  // The following sequence of events happen as part of this test:
   231  //   - An xDS-enabled gRPC server is created and xDS credentials are configured.
   232  //   - xDS is enabled on the client by the use of the xds:/// scheme, and xDS
   233  //     credentials are configured.
   234  //   - Control plane is configured to send security configuration to both the
   235  //     client and the server, pointing to the file watcher certificate provider.
   236  //     We verify both TLS and mTLS scenarios.
   237  func (s) TestServerSideXDS_FileWatcherCerts(t *testing.T) {
   238  	tests := []struct {
   239  		name     string
   240  		secLevel e2e.SecurityLevel
   241  	}{
   242  		{
   243  			name:     "tls",
   244  			secLevel: e2e.SecurityLevelTLS,
   245  		},
   246  		{
   247  			name:     "mtls",
   248  			secLevel: e2e.SecurityLevelMTLS,
   249  		},
   250  	}
   251  	for _, test := range tests {
   252  		t.Run(test.name, func(t *testing.T) {
   253  			managementServer, nodeID, bootstrapContents, xdsResolver := setup.ManagementServerAndResolver(t)
   254  			lis, cleanup2 := setupGRPCServer(t, bootstrapContents)
   255  			defer cleanup2()
   256  
   257  			// Grab the host and port of the server and create client side xDS
   258  			// resources corresponding to it.
   259  			host, port, err := hostPortFromListener(lis)
   260  			if err != nil {
   261  				t.Fatalf("failed to retrieve host and port of server: %v", err)
   262  			}
   263  
   264  			// Create xDS resources to be consumed on the client side. This
   265  			// includes the listener, route configuration, cluster (with
   266  			// security configuration) and endpoint resources.
   267  			serviceName := "my-service-file-watcher-certs-" + test.name
   268  			resources := e2e.DefaultClientResources(e2e.ResourceParams{
   269  				DialTarget: serviceName,
   270  				NodeID:     nodeID,
   271  				Host:       host,
   272  				Port:       port,
   273  				SecLevel:   test.secLevel,
   274  			})
   275  
   276  			// Create an inbound xDS listener resource for the server side that
   277  			// contains security configuration pointing to the file watcher
   278  			// plugin.
   279  			inboundLis := e2e.DefaultServerListener(host, port, test.secLevel, "routeName")
   280  			resources.Listeners = append(resources.Listeners, inboundLis)
   281  
   282  			// Setup the management server with client and server resources.
   283  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   284  			defer cancel()
   285  			if err := managementServer.Update(ctx, resources); err != nil {
   286  				t.Fatal(err)
   287  			}
   288  
   289  			// Create client-side xDS credentials with an insecure fallback.
   290  			creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{
   291  				FallbackCreds: insecure.NewCredentials(),
   292  			})
   293  			if err != nil {
   294  				t.Fatal(err)
   295  			}
   296  
   297  			// Create a ClientConn with the xds scheme and make an RPC.
   298  			cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(creds), grpc.WithResolvers(xdsResolver))
   299  			if err != nil {
   300  				t.Fatalf("failed to create a client for server: %v", err)
   301  			}
   302  			defer cc.Close()
   303  
   304  			client := testgrpc.NewTestServiceClient(cc)
   305  			if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   306  				t.Fatalf("rpc EmptyCall() failed: %v", err)
   307  			}
   308  		})
   309  	}
   310  }
   311  
   312  // TestServerSideXDS_SecurityConfigChange is an e2e test where xDS is enabled on
   313  // the server-side and xdsCredentials are configured for security. The control
   314  // plane initially does not any security configuration. This forces the
   315  // xdsCredentials to use fallback creds, which is this case is insecure creds.
   316  // We verify that a client connecting with TLS creds is not able to successfully
   317  // make an RPC. The control plane then sends a listener resource with security
   318  // configuration pointing to the use of the file_watcher plugin and we verify
   319  // that the same client is now able to successfully make an RPC.
   320  func (s) TestServerSideXDS_SecurityConfigChange(t *testing.T) {
   321  	managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{AllowResourceSubset: true})
   322  
   323  	// Create bootstrap configuration pointing to the above management server.
   324  	nodeID := uuid.New().String()
   325  	bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address)
   326  
   327  	// Create an xDS resolver with the above bootstrap configuration.
   328  	if internal.NewXDSResolverWithConfigForTesting == nil {
   329  		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
   330  	}
   331  	xdsResolver, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bootstrapContents)
   332  	if err != nil {
   333  		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
   334  	}
   335  
   336  	lis, cleanup2 := setupGRPCServer(t, bootstrapContents)
   337  	defer cleanup2()
   338  
   339  	// Grab the host and port of the server and create client side xDS resources
   340  	// corresponding to it. This contains default resources with no security
   341  	// configuration in the Cluster resource. This should force the xDS
   342  	// credentials on the client to use its fallback.
   343  	host, port, err := hostPortFromListener(lis)
   344  	if err != nil {
   345  		t.Fatalf("failed to retrieve host and port of server: %v", err)
   346  	}
   347  	const serviceName = "my-service-security-config-change"
   348  	resources := e2e.DefaultClientResources(e2e.ResourceParams{
   349  		DialTarget: serviceName,
   350  		NodeID:     nodeID,
   351  		Host:       host,
   352  		Port:       port,
   353  		SecLevel:   e2e.SecurityLevelNone,
   354  	})
   355  
   356  	// Create an inbound xDS listener resource for the server side that does not
   357  	// contain any security configuration. This should force the xDS credentials
   358  	// on server to use its fallback.
   359  	inboundLis := e2e.DefaultServerListener(host, port, e2e.SecurityLevelNone, "routeName")
   360  	resources.Listeners = append(resources.Listeners, inboundLis)
   361  
   362  	// Setup the management server with client and server-side resources.
   363  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   364  	defer cancel()
   365  	if err := managementServer.Update(ctx, resources); err != nil {
   366  		t.Fatal(err)
   367  	}
   368  
   369  	// Create client-side xDS credentials with an insecure fallback.
   370  	xdsCreds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{
   371  		FallbackCreds: insecure.NewCredentials(),
   372  	})
   373  	if err != nil {
   374  		t.Fatal(err)
   375  	}
   376  
   377  	// Create a ClientConn with the xds scheme and make a successful RPC.
   378  	xdsCC, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(xdsCreds), grpc.WithResolvers(xdsResolver))
   379  	if err != nil {
   380  		t.Fatalf("failed to create a client for server: %v", err)
   381  	}
   382  	defer xdsCC.Close()
   383  
   384  	client := testgrpc.NewTestServiceClient(xdsCC)
   385  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   386  		t.Fatalf("rpc EmptyCall() failed: %v", err)
   387  	}
   388  
   389  	// Create a ClientConn with TLS creds. This should fail since the server is
   390  	// using fallback credentials which in this case in insecure creds.
   391  	tlsCreds := testutils.CreateClientTLSCredentials(t)
   392  	tlsCC, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(tlsCreds))
   393  	if err != nil {
   394  		t.Fatalf("failed to create a client for server: %v", err)
   395  	}
   396  	defer tlsCC.Close()
   397  
   398  	// We don't set 'waitForReady` here since we want this call to failfast.
   399  	client = testgrpc.NewTestServiceClient(tlsCC)
   400  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}); status.Code(err) != codes.Unavailable {
   401  		t.Fatal("rpc EmptyCall() succeeded when expected to fail")
   402  	}
   403  
   404  	// Switch server and client side resources with ones that contain required
   405  	// security configuration for mTLS with a file watcher certificate provider.
   406  	resources = e2e.DefaultClientResources(e2e.ResourceParams{
   407  		DialTarget: serviceName,
   408  		NodeID:     nodeID,
   409  		Host:       host,
   410  		Port:       port,
   411  		SecLevel:   e2e.SecurityLevelMTLS,
   412  	})
   413  	inboundLis = e2e.DefaultServerListener(host, port, e2e.SecurityLevelMTLS, "routeName")
   414  	resources.Listeners = append(resources.Listeners, inboundLis)
   415  	if err := managementServer.Update(ctx, resources); err != nil {
   416  		t.Fatal(err)
   417  	}
   418  
   419  	// Make another RPC with `waitForReady` set and expect this to succeed.
   420  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
   421  		t.Fatalf("rpc EmptyCall() failed: %v", err)
   422  	}
   423  }