github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/credentials/tls/certprovider/store_test.go (about)

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