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