github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/test/xds_server_integration_test.go (about) 1 //go:build !386 2 // +build !386 3 4 /* 5 * 6 * Copyright 2020 gRPC authors. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 */ 21 22 // Package xds_test contains e2e tests for xDS use. 23 package xds_test 24 25 import ( 26 "context" 27 "fmt" 28 "net" 29 "strconv" 30 "strings" 31 "testing" 32 33 grpc "github.com/hxx258456/ccgo/grpc" 34 "github.com/hxx258456/ccgo/grpc/codes" 35 "github.com/hxx258456/ccgo/grpc/credentials/insecure" 36 "github.com/hxx258456/ccgo/grpc/internal/envconfig" 37 "github.com/hxx258456/ccgo/grpc/internal/testutils" 38 "github.com/hxx258456/ccgo/grpc/status" 39 "github.com/hxx258456/ccgo/grpc/xds" 40 "github.com/hxx258456/ccgo/grpc/xds/internal/httpfilter/rbac" 41 "github.com/hxx258456/ccgo/grpc/xds/internal/testutils/e2e" 42 43 anypb "github.com/golang/protobuf/ptypes/any" 44 wrapperspb "github.com/golang/protobuf/ptypes/wrappers" 45 v3corepb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/core/v3" 46 v3listenerpb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/listener/v3" 47 v3rbacpb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/rbac/v3" 48 v3routepb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/route/v3" 49 rpb "github.com/hxx258456/ccgo/go-control-plane/envoy/extensions/filters/http/rbac/v3" 50 v3routerpb "github.com/hxx258456/ccgo/go-control-plane/envoy/extensions/filters/http/router/v3" 51 v3httppb "github.com/hxx258456/ccgo/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" 52 v3matcherpb "github.com/hxx258456/ccgo/go-control-plane/envoy/type/matcher/v3" 53 xdscreds "github.com/hxx258456/ccgo/grpc/credentials/xds" 54 testpb "github.com/hxx258456/ccgo/grpc/test/grpc_testing" 55 ) 56 57 const ( 58 // Names of files inside tempdir, for certprovider plugin to watch. 59 certFile = "cert.pem" 60 keyFile = "key.pem" 61 rootFile = "ca.pem" 62 ) 63 64 // setupGRPCServer performs the following: 65 // - spin up an xDS-enabled gRPC server, configure it with xdsCredentials and 66 // register the test service on it 67 // - create a local TCP listener and start serving on it 68 // 69 // Returns the following: 70 // - local listener on which the xDS-enabled gRPC server is serving on 71 // - cleanup function to be invoked by the tests when done 72 func setupGRPCServer(t *testing.T, bootstrapContents []byte) (net.Listener, func()) { 73 t.Helper() 74 75 // Configure xDS credentials to be used on the server-side. 76 creds, err := xdscreds.NewServerCredentials(xdscreds.ServerOptions{ 77 FallbackCreds: insecure.NewCredentials(), 78 }) 79 if err != nil { 80 t.Fatal(err) 81 } 82 83 // Initialize an xDS-enabled gRPC server and register the stubServer on it. 84 server := xds.NewGRPCServer(grpc.Creds(creds), xds.BootstrapContentsForTesting(bootstrapContents)) 85 testpb.RegisterTestServiceServer(server, &testService{}) 86 87 // Create a local listener and pass it to Serve(). 88 lis, err := testutils.LocalTCPListener() 89 if err != nil { 90 t.Fatalf("testutils.LocalTCPListener() failed: %v", err) 91 } 92 93 go func() { 94 if err := server.Serve(lis); err != nil { 95 t.Errorf("Serve() failed: %v", err) 96 } 97 }() 98 99 return lis, func() { 100 server.Stop() 101 } 102 } 103 104 func hostPortFromListener(lis net.Listener) (string, uint32, error) { 105 host, p, err := net.SplitHostPort(lis.Addr().String()) 106 if err != nil { 107 return "", 0, fmt.Errorf("net.SplitHostPort(%s) failed: %v", lis.Addr().String(), err) 108 } 109 port, err := strconv.ParseInt(p, 10, 32) 110 if err != nil { 111 return "", 0, fmt.Errorf("strconv.ParseInt(%s, 10, 32) failed: %v", p, err) 112 } 113 return host, uint32(port), nil 114 } 115 116 // TestServerSideXDS_Fallback is an e2e test which verifies xDS credentials 117 // fallback functionality. 118 // 119 // The following sequence of events happen as part of this test: 120 // - An xDS-enabled gRPC server is created and xDS credentials are configured. 121 // - xDS is enabled on the client by the use of the xds:/// scheme, and xDS 122 // credentials are configured. 123 // - Control plane is configured to not send any security configuration to both 124 // the client and the server. This results in both of them using the 125 // configured fallback credentials (which is insecure creds in this case). 126 func (s) TestServerSideXDS_Fallback(t *testing.T) { 127 managementServer, nodeID, bootstrapContents, resolver, cleanup1 := setupManagementServer(t) 128 defer cleanup1() 129 130 lis, cleanup2 := setupGRPCServer(t, bootstrapContents) 131 defer cleanup2() 132 133 // Grab the host and port of the server and create client side xDS resources 134 // corresponding to it. This contains default resources with no security 135 // configuration in the Cluster resources. 136 host, port, err := hostPortFromListener(lis) 137 if err != nil { 138 t.Fatalf("failed to retrieve host and port of server: %v", err) 139 } 140 const serviceName = "my-service-fallback" 141 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 142 DialTarget: serviceName, 143 NodeID: nodeID, 144 Host: host, 145 Port: port, 146 SecLevel: e2e.SecurityLevelNone, 147 }) 148 149 // Create an inbound xDS listener resource for the server side that does not 150 // contain any security configuration. This should force the server-side 151 // xdsCredentials to use fallback. 152 inboundLis := e2e.DefaultServerListener(host, port, e2e.SecurityLevelNone) 153 resources.Listeners = append(resources.Listeners, inboundLis) 154 155 // Setup the management server with client and server-side resources. 156 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 157 defer cancel() 158 if err := managementServer.Update(ctx, resources); err != nil { 159 t.Fatal(err) 160 } 161 162 // Create client-side xDS credentials with an insecure fallback. 163 creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{ 164 FallbackCreds: insecure.NewCredentials(), 165 }) 166 if err != nil { 167 t.Fatal(err) 168 } 169 170 // Create a ClientConn with the xds scheme and make a successful RPC. 171 cc, err := grpc.DialContext(ctx, fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(creds), grpc.WithResolvers(resolver)) 172 if err != nil { 173 t.Fatalf("failed to dial local test server: %v", err) 174 } 175 defer cc.Close() 176 177 client := testpb.NewTestServiceClient(cc) 178 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { 179 t.Errorf("rpc EmptyCall() failed: %v", err) 180 } 181 } 182 183 // TestServerSideXDS_FileWatcherCerts is an e2e test which verifies xDS 184 // credentials with file watcher certificate provider. 185 // 186 // The following sequence of events happen as part of this test: 187 // - An xDS-enabled gRPC server is created and xDS credentials are configured. 188 // - xDS is enabled on the client by the use of the xds:/// scheme, and xDS 189 // credentials are configured. 190 // - Control plane is configured to send security configuration to both the 191 // client and the server, pointing to the file watcher certificate provider. 192 // We verify both TLS and mTLS scenarios. 193 func (s) TestServerSideXDS_FileWatcherCerts(t *testing.T) { 194 tests := []struct { 195 name string 196 secLevel e2e.SecurityLevel 197 }{ 198 { 199 name: "tls", 200 secLevel: e2e.SecurityLevelTLS, 201 }, 202 { 203 name: "mtls", 204 secLevel: e2e.SecurityLevelMTLS, 205 }, 206 } 207 for _, test := range tests { 208 t.Run(test.name, func(t *testing.T) { 209 managementServer, nodeID, bootstrapContents, resolver, cleanup1 := setupManagementServer(t) 210 defer cleanup1() 211 212 lis, cleanup2 := setupGRPCServer(t, bootstrapContents) 213 defer cleanup2() 214 215 // Grab the host and port of the server and create client side xDS 216 // resources corresponding to it. 217 host, port, err := hostPortFromListener(lis) 218 if err != nil { 219 t.Fatalf("failed to retrieve host and port of server: %v", err) 220 } 221 222 // Create xDS resources to be consumed on the client side. This 223 // includes the listener, route configuration, cluster (with 224 // security configuration) and endpoint resources. 225 serviceName := "my-service-file-watcher-certs-" + test.name 226 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 227 DialTarget: serviceName, 228 NodeID: nodeID, 229 Host: host, 230 Port: port, 231 SecLevel: test.secLevel, 232 }) 233 234 // Create an inbound xDS listener resource for the server side that 235 // contains security configuration pointing to the file watcher 236 // plugin. 237 inboundLis := e2e.DefaultServerListener(host, port, test.secLevel) 238 resources.Listeners = append(resources.Listeners, inboundLis) 239 240 // Setup the management server with client and server resources. 241 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 242 defer cancel() 243 if err := managementServer.Update(ctx, resources); err != nil { 244 t.Fatal(err) 245 } 246 247 // Create client-side xDS credentials with an insecure fallback. 248 creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{ 249 FallbackCreds: insecure.NewCredentials(), 250 }) 251 if err != nil { 252 t.Fatal(err) 253 } 254 255 // Create a ClientConn with the xds scheme and make an RPC. 256 cc, err := grpc.DialContext(ctx, fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(creds), grpc.WithResolvers(resolver)) 257 if err != nil { 258 t.Fatalf("failed to dial local test server: %v", err) 259 } 260 defer cc.Close() 261 262 client := testpb.NewTestServiceClient(cc) 263 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { 264 t.Fatalf("rpc EmptyCall() failed: %v", err) 265 } 266 }) 267 } 268 } 269 270 // TestServerSideXDS_SecurityConfigChange is an e2e test where xDS is enabled on 271 // the server-side and xdsCredentials are configured for security. The control 272 // plane initially does not any security configuration. This forces the 273 // xdsCredentials to use fallback creds, which is this case is insecure creds. 274 // We verify that a client connecting with TLS creds is not able to successfully 275 // make an RPC. The control plane then sends a listener resource with security 276 // configuration pointing to the use of the file_watcher plugin and we verify 277 // that the same client is now able to successfully make an RPC. 278 func (s) TestServerSideXDS_SecurityConfigChange(t *testing.T) { 279 managementServer, nodeID, bootstrapContents, resolver, cleanup1 := setupManagementServer(t) 280 defer cleanup1() 281 282 lis, cleanup2 := setupGRPCServer(t, bootstrapContents) 283 defer cleanup2() 284 285 // Grab the host and port of the server and create client side xDS resources 286 // corresponding to it. This contains default resources with no security 287 // configuration in the Cluster resource. This should force the xDS 288 // credentials on the client to use its fallback. 289 host, port, err := hostPortFromListener(lis) 290 if err != nil { 291 t.Fatalf("failed to retrieve host and port of server: %v", err) 292 } 293 const serviceName = "my-service-security-config-change" 294 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 295 DialTarget: serviceName, 296 NodeID: nodeID, 297 Host: host, 298 Port: port, 299 SecLevel: e2e.SecurityLevelNone, 300 }) 301 302 // Create an inbound xDS listener resource for the server side that does not 303 // contain any security configuration. This should force the xDS credentials 304 // on server to use its fallback. 305 inboundLis := e2e.DefaultServerListener(host, port, e2e.SecurityLevelNone) 306 resources.Listeners = append(resources.Listeners, inboundLis) 307 308 // Setup the management server with client and server-side resources. 309 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 310 defer cancel() 311 if err := managementServer.Update(ctx, resources); err != nil { 312 t.Fatal(err) 313 } 314 315 // Create client-side xDS credentials with an insecure fallback. 316 xdsCreds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{ 317 FallbackCreds: insecure.NewCredentials(), 318 }) 319 if err != nil { 320 t.Fatal(err) 321 } 322 323 // Create a ClientConn with the xds scheme and make a successful RPC. 324 xdsCC, err := grpc.DialContext(ctx, fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(xdsCreds), grpc.WithResolvers(resolver)) 325 if err != nil { 326 t.Fatalf("failed to dial local test server: %v", err) 327 } 328 defer xdsCC.Close() 329 330 client := testpb.NewTestServiceClient(xdsCC) 331 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { 332 t.Fatalf("rpc EmptyCall() failed: %v", err) 333 } 334 335 // Create a ClientConn with TLS creds. This should fail since the server is 336 // using fallback credentials which in this case in insecure creds. 337 tlsCreds := createClientTLSCredentials(t) 338 tlsCC, err := grpc.DialContext(ctx, lis.Addr().String(), grpc.WithTransportCredentials(tlsCreds)) 339 if err != nil { 340 t.Fatalf("failed to dial local test server: %v", err) 341 } 342 defer tlsCC.Close() 343 344 // We don't set 'waitForReady` here since we want this call to failfast. 345 client = testpb.NewTestServiceClient(tlsCC) 346 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); status.Code(err) != codes.Unavailable { 347 t.Fatal("rpc EmptyCall() succeeded when expected to fail") 348 } 349 350 // Switch server and client side resources with ones that contain required 351 // security configuration for mTLS with a file watcher certificate provider. 352 resources = e2e.DefaultClientResources(e2e.ResourceParams{ 353 DialTarget: serviceName, 354 NodeID: nodeID, 355 Host: host, 356 Port: port, 357 SecLevel: e2e.SecurityLevelMTLS, 358 }) 359 inboundLis = e2e.DefaultServerListener(host, port, e2e.SecurityLevelMTLS) 360 resources.Listeners = append(resources.Listeners, inboundLis) 361 if err := managementServer.Update(ctx, resources); err != nil { 362 t.Fatal(err) 363 } 364 365 // Make another RPC with `waitForReady` set and expect this to succeed. 366 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { 367 t.Fatalf("rpc EmptyCall() failed: %v", err) 368 } 369 } 370 371 // TestServerSideXDS_RouteConfiguration is an e2e test which verifies routing 372 // functionality. The xDS enabled server will be set up with route configuration 373 // where the route configuration has routes with the correct routing actions 374 // (NonForwardingAction), and the RPC's matching those routes should proceed as 375 // normal. 376 func (s) TestServerSideXDS_RouteConfiguration(t *testing.T) { 377 oldRBAC := envconfig.XDSRBAC 378 envconfig.XDSRBAC = true 379 defer func() { 380 envconfig.XDSRBAC = oldRBAC 381 }() 382 managementServer, nodeID, bootstrapContents, resolver, cleanup1 := setupManagementServer(t) 383 defer cleanup1() 384 385 lis, cleanup2 := setupGRPCServer(t, bootstrapContents) 386 defer cleanup2() 387 388 host, port, err := hostPortFromListener(lis) 389 if err != nil { 390 t.Fatalf("failed to retrieve host and port of server: %v", err) 391 } 392 const serviceName = "my-service-fallback" 393 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 394 DialTarget: serviceName, 395 NodeID: nodeID, 396 Host: host, 397 Port: port, 398 SecLevel: e2e.SecurityLevelNone, 399 }) 400 401 // Create an inbound xDS listener resource with route configuration which 402 // selectively will allow RPC's through or not. This will test routing in 403 // xds(Unary|Stream)Interceptors. 404 vhs := []*v3routepb.VirtualHost{ 405 // Virtual host that will never be matched to test Virtual Host selection. 406 { 407 Domains: []string{"this will not match*"}, 408 Routes: []*v3routepb.Route{ 409 { 410 Match: &v3routepb.RouteMatch{ 411 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}, 412 }, 413 Action: &v3routepb.Route_NonForwardingAction{}, 414 }, 415 }, 416 }, 417 // This Virtual Host will actually get matched to. 418 { 419 Domains: []string{"*"}, 420 Routes: []*v3routepb.Route{ 421 // A routing rule that can be selectively triggered based on properties about incoming RPC. 422 { 423 Match: &v3routepb.RouteMatch{ 424 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/grpc.testing.TestService/EmptyCall"}, 425 // "Fully-qualified RPC method name with leading slash. Same as :path header". 426 }, 427 // Correct Action, so RPC's that match this route should proceed to interceptor processing. 428 Action: &v3routepb.Route_NonForwardingAction{}, 429 }, 430 // This routing rule is matched the same way as the one above, 431 // except has an incorrect action for the server side. However, 432 // since routing chooses the first route which matches an 433 // incoming RPC, this should never get invoked (iteration 434 // through this route slice is deterministic). 435 { 436 Match: &v3routepb.RouteMatch{ 437 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/grpc.testing.TestService/EmptyCall"}, 438 // "Fully-qualified RPC method name with leading slash. Same as :path header". 439 }, 440 // Incorrect Action, so RPC's that match this route should get denied. 441 Action: &v3routepb.Route_Route{ 442 Route: &v3routepb.RouteAction{ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: ""}}, 443 }, 444 }, 445 // Another routing rule that can be selectively triggered based on incoming RPC. 446 { 447 Match: &v3routepb.RouteMatch{ 448 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/grpc.testing.TestService/UnaryCall"}, 449 }, 450 // Wrong action (!Non_Forwarding_Action) so RPC's that match this route should get denied. 451 Action: &v3routepb.Route_Route{ 452 Route: &v3routepb.RouteAction{ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: ""}}, 453 }, 454 }, 455 // Another routing rule that can be selectively triggered based on incoming RPC. 456 { 457 Match: &v3routepb.RouteMatch{ 458 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/grpc.testing.TestService/StreamingInputCall"}, 459 }, 460 // Wrong action (!Non_Forwarding_Action) so RPC's that match this route should get denied. 461 Action: &v3routepb.Route_Route{ 462 Route: &v3routepb.RouteAction{ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: ""}}, 463 }, 464 }, 465 // Not matching route, this is be able to get invoked logically (i.e. doesn't have to match the Route configurations above). 466 }}, 467 } 468 inboundLis := &v3listenerpb.Listener{ 469 Name: fmt.Sprintf(e2e.ServerListenerResourceNameTemplate, net.JoinHostPort(host, strconv.Itoa(int(port)))), 470 Address: &v3corepb.Address{ 471 Address: &v3corepb.Address_SocketAddress{ 472 SocketAddress: &v3corepb.SocketAddress{ 473 Address: host, 474 PortSpecifier: &v3corepb.SocketAddress_PortValue{ 475 PortValue: port, 476 }, 477 }, 478 }, 479 }, 480 FilterChains: []*v3listenerpb.FilterChain{ 481 { 482 Name: "v4-wildcard", 483 FilterChainMatch: &v3listenerpb.FilterChainMatch{ 484 PrefixRanges: []*v3corepb.CidrRange{ 485 { 486 AddressPrefix: "0.0.0.0", 487 PrefixLen: &wrapperspb.UInt32Value{ 488 Value: uint32(0), 489 }, 490 }, 491 }, 492 SourceType: v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK, 493 SourcePrefixRanges: []*v3corepb.CidrRange{ 494 { 495 AddressPrefix: "0.0.0.0", 496 PrefixLen: &wrapperspb.UInt32Value{ 497 Value: uint32(0), 498 }, 499 }, 500 }, 501 }, 502 Filters: []*v3listenerpb.Filter{ 503 { 504 Name: "filter-1", 505 ConfigType: &v3listenerpb.Filter_TypedConfig{ 506 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 507 HttpFilters: []*v3httppb.HttpFilter{e2e.HTTPFilter("router", &v3routerpb.Router{})}, 508 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 509 RouteConfig: &v3routepb.RouteConfiguration{ 510 Name: "routeName", 511 VirtualHosts: vhs, 512 }, 513 }, 514 }), 515 }, 516 }, 517 }, 518 }, 519 { 520 Name: "v6-wildcard", 521 FilterChainMatch: &v3listenerpb.FilterChainMatch{ 522 PrefixRanges: []*v3corepb.CidrRange{ 523 { 524 AddressPrefix: "::", 525 PrefixLen: &wrapperspb.UInt32Value{ 526 Value: uint32(0), 527 }, 528 }, 529 }, 530 SourceType: v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK, 531 SourcePrefixRanges: []*v3corepb.CidrRange{ 532 { 533 AddressPrefix: "::", 534 PrefixLen: &wrapperspb.UInt32Value{ 535 Value: uint32(0), 536 }, 537 }, 538 }, 539 }, 540 Filters: []*v3listenerpb.Filter{ 541 { 542 Name: "filter-1", 543 ConfigType: &v3listenerpb.Filter_TypedConfig{ 544 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 545 HttpFilters: []*v3httppb.HttpFilter{e2e.HTTPFilter("router", &v3routerpb.Router{})}, 546 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 547 RouteConfig: &v3routepb.RouteConfiguration{ 548 Name: "routeName", 549 VirtualHosts: vhs, 550 }, 551 }, 552 }), 553 }, 554 }, 555 }, 556 }, 557 }, 558 } 559 resources.Listeners = append(resources.Listeners, inboundLis) 560 561 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 562 defer cancel() 563 // Setup the management server with client and server-side resources. 564 if err := managementServer.Update(ctx, resources); err != nil { 565 t.Fatal(err) 566 } 567 568 cc, err := grpc.DialContext(ctx, fmt.Sprintf("xds:///%s", serviceName), grpc.WithInsecure(), grpc.WithResolvers(resolver)) 569 if err != nil { 570 t.Fatalf("failed to dial local test server: %v", err) 571 } 572 defer cc.Close() 573 574 client := testpb.NewTestServiceClient(cc) 575 576 // This Empty Call should match to a route with a correct action 577 // (NonForwardingAction). Thus, this RPC should proceed as normal. There is 578 // a routing rule that this RPC would match to that has an incorrect action, 579 // but the server should only use the first route matched to with the 580 // correct action. 581 if _, err = client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { 582 t.Fatalf("rpc EmptyCall() failed: %v", err) 583 } 584 585 // This Unary Call should match to a route with an incorrect action. Thus, 586 // this RPC should not go through as per A36, and this call should receive 587 // an error with codes.Unavailable. 588 if _, err = client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != codes.Unavailable { 589 t.Fatalf("client.UnaryCall() = _, %v, want _, error code %s", err, codes.Unavailable) 590 } 591 592 // This Streaming Call should match to a route with an incorrect action. 593 // Thus, this RPC should not go through as per A36, and this call should 594 // receive an error with codes.Unavailable. 595 stream, err := client.StreamingInputCall(ctx) 596 if err != nil { 597 t.Fatalf("StreamingInputCall(_) = _, %v, want <nil>", err) 598 } 599 if _, err = stream.CloseAndRecv(); status.Code(err) != codes.Unavailable || !strings.Contains(err.Error(), "the incoming RPC matched to a route that was not of action type non forwarding") { 600 t.Fatalf("streaming RPC should have been denied") 601 } 602 603 // This Full Duplex should not match to a route, and thus should return an 604 // error and not proceed. 605 dStream, err := client.FullDuplexCall(ctx) 606 if err != nil { 607 t.Fatalf("FullDuplexCall(_) = _, %v, want <nil>", err) 608 } 609 if _, err = dStream.Recv(); status.Code(err) != codes.Unavailable || !strings.Contains(err.Error(), "the incoming RPC did not match a configured Route") { 610 t.Fatalf("streaming RPC should have been denied") 611 } 612 } 613 614 // serverListenerWithRBACHTTPFilters returns an xds Listener resource with HTTP Filters defined in the HCM, and a route 615 // configuration that always matches to a route and a VH. 616 func serverListenerWithRBACHTTPFilters(host string, port uint32, rbacCfg *rpb.RBAC) *v3listenerpb.Listener { 617 // Rather than declare typed config inline, take a HCM proto and append the 618 // RBAC Filters to it. 619 hcm := &v3httppb.HttpConnectionManager{ 620 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 621 RouteConfig: &v3routepb.RouteConfiguration{ 622 Name: "routeName", 623 VirtualHosts: []*v3routepb.VirtualHost{{ 624 Domains: []string{"*"}, 625 Routes: []*v3routepb.Route{{ 626 Match: &v3routepb.RouteMatch{ 627 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}, 628 }, 629 Action: &v3routepb.Route_NonForwardingAction{}, 630 }}, 631 // This tests override parsing + building when RBAC Filter 632 // passed both normal and override config. 633 TypedPerFilterConfig: map[string]*anypb.Any{ 634 "rbac": testutils.MarshalAny(&rpb.RBACPerRoute{Rbac: rbacCfg}), 635 }, 636 }}}, 637 }, 638 } 639 hcm.HttpFilters = nil 640 hcm.HttpFilters = append(hcm.HttpFilters, e2e.HTTPFilter("rbac", rbacCfg)) 641 hcm.HttpFilters = append(hcm.HttpFilters, e2e.RouterHTTPFilter) 642 643 return &v3listenerpb.Listener{ 644 Name: fmt.Sprintf(e2e.ServerListenerResourceNameTemplate, net.JoinHostPort(host, strconv.Itoa(int(port)))), 645 Address: &v3corepb.Address{ 646 Address: &v3corepb.Address_SocketAddress{ 647 SocketAddress: &v3corepb.SocketAddress{ 648 Address: host, 649 PortSpecifier: &v3corepb.SocketAddress_PortValue{ 650 PortValue: port, 651 }, 652 }, 653 }, 654 }, 655 FilterChains: []*v3listenerpb.FilterChain{ 656 { 657 Name: "v4-wildcard", 658 FilterChainMatch: &v3listenerpb.FilterChainMatch{ 659 PrefixRanges: []*v3corepb.CidrRange{ 660 { 661 AddressPrefix: "0.0.0.0", 662 PrefixLen: &wrapperspb.UInt32Value{ 663 Value: uint32(0), 664 }, 665 }, 666 }, 667 SourceType: v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK, 668 SourcePrefixRanges: []*v3corepb.CidrRange{ 669 { 670 AddressPrefix: "0.0.0.0", 671 PrefixLen: &wrapperspb.UInt32Value{ 672 Value: uint32(0), 673 }, 674 }, 675 }, 676 }, 677 Filters: []*v3listenerpb.Filter{ 678 { 679 Name: "filter-1", 680 ConfigType: &v3listenerpb.Filter_TypedConfig{ 681 TypedConfig: testutils.MarshalAny(hcm), 682 }, 683 }, 684 }, 685 }, 686 { 687 Name: "v6-wildcard", 688 FilterChainMatch: &v3listenerpb.FilterChainMatch{ 689 PrefixRanges: []*v3corepb.CidrRange{ 690 { 691 AddressPrefix: "::", 692 PrefixLen: &wrapperspb.UInt32Value{ 693 Value: uint32(0), 694 }, 695 }, 696 }, 697 SourceType: v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK, 698 SourcePrefixRanges: []*v3corepb.CidrRange{ 699 { 700 AddressPrefix: "::", 701 PrefixLen: &wrapperspb.UInt32Value{ 702 Value: uint32(0), 703 }, 704 }, 705 }, 706 }, 707 Filters: []*v3listenerpb.Filter{ 708 { 709 Name: "filter-1", 710 ConfigType: &v3listenerpb.Filter_TypedConfig{ 711 TypedConfig: testutils.MarshalAny(hcm), 712 }, 713 }, 714 }, 715 }, 716 }, 717 } 718 } 719 720 // TestRBACHTTPFilter tests the xds configured RBAC HTTP Filter. It sets up the 721 // full end to end flow, and makes sure certain RPC's are successful and proceed 722 // as normal and certain RPC's are denied by the RBAC HTTP Filter which gets 723 // called by hooked xds interceptors. 724 func (s) TestRBACHTTPFilter(t *testing.T) { 725 oldRBAC := envconfig.XDSRBAC 726 envconfig.XDSRBAC = true 727 defer func() { 728 envconfig.XDSRBAC = oldRBAC 729 }() 730 rbac.RegisterForTesting() 731 defer rbac.UnregisterForTesting() 732 tests := []struct { 733 name string 734 rbacCfg *rpb.RBAC 735 wantStatusEmptyCall codes.Code 736 wantStatusUnaryCall codes.Code 737 }{ 738 // This test tests an RBAC HTTP Filter which is configured to allow any RPC. 739 // Any RPC passing through this RBAC HTTP Filter should proceed as normal. 740 { 741 name: "allow-anything", 742 rbacCfg: &rpb.RBAC{ 743 Rules: &v3rbacpb.RBAC{ 744 Action: v3rbacpb.RBAC_ALLOW, 745 Policies: map[string]*v3rbacpb.Policy{ 746 "anyone": { 747 Permissions: []*v3rbacpb.Permission{ 748 {Rule: &v3rbacpb.Permission_Any{Any: true}}, 749 }, 750 Principals: []*v3rbacpb.Principal{ 751 {Identifier: &v3rbacpb.Principal_Any{Any: true}}, 752 }, 753 }, 754 }, 755 }, 756 }, 757 wantStatusEmptyCall: codes.OK, 758 wantStatusUnaryCall: codes.OK, 759 }, 760 // This test tests an RBAC HTTP Filter which is configured to allow only 761 // RPC's with certain paths ("UnaryCall"). Only unary calls passing 762 // through this RBAC HTTP Filter should proceed as normal, and any 763 // others should be denied. 764 { 765 name: "allow-certain-path", 766 rbacCfg: &rpb.RBAC{ 767 Rules: &v3rbacpb.RBAC{ 768 Action: v3rbacpb.RBAC_ALLOW, 769 Policies: map[string]*v3rbacpb.Policy{ 770 "certain-path": { 771 Permissions: []*v3rbacpb.Permission{ 772 {Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "/grpc.testing.TestService/UnaryCall"}}}}}}, 773 }, 774 Principals: []*v3rbacpb.Principal{ 775 {Identifier: &v3rbacpb.Principal_Any{Any: true}}, 776 }, 777 }, 778 }, 779 }, 780 }, 781 wantStatusEmptyCall: codes.PermissionDenied, 782 wantStatusUnaryCall: codes.OK, 783 }, 784 // This test that a RBAC Config with nil rules means that every RPC is 785 // allowed. This maps to the line "If absent, no enforcing RBAC policy 786 // will be applied" from the RBAC Proto documentation for the Rules 787 // field. 788 { 789 name: "absent-rules", 790 rbacCfg: &rpb.RBAC{ 791 Rules: nil, 792 }, 793 wantStatusEmptyCall: codes.OK, 794 wantStatusUnaryCall: codes.OK, 795 }, 796 // The two tests below test that configuring the xDS RBAC HTTP Filter 797 // with :authority and host header matchers end up being logically 798 // equivalent. This represents functionality from this line in A41 - 799 // "As documented for HeaderMatcher, Envoy aliases :authority and Host 800 // in its header map implementation, so they should be treated 801 // equivalent for the RBAC matchers; there must be no behavior change 802 // depending on which of the two header names is used in the RBAC 803 // policy." 804 805 // This test tests an xDS RBAC Filter with an :authority header matcher. 806 { 807 name: "match-on-authority", 808 rbacCfg: &rpb.RBAC{ 809 Rules: &v3rbacpb.RBAC{ 810 Action: v3rbacpb.RBAC_ALLOW, 811 Policies: map[string]*v3rbacpb.Policy{ 812 "match-on-authority": { 813 Permissions: []*v3rbacpb.Permission{ 814 {Rule: &v3rbacpb.Permission_Header{Header: &v3routepb.HeaderMatcher{Name: ":authority", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_PrefixMatch{PrefixMatch: "my-service-fallback"}}}}, 815 }, 816 Principals: []*v3rbacpb.Principal{ 817 {Identifier: &v3rbacpb.Principal_Any{Any: true}}, 818 }, 819 }, 820 }, 821 }, 822 }, 823 wantStatusEmptyCall: codes.OK, 824 wantStatusUnaryCall: codes.OK, 825 }, 826 // This test tests that configuring an xDS RBAC Filter with a host 827 // header matcher has the same behavior as if it was configured with 828 // :authority. Since host and authority are aliased, this should still 829 // continue to match on incoming RPC's :authority, just as the test 830 // above. 831 { 832 name: "match-on-host", 833 rbacCfg: &rpb.RBAC{ 834 Rules: &v3rbacpb.RBAC{ 835 Action: v3rbacpb.RBAC_ALLOW, 836 Policies: map[string]*v3rbacpb.Policy{ 837 "match-on-authority": { 838 Permissions: []*v3rbacpb.Permission{ 839 {Rule: &v3rbacpb.Permission_Header{Header: &v3routepb.HeaderMatcher{Name: "host", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_PrefixMatch{PrefixMatch: "my-service-fallback"}}}}, 840 }, 841 Principals: []*v3rbacpb.Principal{ 842 {Identifier: &v3rbacpb.Principal_Any{Any: true}}, 843 }, 844 }, 845 }, 846 }, 847 }, 848 wantStatusEmptyCall: codes.OK, 849 wantStatusUnaryCall: codes.OK, 850 }, 851 // This test tests that the RBAC HTTP Filter hard codes the :method 852 // header to POST. Since the RBAC Configuration says to deny every RPC 853 // with a method :POST, every RPC tried should be denied. 854 { 855 name: "deny-post", 856 rbacCfg: &rpb.RBAC{ 857 Rules: &v3rbacpb.RBAC{ 858 Action: v3rbacpb.RBAC_DENY, 859 Policies: map[string]*v3rbacpb.Policy{ 860 "post-method": { 861 Permissions: []*v3rbacpb.Permission{ 862 {Rule: &v3rbacpb.Permission_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_ExactMatch{ExactMatch: "POST"}}}}, 863 }, 864 Principals: []*v3rbacpb.Principal{ 865 {Identifier: &v3rbacpb.Principal_Any{Any: true}}, 866 }, 867 }, 868 }, 869 }, 870 }, 871 wantStatusEmptyCall: codes.PermissionDenied, 872 wantStatusUnaryCall: codes.PermissionDenied, 873 }, 874 // This test tests that RBAC ignores the TE: trailers header (which is 875 // hardcoded in http2_client.go for every RPC). Since the RBAC 876 // Configuration says to only ALLOW RPC's with a TE: Trailers, every RPC 877 // tried should be denied. 878 { 879 name: "allow-only-te", 880 rbacCfg: &rpb.RBAC{ 881 Rules: &v3rbacpb.RBAC{ 882 Action: v3rbacpb.RBAC_ALLOW, 883 Policies: map[string]*v3rbacpb.Policy{ 884 "post-method": { 885 Permissions: []*v3rbacpb.Permission{ 886 {Rule: &v3rbacpb.Permission_Header{Header: &v3routepb.HeaderMatcher{Name: "TE", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_ExactMatch{ExactMatch: "trailers"}}}}, 887 }, 888 Principals: []*v3rbacpb.Principal{ 889 {Identifier: &v3rbacpb.Principal_Any{Any: true}}, 890 }, 891 }, 892 }, 893 }, 894 }, 895 wantStatusEmptyCall: codes.PermissionDenied, 896 wantStatusUnaryCall: codes.PermissionDenied, 897 }, 898 // This test tests that an RBAC Config with Action.LOG configured allows 899 // every RPC through. This maps to the line "At this time, if the 900 // RBAC.action is Action.LOG then the policy will be completely ignored, 901 // as if RBAC was not configurated." from A41 902 { 903 name: "action-log", 904 rbacCfg: &rpb.RBAC{ 905 Rules: &v3rbacpb.RBAC{ 906 Action: v3rbacpb.RBAC_LOG, 907 Policies: map[string]*v3rbacpb.Policy{ 908 "anyone": { 909 Permissions: []*v3rbacpb.Permission{ 910 {Rule: &v3rbacpb.Permission_Any{Any: true}}, 911 }, 912 Principals: []*v3rbacpb.Principal{ 913 {Identifier: &v3rbacpb.Principal_Any{Any: true}}, 914 }, 915 }, 916 }, 917 }, 918 }, 919 wantStatusEmptyCall: codes.OK, 920 wantStatusUnaryCall: codes.OK, 921 }, 922 } 923 924 for _, test := range tests { 925 t.Run(test.name, func(t *testing.T) { 926 func() { 927 managementServer, nodeID, bootstrapContents, resolver, cleanup1 := setupManagementServer(t) 928 defer cleanup1() 929 930 lis, cleanup2 := setupGRPCServer(t, bootstrapContents) 931 defer cleanup2() 932 933 host, port, err := hostPortFromListener(lis) 934 if err != nil { 935 t.Fatalf("failed to retrieve host and port of server: %v", err) 936 } 937 const serviceName = "my-service-fallback" 938 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 939 DialTarget: serviceName, 940 NodeID: nodeID, 941 Host: host, 942 Port: port, 943 SecLevel: e2e.SecurityLevelNone, 944 }) 945 inboundLis := serverListenerWithRBACHTTPFilters(host, port, test.rbacCfg) 946 resources.Listeners = append(resources.Listeners, inboundLis) 947 948 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 949 defer cancel() 950 // Setup the management server with client and server-side resources. 951 if err := managementServer.Update(ctx, resources); err != nil { 952 t.Fatal(err) 953 } 954 955 cc, err := grpc.DialContext(ctx, fmt.Sprintf("xds:///%s", serviceName), grpc.WithInsecure(), grpc.WithResolvers(resolver)) 956 if err != nil { 957 t.Fatalf("failed to dial local test server: %v", err) 958 } 959 defer cc.Close() 960 961 client := testpb.NewTestServiceClient(cc) 962 963 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); status.Code(err) != test.wantStatusEmptyCall { 964 t.Fatalf("EmptyCall() returned err with status: %v, wantStatusEmptyCall: %v", status.Code(err), test.wantStatusEmptyCall) 965 } 966 967 if _, err := client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != test.wantStatusUnaryCall { 968 t.Fatalf("UnaryCall() returned err with status: %v, wantStatusUnaryCall: %v", err, test.wantStatusUnaryCall) 969 } 970 971 // Toggle the RBAC Env variable off, this should disable RBAC and allow any RPC"s through (will not go through 972 // routing or processed by HTTP Filters and thus will never get denied by RBAC). 973 envconfig.XDSRBAC = false 974 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); status.Code(err) != codes.OK { 975 t.Fatalf("EmptyCall() returned err with status: %v, once RBAC is disabled all RPC's should proceed as normal", status.Code(err)) 976 } 977 if _, err := client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != codes.OK { 978 t.Fatalf("UnaryCall() returned err with status: %v, once RBAC is disabled all RPC's should proceed as normal", status.Code(err)) 979 } 980 // Toggle RBAC back on for next iterations. 981 envconfig.XDSRBAC = true 982 }() 983 }) 984 } 985 } 986 987 // serverListenerWithBadRouteConfiguration returns an xds Listener resource with 988 // a Route Configuration that will never successfully match in order to test 989 // RBAC Environment variable being toggled on and off. 990 func serverListenerWithBadRouteConfiguration(host string, port uint32) *v3listenerpb.Listener { 991 return &v3listenerpb.Listener{ 992 Name: fmt.Sprintf(e2e.ServerListenerResourceNameTemplate, net.JoinHostPort(host, strconv.Itoa(int(port)))), 993 Address: &v3corepb.Address{ 994 Address: &v3corepb.Address_SocketAddress{ 995 SocketAddress: &v3corepb.SocketAddress{ 996 Address: host, 997 PortSpecifier: &v3corepb.SocketAddress_PortValue{ 998 PortValue: port, 999 }, 1000 }, 1001 }, 1002 }, 1003 FilterChains: []*v3listenerpb.FilterChain{ 1004 { 1005 Name: "v4-wildcard", 1006 FilterChainMatch: &v3listenerpb.FilterChainMatch{ 1007 PrefixRanges: []*v3corepb.CidrRange{ 1008 { 1009 AddressPrefix: "0.0.0.0", 1010 PrefixLen: &wrapperspb.UInt32Value{ 1011 Value: uint32(0), 1012 }, 1013 }, 1014 }, 1015 SourceType: v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK, 1016 SourcePrefixRanges: []*v3corepb.CidrRange{ 1017 { 1018 AddressPrefix: "0.0.0.0", 1019 PrefixLen: &wrapperspb.UInt32Value{ 1020 Value: uint32(0), 1021 }, 1022 }, 1023 }, 1024 }, 1025 Filters: []*v3listenerpb.Filter{ 1026 { 1027 Name: "filter-1", 1028 ConfigType: &v3listenerpb.Filter_TypedConfig{ 1029 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 1030 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 1031 RouteConfig: &v3routepb.RouteConfiguration{ 1032 Name: "routeName", 1033 VirtualHosts: []*v3routepb.VirtualHost{{ 1034 // Incoming RPC's will try and match to Virtual Hosts based on their :authority header. 1035 // Thus, incoming RPC's will never match to a Virtual Host (server side requires matching 1036 // to a VH/Route of type Non Forwarding Action to proceed normally), and all incoming RPC's 1037 // with this route configuration will be denied. 1038 Domains: []string{"will-never-match"}, 1039 Routes: []*v3routepb.Route{{ 1040 Match: &v3routepb.RouteMatch{ 1041 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}, 1042 }, 1043 Action: &v3routepb.Route_NonForwardingAction{}, 1044 }}}}}, 1045 }, 1046 HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter}, 1047 }), 1048 }, 1049 }, 1050 }, 1051 }, 1052 { 1053 Name: "v6-wildcard", 1054 FilterChainMatch: &v3listenerpb.FilterChainMatch{ 1055 PrefixRanges: []*v3corepb.CidrRange{ 1056 { 1057 AddressPrefix: "::", 1058 PrefixLen: &wrapperspb.UInt32Value{ 1059 Value: uint32(0), 1060 }, 1061 }, 1062 }, 1063 SourceType: v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK, 1064 SourcePrefixRanges: []*v3corepb.CidrRange{ 1065 { 1066 AddressPrefix: "::", 1067 PrefixLen: &wrapperspb.UInt32Value{ 1068 Value: uint32(0), 1069 }, 1070 }, 1071 }, 1072 }, 1073 Filters: []*v3listenerpb.Filter{ 1074 { 1075 Name: "filter-1", 1076 ConfigType: &v3listenerpb.Filter_TypedConfig{ 1077 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 1078 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 1079 RouteConfig: &v3routepb.RouteConfiguration{ 1080 Name: "routeName", 1081 VirtualHosts: []*v3routepb.VirtualHost{{ 1082 // Incoming RPC's will try and match to Virtual Hosts based on their :authority header. 1083 // Thus, incoming RPC's will never match to a Virtual Host (server side requires matching 1084 // to a VH/Route of type Non Forwarding Action to proceed normally), and all incoming RPC's 1085 // with this route configuration will be denied. 1086 Domains: []string{"will-never-match"}, 1087 Routes: []*v3routepb.Route{{ 1088 Match: &v3routepb.RouteMatch{ 1089 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}, 1090 }, 1091 Action: &v3routepb.Route_NonForwardingAction{}, 1092 }}}}}, 1093 }, 1094 HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter}, 1095 }), 1096 }, 1097 }, 1098 }, 1099 }, 1100 }, 1101 } 1102 } 1103 1104 func (s) TestRBACToggledOn_WithBadRouteConfiguration(t *testing.T) { 1105 // Turn RBAC support on. 1106 oldRBAC := envconfig.XDSRBAC 1107 envconfig.XDSRBAC = true 1108 defer func() { 1109 envconfig.XDSRBAC = oldRBAC 1110 }() 1111 1112 managementServer, nodeID, bootstrapContents, resolver, cleanup1 := setupManagementServer(t) 1113 defer cleanup1() 1114 1115 lis, cleanup2 := setupGRPCServer(t, bootstrapContents) 1116 defer cleanup2() 1117 1118 host, port, err := hostPortFromListener(lis) 1119 if err != nil { 1120 t.Fatalf("failed to retrieve host and port of server: %v", err) 1121 } 1122 const serviceName = "my-service-fallback" 1123 1124 // The inbound listener needs a route table that will never match on a VH, 1125 // and thus shouldn't allow incoming RPC's to proceed. 1126 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 1127 DialTarget: serviceName, 1128 NodeID: nodeID, 1129 Host: host, 1130 Port: port, 1131 SecLevel: e2e.SecurityLevelNone, 1132 }) 1133 // Since RBAC support is turned ON, all the RPC's should get denied with 1134 // status code Unavailable due to not matching to a route of type Non 1135 // Forwarding Action (Route Table not configured properly). 1136 inboundLis := serverListenerWithBadRouteConfiguration(host, port) 1137 resources.Listeners = append(resources.Listeners, inboundLis) 1138 1139 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1140 defer cancel() 1141 // Setup the management server with client and server-side resources. 1142 if err := managementServer.Update(ctx, resources); err != nil { 1143 t.Fatal(err) 1144 } 1145 1146 cc, err := grpc.DialContext(ctx, fmt.Sprintf("xds:///%s", serviceName), grpc.WithInsecure(), grpc.WithResolvers(resolver)) 1147 if err != nil { 1148 t.Fatalf("failed to dial local test server: %v", err) 1149 } 1150 defer cc.Close() 1151 1152 client := testpb.NewTestServiceClient(cc) 1153 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); status.Code(err) != codes.Unavailable { 1154 t.Fatalf("EmptyCall() returned err with status: %v, if RBAC is disabled all RPC's should proceed as normal", status.Code(err)) 1155 } 1156 if _, err := client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != codes.Unavailable { 1157 t.Fatalf("UnaryCall() returned err with status: %v, if RBAC is disabled all RPC's should proceed as normal", status.Code(err)) 1158 } 1159 } 1160 1161 func (s) TestRBACToggledOff_WithBadRouteConfiguration(t *testing.T) { 1162 // Turn RBAC support off. 1163 oldRBAC := envconfig.XDSRBAC 1164 envconfig.XDSRBAC = false 1165 defer func() { 1166 envconfig.XDSRBAC = oldRBAC 1167 }() 1168 1169 managementServer, nodeID, bootstrapContents, resolver, cleanup1 := setupManagementServer(t) 1170 defer cleanup1() 1171 1172 lis, cleanup2 := setupGRPCServer(t, bootstrapContents) 1173 defer cleanup2() 1174 1175 host, port, err := hostPortFromListener(lis) 1176 if err != nil { 1177 t.Fatalf("failed to retrieve host and port of server: %v", err) 1178 } 1179 const serviceName = "my-service-fallback" 1180 1181 // The inbound listener needs a route table that will never match on a VH, 1182 // and thus shouldn't allow incoming RPC's to proceed. 1183 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 1184 DialTarget: serviceName, 1185 NodeID: nodeID, 1186 Host: host, 1187 Port: port, 1188 SecLevel: e2e.SecurityLevelNone, 1189 }) 1190 // This bad route configuration shouldn't affect incoming RPC's from 1191 // proceeding as normal, as the configuration shouldn't be parsed due to the 1192 // RBAC Environment variable not being set to true. 1193 inboundLis := serverListenerWithBadRouteConfiguration(host, port) 1194 resources.Listeners = append(resources.Listeners, inboundLis) 1195 1196 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 1197 defer cancel() 1198 // Setup the management server with client and server-side resources. 1199 if err := managementServer.Update(ctx, resources); err != nil { 1200 t.Fatal(err) 1201 } 1202 1203 cc, err := grpc.DialContext(ctx, fmt.Sprintf("xds:///%s", serviceName), grpc.WithInsecure(), grpc.WithResolvers(resolver)) 1204 if err != nil { 1205 t.Fatalf("failed to dial local test server: %v", err) 1206 } 1207 defer cc.Close() 1208 1209 client := testpb.NewTestServiceClient(cc) 1210 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); status.Code(err) != codes.OK { 1211 t.Fatalf("EmptyCall() returned err with status: %v, if RBAC is disabled all RPC's should proceed as normal", status.Code(err)) 1212 } 1213 if _, err := client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != codes.OK { 1214 t.Fatalf("UnaryCall() returned err with status: %v, if RBAC is disabled all RPC's should proceed as normal", status.Code(err)) 1215 } 1216 }