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 }