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

     1  /*
     2   *
     3   * Copyright 2021 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package xds_test
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"testing"
    25  
    26  	"google.golang.org/grpc"
    27  	"google.golang.org/grpc/credentials/insecure"
    28  	xdscreds "google.golang.org/grpc/credentials/xds"
    29  	"google.golang.org/grpc/internal"
    30  	"google.golang.org/grpc/internal/stubserver"
    31  	"google.golang.org/grpc/internal/testutils"
    32  	"google.golang.org/grpc/internal/testutils/xds/e2e"
    33  	"google.golang.org/grpc/internal/testutils/xds/e2e/setup"
    34  	"google.golang.org/grpc/resolver"
    35  
    36  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    37  	v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    38  	"github.com/google/uuid"
    39  	testgrpc "google.golang.org/grpc/interop/grpc_testing"
    40  	testpb "google.golang.org/grpc/interop/grpc_testing"
    41  )
    42  
    43  func (s) TestUnmarshalListener_WithUpdateValidatorFunc(t *testing.T) {
    44  	const (
    45  		serviceName                     = "my-service-client-side-xds"
    46  		missingIdentityProviderInstance = "missing-identity-provider-instance"
    47  		missingRootProviderInstance     = "missing-root-provider-instance"
    48  	)
    49  
    50  	tests := []struct {
    51  		name           string
    52  		securityConfig *v3corepb.TransportSocket
    53  		wantErr        bool
    54  	}{
    55  		{
    56  			name: "both identity and root providers are not present in bootstrap",
    57  			securityConfig: &v3corepb.TransportSocket{
    58  				Name: "envoy.transport_sockets.tls",
    59  				ConfigType: &v3corepb.TransportSocket_TypedConfig{
    60  					TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
    61  						CommonTlsContext: &v3tlspb.CommonTlsContext{
    62  							TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
    63  								InstanceName: missingIdentityProviderInstance,
    64  							},
    65  							ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
    66  								ValidationContext: &v3tlspb.CertificateValidationContext{
    67  									CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
    68  										InstanceName: missingRootProviderInstance,
    69  									},
    70  								},
    71  							},
    72  						},
    73  					}),
    74  				},
    75  			},
    76  			wantErr: true,
    77  		},
    78  		{
    79  			name: "only identity provider is not present in bootstrap",
    80  			securityConfig: &v3corepb.TransportSocket{
    81  				Name: "envoy.transport_sockets.tls",
    82  				ConfigType: &v3corepb.TransportSocket_TypedConfig{
    83  					TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
    84  						CommonTlsContext: &v3tlspb.CommonTlsContext{
    85  							TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
    86  								InstanceName: missingIdentityProviderInstance,
    87  							},
    88  							ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
    89  								ValidationContext: &v3tlspb.CertificateValidationContext{
    90  									CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
    91  										InstanceName: e2e.ServerSideCertProviderInstance,
    92  									},
    93  								},
    94  							},
    95  						},
    96  					}),
    97  				},
    98  			},
    99  			wantErr: true,
   100  		},
   101  		{
   102  			name: "only root provider is not present in bootstrap",
   103  			securityConfig: &v3corepb.TransportSocket{
   104  				Name: "envoy.transport_sockets.tls",
   105  				ConfigType: &v3corepb.TransportSocket_TypedConfig{
   106  					TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   107  						CommonTlsContext: &v3tlspb.CommonTlsContext{
   108  							TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   109  								InstanceName: e2e.ServerSideCertProviderInstance,
   110  							},
   111  							ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   112  								ValidationContext: &v3tlspb.CertificateValidationContext{
   113  									CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   114  										InstanceName: missingRootProviderInstance,
   115  									},
   116  								},
   117  							},
   118  						},
   119  					}),
   120  				},
   121  			},
   122  			wantErr: true,
   123  		},
   124  		{
   125  			name: "both identity and root providers are present in bootstrap",
   126  			securityConfig: &v3corepb.TransportSocket{
   127  				Name: "envoy.transport_sockets.tls",
   128  				ConfigType: &v3corepb.TransportSocket_TypedConfig{
   129  					TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   130  						CommonTlsContext: &v3tlspb.CommonTlsContext{
   131  							TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   132  								InstanceName: e2e.ServerSideCertProviderInstance,
   133  							},
   134  							ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   135  								ValidationContext: &v3tlspb.CertificateValidationContext{
   136  									CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   137  										InstanceName: e2e.ServerSideCertProviderInstance,
   138  									},
   139  								},
   140  							},
   141  						},
   142  					}),
   143  				},
   144  			},
   145  			wantErr: false,
   146  		},
   147  	}
   148  
   149  	for _, test := range tests {
   150  		t.Run(test.name, func(t *testing.T) {
   151  			managementServer, nodeID, bootstrapContents, xdsResolver := setup.ManagementServerAndResolver(t)
   152  
   153  			lis, cleanup2 := setupGRPCServer(t, bootstrapContents)
   154  			defer cleanup2()
   155  
   156  			// Grab the host and port of the server and create client side xDS
   157  			// resources corresponding to it.
   158  			host, port, err := hostPortFromListener(lis)
   159  			if err != nil {
   160  				t.Fatalf("failed to retrieve host and port of server: %v", err)
   161  			}
   162  
   163  			// Create xDS resources to be consumed on the client side. This
   164  			// includes the listener, route configuration, cluster (with
   165  			// security configuration) and endpoint resources.
   166  			resources := e2e.DefaultClientResources(e2e.ResourceParams{
   167  				DialTarget: serviceName,
   168  				NodeID:     nodeID,
   169  				Host:       host,
   170  				Port:       port,
   171  				SecLevel:   e2e.SecurityLevelMTLS,
   172  			})
   173  
   174  			// Create an inbound xDS listener resource for the server side.
   175  			inboundLis := e2e.DefaultServerListener(host, port, e2e.SecurityLevelMTLS, "routeName")
   176  			for _, fc := range inboundLis.GetFilterChains() {
   177  				fc.TransportSocket = test.securityConfig
   178  			}
   179  			resources.Listeners = append(resources.Listeners, inboundLis)
   180  
   181  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   182  			defer cancel()
   183  			if err := managementServer.Update(ctx, resources); err != nil {
   184  				t.Fatal(err)
   185  			}
   186  
   187  			// Create client-side xDS credentials with an insecure fallback.
   188  			creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
   189  			if err != nil {
   190  				t.Fatal(err)
   191  			}
   192  
   193  			// Create a ClientConn with the xds scheme and make an RPC.
   194  			cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(creds), grpc.WithResolvers(xdsResolver))
   195  			if err != nil {
   196  				t.Fatalf("grpc.NewClient() failed: %v", err)
   197  			}
   198  			defer cc.Close()
   199  
   200  			// Make a context with a shorter timeout from the top level test
   201  			// context for cases where we expect failures.
   202  			timeout := defaultTestTimeout
   203  			if test.wantErr {
   204  				timeout = defaultTestShortTimeout
   205  			}
   206  			ctx, cancel = context.WithTimeout(ctx, timeout)
   207  			defer cancel()
   208  			client := testgrpc.NewTestServiceClient(cc)
   209  			if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); (err != nil) != test.wantErr {
   210  				t.Fatalf("EmptyCall() returned err: %v, wantErr %v", err, test.wantErr)
   211  			}
   212  		})
   213  	}
   214  }
   215  
   216  func (s) TestUnmarshalCluster_WithUpdateValidatorFunc(t *testing.T) {
   217  	const (
   218  		serviceName                     = "my-service-client-side-xds"
   219  		missingIdentityProviderInstance = "missing-identity-provider-instance"
   220  		missingRootProviderInstance     = "missing-root-provider-instance"
   221  	)
   222  
   223  	tests := []struct {
   224  		name           string
   225  		securityConfig *v3corepb.TransportSocket
   226  		wantErr        bool
   227  	}{
   228  		{
   229  			name: "both identity and root providers are not present in bootstrap",
   230  			securityConfig: &v3corepb.TransportSocket{
   231  				Name: "envoy.transport_sockets.tls",
   232  				ConfigType: &v3corepb.TransportSocket_TypedConfig{
   233  					TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   234  						CommonTlsContext: &v3tlspb.CommonTlsContext{
   235  							TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   236  								InstanceName: missingIdentityProviderInstance,
   237  							},
   238  							ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   239  								ValidationContext: &v3tlspb.CertificateValidationContext{
   240  									CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   241  										InstanceName: missingRootProviderInstance,
   242  									},
   243  								},
   244  							},
   245  						},
   246  					}),
   247  				},
   248  			},
   249  			wantErr: true,
   250  		},
   251  		{
   252  			name: "only identity provider is not present in bootstrap",
   253  			securityConfig: &v3corepb.TransportSocket{
   254  				Name: "envoy.transport_sockets.tls",
   255  				ConfigType: &v3corepb.TransportSocket_TypedConfig{
   256  					TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   257  						CommonTlsContext: &v3tlspb.CommonTlsContext{
   258  							TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   259  								InstanceName: missingIdentityProviderInstance,
   260  							},
   261  							ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   262  								ValidationContext: &v3tlspb.CertificateValidationContext{
   263  									CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   264  										InstanceName: e2e.ClientSideCertProviderInstance,
   265  									},
   266  								},
   267  							},
   268  						},
   269  					}),
   270  				},
   271  			},
   272  			wantErr: true,
   273  		},
   274  		{
   275  			name: "only root provider is not present in bootstrap",
   276  			securityConfig: &v3corepb.TransportSocket{
   277  				Name: "envoy.transport_sockets.tls",
   278  				ConfigType: &v3corepb.TransportSocket_TypedConfig{
   279  					TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   280  						CommonTlsContext: &v3tlspb.CommonTlsContext{
   281  							TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   282  								InstanceName: e2e.ClientSideCertProviderInstance,
   283  							},
   284  							ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   285  								ValidationContext: &v3tlspb.CertificateValidationContext{
   286  									CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   287  										InstanceName: missingRootProviderInstance,
   288  									},
   289  								},
   290  							},
   291  						},
   292  					}),
   293  				},
   294  			},
   295  			wantErr: true,
   296  		},
   297  		{
   298  			name: "both identity and root providers are present in bootstrap",
   299  			securityConfig: &v3corepb.TransportSocket{
   300  				Name: "envoy.transport_sockets.tls",
   301  				ConfigType: &v3corepb.TransportSocket_TypedConfig{
   302  					TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
   303  						CommonTlsContext: &v3tlspb.CommonTlsContext{
   304  							TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   305  								InstanceName: e2e.ClientSideCertProviderInstance,
   306  							},
   307  							ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   308  								ValidationContext: &v3tlspb.CertificateValidationContext{
   309  									CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   310  										InstanceName: e2e.ClientSideCertProviderInstance,
   311  									},
   312  								},
   313  							},
   314  						},
   315  					}),
   316  				},
   317  			},
   318  			wantErr: false,
   319  		},
   320  	}
   321  
   322  	for _, test := range tests {
   323  		t.Run(test.name, func(t *testing.T) {
   324  			managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{AllowResourceSubset: true})
   325  
   326  			// Create bootstrap configuration pointing to the above management
   327  			// server with certificate provider configuration.
   328  			nodeID := uuid.New().String()
   329  			bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address)
   330  
   331  			// Create an xDS resolver with the above bootstrap configuration.
   332  			if internal.NewXDSResolverWithConfigForTesting == nil {
   333  				t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
   334  			}
   335  			xdsResolver, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bootstrapContents)
   336  			if err != nil {
   337  				t.Fatalf("Failed to create xDS resolver for testing: %v", err)
   338  			}
   339  
   340  			server := stubserver.StartTestService(t, nil)
   341  			defer server.Stop()
   342  
   343  			// This creates a `Cluster` resource with a security config which
   344  			// refers to `e2e.ClientSideCertProviderInstance` for both root and
   345  			// identity certs.
   346  			resources := e2e.DefaultClientResources(e2e.ResourceParams{
   347  				DialTarget: serviceName,
   348  				NodeID:     nodeID,
   349  				Host:       "localhost",
   350  				Port:       testutils.ParsePort(t, server.Address),
   351  				SecLevel:   e2e.SecurityLevelMTLS,
   352  			})
   353  			resources.Clusters[0].TransportSocket = test.securityConfig
   354  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   355  			defer cancel()
   356  			if err := managementServer.Update(ctx, resources); err != nil {
   357  				t.Fatal(err)
   358  			}
   359  
   360  			cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(xdsResolver))
   361  			if err != nil {
   362  				t.Fatalf("grpc.NewClient() failed: %v", err)
   363  			}
   364  			defer cc.Close()
   365  
   366  			// Make a context with a shorter timeout from the top level test
   367  			// context for cases where we expect failures.
   368  			timeout := defaultTestTimeout
   369  			if test.wantErr {
   370  				timeout = defaultTestShortTimeout
   371  			}
   372  			ctx2, cancel2 := context.WithTimeout(ctx, timeout)
   373  			defer cancel2()
   374  			client := testgrpc.NewTestServiceClient(cc)
   375  			if _, err := client.EmptyCall(ctx2, &testpb.Empty{}, grpc.WaitForReady(true)); (err != nil) != test.wantErr {
   376  				t.Fatalf("EmptyCall() returned err: %v, wantErr %v", err, test.wantErr)
   377  			}
   378  		})
   379  	}
   380  }