...

Source file src/google.golang.org/grpc/credentials/tls/certprovider/store_test.go

Documentation: google.golang.org/grpc/credentials/tls/certprovider

     1  /*
     2   *
     3   * Copyright 2020 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package certprovider
    20  
    21  import (
    22  	"context"
    23  	"crypto/tls"
    24  	"crypto/x509"
    25  	"errors"
    26  	"fmt"
    27  	"os"
    28  	"testing"
    29  	"time"
    30  
    31  	"google.golang.org/grpc/internal/grpctest"
    32  	"google.golang.org/grpc/internal/testutils"
    33  	"google.golang.org/grpc/testdata"
    34  )
    35  
    36  const (
    37  	fakeProvider1Name       = "fake-certificate-provider-1"
    38  	fakeProvider2Name       = "fake-certificate-provider-2"
    39  	fakeConfig              = "my fake config"
    40  	defaultTestTimeout      = 5 * time.Second
    41  	defaultTestShortTimeout = 10 * time.Millisecond
    42  )
    43  
    44  var fpb1, fpb2 *fakeProviderBuilder
    45  
    46  func init() {
    47  	fpb1 = &fakeProviderBuilder{
    48  		name:         fakeProvider1Name,
    49  		providerChan: testutils.NewChannel(),
    50  	}
    51  	fpb2 = &fakeProviderBuilder{
    52  		name:         fakeProvider2Name,
    53  		providerChan: testutils.NewChannel(),
    54  	}
    55  	Register(fpb1)
    56  	Register(fpb2)
    57  }
    58  
    59  type s struct {
    60  	grpctest.Tester
    61  }
    62  
    63  func Test(t *testing.T) {
    64  	grpctest.RunSubTests(t, s{})
    65  }
    66  
    67  // fakeProviderBuilder builds new instances of fakeProvider and interprets the
    68  // config provided to it as a string.
    69  type fakeProviderBuilder struct {
    70  	name         string
    71  	providerChan *testutils.Channel
    72  }
    73  
    74  func (b *fakeProviderBuilder) ParseConfig(config any) (*BuildableConfig, error) {
    75  	s, ok := config.(string)
    76  	if !ok {
    77  		return nil, fmt.Errorf("providerBuilder %s received config of type %T, want string", b.name, config)
    78  	}
    79  	return NewBuildableConfig(b.name, []byte(s), func(BuildOptions) Provider {
    80  		fp := &fakeProvider{
    81  			Distributor: NewDistributor(),
    82  			config:      s,
    83  		}
    84  		b.providerChan.Send(fp)
    85  		return fp
    86  	}), nil
    87  }
    88  
    89  func (b *fakeProviderBuilder) Name() string {
    90  	return b.name
    91  }
    92  
    93  // fakeProvider is an implementation of the Provider interface which provides a
    94  // method for tests to invoke to push new key materials.
    95  type fakeProvider struct {
    96  	*Distributor
    97  	config string
    98  }
    99  
   100  func (p *fakeProvider) Start(BuildOptions) Provider {
   101  	// This is practically a no-op since this provider doesn't do any work which
   102  	// needs to be started at this point.
   103  	return p
   104  }
   105  
   106  // newKeyMaterial allows tests to push new key material to the fake provider
   107  // which will be made available to users of this provider.
   108  func (p *fakeProvider) newKeyMaterial(km *KeyMaterial, err error) {
   109  	p.Distributor.Set(km, err)
   110  }
   111  
   112  // Close helps implement the Provider interface.
   113  func (p *fakeProvider) Close() {
   114  	p.Distributor.Stop()
   115  }
   116  
   117  // loadKeyMaterials is a helper to read cert/key files from testdata and convert
   118  // them into a KeyMaterialReader struct.
   119  func loadKeyMaterials(t *testing.T, cert, key, ca string) *KeyMaterial {
   120  	t.Helper()
   121  
   122  	certs, err := tls.LoadX509KeyPair(testdata.Path(cert), testdata.Path(key))
   123  	if err != nil {
   124  		t.Fatalf("Failed to load keyPair: %v", err)
   125  	}
   126  
   127  	pemData, err := os.ReadFile(testdata.Path(ca))
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	roots := x509.NewCertPool()
   132  	roots.AppendCertsFromPEM(pemData)
   133  	return &KeyMaterial{Certs: []tls.Certificate{certs}, Roots: roots}
   134  }
   135  
   136  // kmReader wraps the KeyMaterial method exposed by Provider and Distributor
   137  // implementations. Defining the interface here makes it possible to use the
   138  // same helper from both provider and distributor tests.
   139  type kmReader interface {
   140  	KeyMaterial(context.Context) (*KeyMaterial, error)
   141  }
   142  
   143  // readAndVerifyKeyMaterial attempts to read key material from the given
   144  // provider and compares it against the expected key material.
   145  func readAndVerifyKeyMaterial(ctx context.Context, kmr kmReader, wantKM *KeyMaterial) error {
   146  	gotKM, err := kmr.KeyMaterial(ctx)
   147  	if err != nil {
   148  		return fmt.Errorf("KeyMaterial(ctx) failed: %w", err)
   149  	}
   150  	return compareKeyMaterial(gotKM, wantKM)
   151  }
   152  
   153  func compareKeyMaterial(got, want *KeyMaterial) error {
   154  	if len(got.Certs) != len(want.Certs) {
   155  		return fmt.Errorf("keyMaterial certs = %+v, want %+v", got, want)
   156  	}
   157  	for i := 0; i < len(got.Certs); i++ {
   158  		if !got.Certs[i].Leaf.Equal(want.Certs[i].Leaf) {
   159  			return fmt.Errorf("keyMaterial certs = %+v, want %+v", got, want)
   160  		}
   161  	}
   162  
   163  	if gotR, wantR := got.Roots, want.Roots; !gotR.Equal(wantR) {
   164  		return fmt.Errorf("keyMaterial roots = %v, want %v", gotR, wantR)
   165  	}
   166  
   167  	return nil
   168  }
   169  
   170  func createProvider(t *testing.T, name, config string, opts BuildOptions) Provider {
   171  	t.Helper()
   172  	prov, err := GetProvider(name, config, opts)
   173  	if err != nil {
   174  		t.Fatalf("GetProvider(%s, %s, %v) failed: %v", name, config, opts, err)
   175  	}
   176  	return prov
   177  }
   178  
   179  // TestStoreSingleProvider creates a single provider through the store and calls
   180  // methods on them.
   181  func (s) TestStoreSingleProvider(t *testing.T) {
   182  	prov := createProvider(t, fakeProvider1Name, fakeConfig, BuildOptions{CertName: "default"})
   183  	defer prov.Close()
   184  
   185  	// Our fakeProviderBuilder pushes newly created providers on a channel. Grab
   186  	// the fake provider from that channel.
   187  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   188  	defer cancel()
   189  	p, err := fpb1.providerChan.Receive(ctx)
   190  	if err != nil {
   191  		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
   192  	}
   193  	fakeProv := p.(*fakeProvider)
   194  
   195  	// Attempt to read from key material from the Provider returned by the
   196  	// store. This will fail because we have not pushed any key material into
   197  	// our fake provider.
   198  	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   199  	defer sCancel()
   200  	if err := readAndVerifyKeyMaterial(sCtx, prov, nil); !errors.Is(err, context.DeadlineExceeded) {
   201  		t.Fatal(err)
   202  	}
   203  
   204  	// Load key material from testdata directory, push it into out fakeProvider
   205  	// and attempt to read from the Provider returned by the store.
   206  	testKM1 := loadKeyMaterials(t, "x509/server1_cert.pem", "x509/server1_key.pem", "x509/client_ca_cert.pem")
   207  	fakeProv.newKeyMaterial(testKM1, nil)
   208  	if err := readAndVerifyKeyMaterial(ctx, prov, testKM1); err != nil {
   209  		t.Fatal(err)
   210  	}
   211  
   212  	// Push new key material and read from the Provider. This should returned
   213  	// updated key material.
   214  	testKM2 := loadKeyMaterials(t, "x509/server2_cert.pem", "x509/server2_key.pem", "x509/client_ca_cert.pem")
   215  	fakeProv.newKeyMaterial(testKM2, nil)
   216  	if err := readAndVerifyKeyMaterial(ctx, prov, testKM2); err != nil {
   217  		t.Fatal(err)
   218  	}
   219  }
   220  
   221  // TestStoreSingleProviderSameConfigDifferentOpts creates multiple providers of
   222  // same type, for same configs but different keyMaterial options through the
   223  // store (and expects the store's sharing mechanism to kick in) and calls
   224  // methods on them.
   225  func (s) TestStoreSingleProviderSameConfigDifferentOpts(t *testing.T) {
   226  	// Create three readers on the same fake provider. Two of these readers use
   227  	// certName `foo`, while the third one uses certName `bar`.
   228  	optsFoo := BuildOptions{CertName: "foo"}
   229  	provFoo1 := createProvider(t, fakeProvider1Name, fakeConfig, optsFoo)
   230  	provFoo2 := createProvider(t, fakeProvider1Name, fakeConfig, optsFoo)
   231  	defer func() {
   232  		provFoo1.Close()
   233  		provFoo2.Close()
   234  	}()
   235  
   236  	// Our fakeProviderBuilder pushes newly created providers on a channel.
   237  	// Grab the fake provider for optsFoo.
   238  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   239  	defer cancel()
   240  	p, err := fpb1.providerChan.Receive(ctx)
   241  	if err != nil {
   242  		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
   243  	}
   244  	fakeProvFoo := p.(*fakeProvider)
   245  
   246  	// Make sure only provider was created by the builder so far. The store
   247  	// should be able to share the providers.
   248  	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   249  	defer sCancel()
   250  	if _, err := fpb1.providerChan.Receive(sCtx); !errors.Is(err, context.DeadlineExceeded) {
   251  		t.Fatalf("A second provider created when expected to be shared by the store")
   252  	}
   253  
   254  	optsBar := BuildOptions{CertName: "bar"}
   255  	provBar1 := createProvider(t, fakeProvider1Name, fakeConfig, optsBar)
   256  	defer provBar1.Close()
   257  
   258  	// Grab the fake provider for optsBar.
   259  	p, err = fpb1.providerChan.Receive(ctx)
   260  	if err != nil {
   261  		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
   262  	}
   263  	fakeProvBar := p.(*fakeProvider)
   264  
   265  	// Push key material for optsFoo, and make sure the foo providers return
   266  	// appropriate key material and the bar provider times out.
   267  	fooKM := loadKeyMaterials(t, "x509/server1_cert.pem", "x509/server1_key.pem", "x509/client_ca_cert.pem")
   268  	fakeProvFoo.newKeyMaterial(fooKM, nil)
   269  	if err := readAndVerifyKeyMaterial(ctx, provFoo1, fooKM); err != nil {
   270  		t.Fatal(err)
   271  	}
   272  	if err := readAndVerifyKeyMaterial(ctx, provFoo2, fooKM); err != nil {
   273  		t.Fatal(err)
   274  	}
   275  	sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
   276  	defer sCancel()
   277  	if err := readAndVerifyKeyMaterial(sCtx, provBar1, nil); !errors.Is(err, context.DeadlineExceeded) {
   278  		t.Fatal(err)
   279  	}
   280  
   281  	// Push key material for optsBar, and make sure the bar provider returns
   282  	// appropriate key material.
   283  	barKM := loadKeyMaterials(t, "x509/server2_cert.pem", "x509/server2_key.pem", "x509/client_ca_cert.pem")
   284  	fakeProvBar.newKeyMaterial(barKM, nil)
   285  	if err := readAndVerifyKeyMaterial(ctx, provBar1, barKM); err != nil {
   286  		t.Fatal(err)
   287  	}
   288  
   289  	// Make sure the above push of new key material does not affect foo readers.
   290  	if err := readAndVerifyKeyMaterial(ctx, provFoo1, fooKM); err != nil {
   291  		t.Fatal(err)
   292  	}
   293  }
   294  
   295  // TestStoreSingleProviderDifferentConfigs creates multiple instances of the
   296  // same type of provider through the store with different configs. The store
   297  // would end up creating different provider instances for these and no sharing
   298  // would take place.
   299  func (s) TestStoreSingleProviderDifferentConfigs(t *testing.T) {
   300  	// Create two providers of the same type, but with different configs.
   301  	opts := BuildOptions{CertName: "foo"}
   302  	cfg1 := fakeConfig + "1111"
   303  	cfg2 := fakeConfig + "2222"
   304  
   305  	prov1 := createProvider(t, fakeProvider1Name, cfg1, opts)
   306  	defer prov1.Close()
   307  	// Our fakeProviderBuilder pushes newly created providers on a channel. Grab
   308  	// the fake provider from that channel.
   309  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   310  	defer cancel()
   311  	p1, err := fpb1.providerChan.Receive(ctx)
   312  	if err != nil {
   313  		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
   314  	}
   315  	fakeProv1 := p1.(*fakeProvider)
   316  
   317  	prov2 := createProvider(t, fakeProvider1Name, cfg2, opts)
   318  	defer prov2.Close()
   319  	// Grab the second provider from the channel.
   320  	p2, err := fpb1.providerChan.Receive(ctx)
   321  	if err != nil {
   322  		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
   323  	}
   324  	fakeProv2 := p2.(*fakeProvider)
   325  
   326  	// Push the same key material into both fake providers and verify that the
   327  	// providers returned by the store return the appropriate key material.
   328  	km1 := loadKeyMaterials(t, "x509/server1_cert.pem", "x509/server1_key.pem", "x509/client_ca_cert.pem")
   329  	fakeProv1.newKeyMaterial(km1, nil)
   330  	fakeProv2.newKeyMaterial(km1, nil)
   331  	if err := readAndVerifyKeyMaterial(ctx, prov1, km1); err != nil {
   332  		t.Fatal(err)
   333  	}
   334  	if err := readAndVerifyKeyMaterial(ctx, prov2, km1); err != nil {
   335  		t.Fatal(err)
   336  	}
   337  
   338  	// Push new key material into only one of the fake providers and verify
   339  	// that the providers returned by the store return the appropriate key
   340  	// material.
   341  	km2 := loadKeyMaterials(t, "x509/server2_cert.pem", "x509/server2_key.pem", "x509/client_ca_cert.pem")
   342  	fakeProv2.newKeyMaterial(km2, nil)
   343  	if err := readAndVerifyKeyMaterial(ctx, prov1, km1); err != nil {
   344  		t.Fatal(err)
   345  	}
   346  	if err := readAndVerifyKeyMaterial(ctx, prov2, km2); err != nil {
   347  		t.Fatal(err)
   348  	}
   349  
   350  	// Close one of the providers and verify that the other one is not affected.
   351  	prov1.Close()
   352  	if err := readAndVerifyKeyMaterial(ctx, prov2, km2); err != nil {
   353  		t.Fatal(err)
   354  	}
   355  }
   356  
   357  // TestStoreMultipleProviders creates providers of different types and makes
   358  // sure closing of one does not affect the other.
   359  func (s) TestStoreMultipleProviders(t *testing.T) {
   360  	opts := BuildOptions{CertName: "foo"}
   361  	prov1 := createProvider(t, fakeProvider1Name, fakeConfig, opts)
   362  	defer prov1.Close()
   363  	// Our fakeProviderBuilder pushes newly created providers on a channel. Grab
   364  	// the fake provider from that channel.
   365  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   366  	defer cancel()
   367  	p1, err := fpb1.providerChan.Receive(ctx)
   368  	if err != nil {
   369  		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
   370  	}
   371  	fakeProv1 := p1.(*fakeProvider)
   372  
   373  	prov2 := createProvider(t, fakeProvider2Name, fakeConfig, opts)
   374  	defer prov2.Close()
   375  	// Grab the second provider from the channel.
   376  	p2, err := fpb2.providerChan.Receive(ctx)
   377  	if err != nil {
   378  		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider2Name)
   379  	}
   380  	fakeProv2 := p2.(*fakeProvider)
   381  
   382  	// Push the key material into both providers and verify that the
   383  	// readers return the appropriate key material.
   384  	km1 := loadKeyMaterials(t, "x509/server1_cert.pem", "x509/server1_key.pem", "x509/client_ca_cert.pem")
   385  	fakeProv1.newKeyMaterial(km1, nil)
   386  	km2 := loadKeyMaterials(t, "x509/server2_cert.pem", "x509/server2_key.pem", "x509/client_ca_cert.pem")
   387  	fakeProv2.newKeyMaterial(km2, nil)
   388  	if err := readAndVerifyKeyMaterial(ctx, prov1, km1); err != nil {
   389  		t.Fatal(err)
   390  	}
   391  	if err := readAndVerifyKeyMaterial(ctx, prov2, km2); err != nil {
   392  		t.Fatal(err)
   393  	}
   394  
   395  	// Close one of the providers and verify that the other one is not affected.
   396  	prov1.Close()
   397  	if err := readAndVerifyKeyMaterial(ctx, prov2, km2); err != nil {
   398  		t.Fatal(err)
   399  	}
   400  }
   401  

View as plain text