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