google.golang.org/grpc@v1.74.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" 34 "google.golang.org/grpc/credentials/insecure" 35 xdscreds "google.golang.org/grpc/credentials/xds" 36 "google.golang.org/grpc/internal" 37 "google.golang.org/grpc/internal/grpcsync" 38 "google.golang.org/grpc/internal/stubserver" 39 "google.golang.org/grpc/internal/testutils" 40 "google.golang.org/grpc/internal/testutils/xds/e2e" 41 "google.golang.org/grpc/internal/testutils/xds/e2e/setup" 42 "google.golang.org/grpc/peer" 43 "google.golang.org/grpc/resolver" 44 "google.golang.org/grpc/status" 45 "google.golang.org/grpc/xds" 46 47 testgrpc "google.golang.org/grpc/interop/grpc_testing" 48 testpb "google.golang.org/grpc/interop/grpc_testing" 49 ) 50 51 func testModeChangeServerOption(t *testing.T) grpc.ServerOption { 52 // Create a server option to get notified about serving mode changes. We don't 53 // do anything other than throwing a log entry here. But this is required, 54 // since the server code emits a log entry at the default level (which is 55 // ERROR) if no callback is registered for serving mode changes. Our 56 // testLogger fails the test if there is any log entry at ERROR level. It does 57 // provide an ExpectError() method, but that takes a string and it would be 58 // painful to construct the exact error message expected here. Instead this 59 // works just fine. 60 return xds.ServingModeCallback(func(addr net.Addr, args xds.ServingModeChangeArgs) { 61 t.Logf("Serving mode for listener %q changed to %q, err: %v", addr.String(), args.Mode, args.Err) 62 }) 63 } 64 65 // acceptNotifyingListener wraps a listener and notifies users when a server 66 // calls the Listener.Accept() method. This can be used to ensure that the 67 // server is ready before requests are sent to it. 68 type acceptNotifyingListener struct { 69 net.Listener 70 serverReady grpcsync.Event 71 } 72 73 func (l *acceptNotifyingListener) Accept() (net.Conn, error) { 74 l.serverReady.Fire() 75 return l.Listener.Accept() 76 } 77 78 // setupGRPCServer performs the following: 79 // - spin up an xDS-enabled gRPC server, configure it with xdsCredentials and 80 // register the test service on it 81 // - create a local TCP listener and start serving on it 82 // 83 // Returns the following: 84 // - local listener on which the xDS-enabled gRPC server is serving on 85 // - cleanup function to be invoked by the tests when done 86 func setupGRPCServer(t *testing.T, bootstrapContents []byte, opts ...grpc.ServerOption) (net.Listener, func()) { 87 t.Helper() 88 89 // Configure xDS credentials to be used on the server-side. 90 creds, err := xdscreds.NewServerCredentials(xdscreds.ServerOptions{ 91 FallbackCreds: insecure.NewCredentials(), 92 }) 93 if err != nil { 94 t.Fatal(err) 95 } 96 97 // Initialize a test gRPC server, assign it to the stub server, and start 98 // the test service. 99 stub := &stubserver.StubServer{ 100 EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) { 101 return &testpb.Empty{}, nil 102 }, 103 UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { 104 return &testpb.SimpleResponse{}, nil 105 }, 106 FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error { 107 for { 108 _, err := stream.Recv() // hangs here forever if stream doesn't shut down...doesn't receive EOF without any errors 109 if err == io.EOF { 110 return nil 111 } 112 } 113 }, 114 } 115 116 opts = append([]grpc.ServerOption{ 117 grpc.Creds(creds), 118 testModeChangeServerOption(t), 119 xds.BootstrapContentsForTesting(bootstrapContents), 120 }, opts...) 121 if stub.S, err = xds.NewGRPCServer(opts...); err != nil { 122 t.Fatalf("Failed to create an xDS enabled gRPC server: %v", err) 123 } 124 125 // Create a local listener and pass it to Serve(). 126 lis, err := testutils.LocalTCPListener() 127 if err != nil { 128 t.Fatalf("testutils.LocalTCPListener() failed: %v", err) 129 } 130 131 readyLis := &acceptNotifyingListener{ 132 Listener: lis, 133 serverReady: *grpcsync.NewEvent(), 134 } 135 136 stub.Listener = readyLis 137 stubserver.StartTestService(t, stub) 138 139 // Wait for the server to start running. 140 select { 141 case <-readyLis.serverReady.Done(): 142 case <-time.After(defaultTestTimeout): 143 t.Fatalf("Timed out while waiting for the backend server to start serving") 144 } 145 146 return lis, func() { 147 stub.S.Stop() 148 } 149 } 150 151 func hostPortFromListener(lis net.Listener) (string, uint32, error) { 152 host, p, err := net.SplitHostPort(lis.Addr().String()) 153 if err != nil { 154 return "", 0, fmt.Errorf("net.SplitHostPort(%s) failed: %v", lis.Addr().String(), err) 155 } 156 port, err := strconv.ParseInt(p, 10, 32) 157 if err != nil { 158 return "", 0, fmt.Errorf("strconv.ParseInt(%s, 10, 32) failed: %v", p, err) 159 } 160 return host, uint32(port), nil 161 } 162 163 // TestServerSideXDS_Fallback is an e2e test which verifies xDS credentials 164 // fallback functionality. 165 // 166 // The following sequence of events happen as part of this test: 167 // - An xDS-enabled gRPC server is created and xDS credentials are configured. 168 // - xDS is enabled on the client by the use of the xds:/// scheme, and xDS 169 // credentials are configured. 170 // - Control plane is configured to not send any security configuration to both 171 // the client and the server. This results in both of them using the 172 // configured fallback credentials (which is insecure creds in this case). 173 func (s) TestServerSideXDS_Fallback(t *testing.T) { 174 managementServer, nodeID, bootstrapContents, xdsResolver := setup.ManagementServerAndResolver(t) 175 176 lis, cleanup2 := setupGRPCServer(t, bootstrapContents) 177 defer cleanup2() 178 179 // Grab the host and port of the server and create client side xDS resources 180 // corresponding to it. This contains default resources with no security 181 // configuration in the Cluster resources. 182 host, port, err := hostPortFromListener(lis) 183 if err != nil { 184 t.Fatalf("failed to retrieve host and port of server: %v", err) 185 } 186 const serviceName = "my-service-fallback" 187 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 188 DialTarget: serviceName, 189 NodeID: nodeID, 190 Host: host, 191 Port: port, 192 SecLevel: e2e.SecurityLevelNone, 193 }) 194 195 // Create an inbound xDS listener resource for the server side that does not 196 // contain any security configuration. This should force the server-side 197 // xdsCredentials to use fallback. 198 inboundLis := e2e.DefaultServerListener(host, port, e2e.SecurityLevelNone, "routeName") 199 resources.Listeners = append(resources.Listeners, inboundLis) 200 201 // Setup the management server with client and server-side resources. 202 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 203 defer cancel() 204 if err := managementServer.Update(ctx, resources); err != nil { 205 t.Fatal(err) 206 } 207 208 // Create client-side xDS credentials with an insecure fallback. 209 creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{ 210 FallbackCreds: insecure.NewCredentials(), 211 }) 212 if err != nil { 213 t.Fatal(err) 214 } 215 216 // Create a ClientConn with the xds scheme and make a successful RPC. 217 cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(creds), grpc.WithResolvers(xdsResolver)) 218 if err != nil { 219 t.Fatalf("failed to create a client for server: %v", err) 220 } 221 defer cc.Close() 222 223 client := testgrpc.NewTestServiceClient(cc) 224 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { 225 t.Errorf("rpc EmptyCall() failed: %v", err) 226 } 227 } 228 229 // TestServerSideXDS_FileWatcherCerts is an e2e test which verifies xDS 230 // credentials with file watcher certificate provider. 231 // 232 // The following sequence of events happen as part of this test: 233 // - An xDS-enabled gRPC server is created and xDS credentials are configured. 234 // - xDS is enabled on the client by the use of the xds:/// scheme, and xDS 235 // credentials are configured. 236 // - Control plane is configured to send security configuration to both the 237 // client and the server, pointing to the file watcher certificate provider. 238 // We verify both TLS and mTLS scenarios. 239 func (s) TestServerSideXDS_FileWatcherCerts(t *testing.T) { 240 tests := []struct { 241 name string 242 secLevel e2e.SecurityLevel 243 }{ 244 { 245 name: "tls", 246 secLevel: e2e.SecurityLevelTLS, 247 }, 248 { 249 name: "mtls", 250 secLevel: e2e.SecurityLevelMTLS, 251 }, 252 } 253 for _, test := range tests { 254 t.Run(test.name, func(t *testing.T) { 255 managementServer, nodeID, bootstrapContents, xdsResolver := setup.ManagementServerAndResolver(t) 256 lis, cleanup2 := setupGRPCServer(t, bootstrapContents) 257 defer cleanup2() 258 259 // Grab the host and port of the server and create client side xDS 260 // resources corresponding to it. 261 host, port, err := hostPortFromListener(lis) 262 if err != nil { 263 t.Fatalf("failed to retrieve host and port of server: %v", err) 264 } 265 266 // Create xDS resources to be consumed on the client side. This 267 // includes the listener, route configuration, cluster (with 268 // security configuration) and endpoint resources. 269 serviceName := "my-service-file-watcher-certs-" + test.name 270 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 271 DialTarget: serviceName, 272 NodeID: nodeID, 273 Host: host, 274 Port: port, 275 SecLevel: test.secLevel, 276 }) 277 278 // Create an inbound xDS listener resource for the server side that 279 // contains security configuration pointing to the file watcher 280 // plugin. 281 inboundLis := e2e.DefaultServerListener(host, port, test.secLevel, "routeName") 282 resources.Listeners = append(resources.Listeners, inboundLis) 283 284 // Setup the management server with client and server resources. 285 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 286 defer cancel() 287 if err := managementServer.Update(ctx, resources); err != nil { 288 t.Fatal(err) 289 } 290 291 // Create client-side xDS credentials with an insecure fallback. 292 creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{ 293 FallbackCreds: insecure.NewCredentials(), 294 }) 295 if err != nil { 296 t.Fatal(err) 297 } 298 299 // Create a ClientConn with the xds scheme and make an RPC. 300 cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(creds), grpc.WithResolvers(xdsResolver)) 301 if err != nil { 302 t.Fatalf("failed to create a client for server: %v", err) 303 } 304 defer cc.Close() 305 306 client := testgrpc.NewTestServiceClient(cc) 307 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { 308 t.Fatalf("rpc EmptyCall() failed: %v", err) 309 } 310 }) 311 } 312 } 313 314 // TestServerSideXDS_SecurityConfigChange is an e2e test where xDS is enabled on 315 // the server-side and xdsCredentials are configured for security. The control 316 // plane initially does not any security configuration. This forces the 317 // xdsCredentials to use fallback creds, which is this case is insecure creds. 318 // We verify that a client connecting with TLS creds is not able to successfully 319 // make an RPC. The control plane then sends a listener resource with security 320 // configuration pointing to the use of the file_watcher plugin and we verify 321 // that the same client is now able to successfully make an RPC. 322 func (s) TestServerSideXDS_SecurityConfigChange(t *testing.T) { 323 managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{AllowResourceSubset: true}) 324 325 // Create bootstrap configuration pointing to the above management server. 326 nodeID := uuid.New().String() 327 bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address) 328 329 // Create an xDS resolver with the above bootstrap configuration. 330 if internal.NewXDSResolverWithConfigForTesting == nil { 331 t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil") 332 } 333 xdsResolver, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bootstrapContents) 334 if err != nil { 335 t.Fatalf("Failed to create xDS resolver for testing: %v", err) 336 } 337 338 lis, cleanup2 := setupGRPCServer(t, bootstrapContents) 339 defer cleanup2() 340 341 // Grab the host and port of the server and create client side xDS resources 342 // corresponding to it. This contains default resources with no security 343 // configuration in the Cluster resource. This should force the xDS 344 // credentials on the client to use its fallback. 345 host, port, err := hostPortFromListener(lis) 346 if err != nil { 347 t.Fatalf("failed to retrieve host and port of server: %v", err) 348 } 349 const serviceName = "my-service-security-config-change" 350 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 351 DialTarget: serviceName, 352 NodeID: nodeID, 353 Host: host, 354 Port: port, 355 SecLevel: e2e.SecurityLevelNone, 356 }) 357 358 // Create an inbound xDS listener resource for the server side that does not 359 // contain any security configuration. This should force the xDS credentials 360 // on server to use its fallback. 361 inboundLis := e2e.DefaultServerListener(host, port, e2e.SecurityLevelNone, "routeName") 362 resources.Listeners = append(resources.Listeners, inboundLis) 363 364 // Setup the management server with client and server-side resources. 365 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 366 defer cancel() 367 if err := managementServer.Update(ctx, resources); err != nil { 368 t.Fatal(err) 369 } 370 371 // Create client-side xDS credentials with an insecure fallback. 372 xdsCreds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{ 373 FallbackCreds: insecure.NewCredentials(), 374 }) 375 if err != nil { 376 t.Fatal(err) 377 } 378 379 // Create a ClientConn with the xds scheme and make a successful RPC. 380 xdsCC, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(xdsCreds), grpc.WithResolvers(xdsResolver)) 381 if err != nil { 382 t.Fatalf("failed to create a client for server: %v", err) 383 } 384 defer xdsCC.Close() 385 386 client := testgrpc.NewTestServiceClient(xdsCC) 387 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { 388 t.Fatalf("rpc EmptyCall() failed: %v", err) 389 } 390 391 // Create a ClientConn with TLS creds. This should fail since the server is 392 // using fallback credentials which in this case in insecure creds. 393 tlsCreds := testutils.CreateClientTLSCredentials(t) 394 tlsCC, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(tlsCreds)) 395 if err != nil { 396 t.Fatalf("failed to create a client for server: %v", err) 397 } 398 defer tlsCC.Close() 399 400 // We don't set 'waitForReady` here since we want this call to failfast. 401 client = testgrpc.NewTestServiceClient(tlsCC) 402 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); status.Code(err) != codes.Unavailable { 403 t.Fatal("rpc EmptyCall() succeeded when expected to fail") 404 } 405 406 // Switch server and client side resources with ones that contain required 407 // security configuration for mTLS with a file watcher certificate provider. 408 resources = e2e.DefaultClientResources(e2e.ResourceParams{ 409 DialTarget: serviceName, 410 NodeID: nodeID, 411 Host: host, 412 Port: port, 413 SecLevel: e2e.SecurityLevelMTLS, 414 }) 415 inboundLis = e2e.DefaultServerListener(host, port, e2e.SecurityLevelMTLS, "routeName") 416 resources.Listeners = append(resources.Listeners, inboundLis) 417 if err := managementServer.Update(ctx, resources); err != nil { 418 t.Fatal(err) 419 } 420 421 // Make another RPC with `waitForReady` set and expect this to succeed. 422 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { 423 t.Fatalf("rpc EmptyCall() failed: %v", err) 424 } 425 } 426 427 // TestServerSideXDS_FileWatcherCertsSPIFFE is an e2e test which verifies xDS 428 // credentials with file watcher certificate provider that is configured with a 429 // SPIFFE Bundle Map for it's roots. 430 // 431 // The following sequence of events happen as part of this test: 432 // - An xDS-enabled gRPC server is created and xDS credentials are configured. 433 // - xDS is enabled on the client by the use of the xds:/// scheme, and xDS 434 // credentials are configured. 435 // - Control plane is configured to send security configuration to both the 436 // client and the server, pointing to the file watcher certificate provider. 437 // We verify both TLS and mTLS scenarios. 438 func (s) TestServerSideXDS_FileWatcherCertsSPIFFE(t *testing.T) { 439 tests := []struct { 440 name string 441 secLevel e2e.SecurityLevel 442 }{ 443 { 444 name: "tls", 445 secLevel: e2e.SecurityLevelTLS, 446 }, 447 { 448 name: "mtls", 449 secLevel: e2e.SecurityLevelMTLS, 450 }, 451 } 452 for _, test := range tests { 453 t.Run(test.name, func(t *testing.T) { 454 managementServer, nodeID, bootstrapContents, xdsResolver := setup.ManagementServerAndResolverWithSPIFFE(t) 455 lis, cleanup2 := setupGRPCServer(t, bootstrapContents) 456 defer cleanup2() 457 458 // Grab the host and port of the server and create client side xDS 459 // resources corresponding to it. 460 host, port, err := hostPortFromListener(lis) 461 if err != nil { 462 t.Fatalf("failed to retrieve host and port of server: %v", err) 463 } 464 465 // Create xDS resources to be consumed on the client side. This 466 // includes the listener, route configuration, cluster (with 467 // security configuration) and endpoint resources. 468 serviceName := "my-service-file-watcher-certs-" + test.name 469 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 470 DialTarget: serviceName, 471 NodeID: nodeID, 472 Host: host, 473 Port: port, 474 SecLevel: test.secLevel, 475 }) 476 477 // Create an inbound xDS listener resource for the server side that 478 // contains security configuration pointing to the file watcher 479 // plugin. 480 inboundLis := e2e.DefaultServerListener(host, port, test.secLevel, "routeName") 481 resources.Listeners = append(resources.Listeners, inboundLis) 482 483 // Setup the management server with client and server resources. 484 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 485 defer cancel() 486 if err := managementServer.Update(ctx, resources); err != nil { 487 t.Fatal(err) 488 } 489 490 // Create client-side xDS credentials with an insecure fallback. 491 creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{ 492 FallbackCreds: insecure.NewCredentials(), 493 }) 494 if err != nil { 495 t.Fatal(err) 496 } 497 498 // Create a ClientConn with the xds scheme and make an RPC. 499 cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(creds), grpc.WithResolvers(xdsResolver)) 500 if err != nil { 501 t.Fatalf("failed to create a client for server: %v", err) 502 } 503 defer cc.Close() 504 505 peer := &peer.Peer{} 506 client := testgrpc.NewTestServiceClient(cc) 507 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true), grpc.Peer(peer)); err != nil { 508 t.Fatalf("rpc EmptyCall() failed: %v", err) 509 } 510 verifySecurityInformationFromPeerSPIFFE(t, peer, test.secLevel, 1) 511 }) 512 } 513 } 514 515 // Checks the AuthInfo available in the peer if it matches the expected security 516 // level of the connection. 517 func verifySecurityInformationFromPeerSPIFFE(t *testing.T, pr *peer.Peer, wantSecLevel e2e.SecurityLevel, wantPeerChainLen int) { 518 // This is not a true helper in the Go sense, because it does not perform 519 // setup or cleanup tasks. Marking it a helper is to ensure that when the 520 // test fails, the line information of the caller is outputted instead of 521 // from here. 522 // 523 // And this function directly calls t.Fatalf() instead of returning an error 524 // and letting the caller decide what to do with it. This is also OK since 525 // all callers will simply end up calling t.Fatalf() with the returned 526 // error, and can't add any contextual information of value to the error 527 // message. 528 t.Helper() 529 530 authType := pr.AuthInfo.AuthType() 531 switch wantSecLevel { 532 case e2e.SecurityLevelNone: 533 if authType != "insecure" { 534 t.Fatalf("AuthType() is %s, want insecure", authType) 535 } 536 case e2e.SecurityLevelMTLS: 537 if authType != "tls" { 538 t.Fatalf("AuthType() is %s, want tls", authType) 539 } 540 ai, ok := pr.AuthInfo.(credentials.TLSInfo) 541 if !ok { 542 t.Fatalf("AuthInfo type is %T, want %T", pr.AuthInfo, credentials.TLSInfo{}) 543 } 544 if len(ai.State.PeerCertificates) != wantPeerChainLen { 545 t.Fatalf("Number of peer certificates is %d, want %d", len(ai.State.PeerCertificates), wantPeerChainLen) 546 } 547 } 548 }