1// +build go1.13 2 3/* 4 * 5 * Copyright 2020 gRPC authors. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ 20 21package certprovider 22 23import ( 24 "context" 25 "crypto/tls" 26 "crypto/x509" 27 "errors" 28 "fmt" 29 "io/ioutil" 30 "reflect" 31 "testing" 32 "time" 33 34 "google.golang.org/grpc/internal/grpctest" 35 "google.golang.org/grpc/internal/testutils" 36 "google.golang.org/grpc/testdata" 37) 38 39const ( 40 fakeProvider1Name = "fake-certificate-provider-1" 41 fakeProvider2Name = "fake-certificate-provider-2" 42 fakeConfig = "my fake config" 43 defaultTestTimeout = 5 * time.Second 44 defaultTestShortTimeout = 10 * time.Millisecond 45) 46 47var fpb1, fpb2 *fakeProviderBuilder 48 49func init() { 50 fpb1 = &fakeProviderBuilder{ 51 name: fakeProvider1Name, 52 providerChan: testutils.NewChannel(), 53 } 54 fpb2 = &fakeProviderBuilder{ 55 name: fakeProvider2Name, 56 providerChan: testutils.NewChannel(), 57 } 58 Register(fpb1) 59 Register(fpb2) 60} 61 62type s struct { 63 grpctest.Tester 64} 65 66func Test(t *testing.T) { 67 grpctest.RunSubTests(t, s{}) 68} 69 70// fakeProviderBuilder builds new instances of fakeProvider and interprets the 71// config provided to it as a string. 72type fakeProviderBuilder struct { 73 name string 74 providerChan *testutils.Channel 75} 76 77func (b *fakeProviderBuilder) ParseConfig(config interface{}) (*BuildableConfig, error) { 78 s, ok := config.(string) 79 if !ok { 80 return nil, fmt.Errorf("providerBuilder %s received config of type %T, want string", b.name, config) 81 } 82 return NewBuildableConfig(b.name, []byte(s), func(BuildOptions) Provider { 83 fp := &fakeProvider{ 84 Distributor: NewDistributor(), 85 config: s, 86 } 87 b.providerChan.Send(fp) 88 return fp 89 }), nil 90} 91 92func (b *fakeProviderBuilder) Name() string { 93 return b.name 94} 95 96// fakeProvider is an implementation of the Provider interface which provides a 97// method for tests to invoke to push new key materials. 98type fakeProvider struct { 99 *Distributor 100 config string 101} 102 103func (p *fakeProvider) Start(BuildOptions) Provider { 104 // This is practically a no-op since this provider doesn't do any work which 105 // needs to be started at this point. 106 return p 107} 108 109// newKeyMaterial allows tests to push new key material to the fake provider 110// which will be made available to users of this provider. 111func (p *fakeProvider) newKeyMaterial(km *KeyMaterial, err error) { 112 p.Distributor.Set(km, err) 113} 114 115// Close helps implement the Provider interface. 116func (p *fakeProvider) Close() { 117 p.Distributor.Stop() 118} 119 120// loadKeyMaterials is a helper to read cert/key files from testdata and convert 121// them into a KeyMaterialReader struct. 122func loadKeyMaterials(t *testing.T, cert, key, ca string) *KeyMaterial { 123 t.Helper() 124 125 certs, err := tls.LoadX509KeyPair(testdata.Path(cert), testdata.Path(key)) 126 if err != nil { 127 t.Fatalf("Failed to load keyPair: %v", err) 128 } 129 130 pemData, err := ioutil.ReadFile(testdata.Path(ca)) 131 if err != nil { 132 t.Fatal(err) 133 } 134 roots := x509.NewCertPool() 135 roots.AppendCertsFromPEM(pemData) 136 return &KeyMaterial{Certs: []tls.Certificate{certs}, Roots: roots} 137} 138 139// kmReader wraps the KeyMaterial method exposed by Provider and Distributor 140// implementations. Defining the interface here makes it possible to use the 141// same helper from both provider and distributor tests. 142type kmReader interface { 143 KeyMaterial(context.Context) (*KeyMaterial, error) 144} 145 146// readAndVerifyKeyMaterial attempts to read key material from the given 147// provider and compares it against the expected key material. 148func readAndVerifyKeyMaterial(ctx context.Context, kmr kmReader, wantKM *KeyMaterial) error { 149 gotKM, err := kmr.KeyMaterial(ctx) 150 if err != nil { 151 return fmt.Errorf("KeyMaterial(ctx) failed: %w", err) 152 } 153 return compareKeyMaterial(gotKM, wantKM) 154} 155 156func compareKeyMaterial(got, want *KeyMaterial) error { 157 // TODO(easwars): Remove all references to reflect.DeepEqual and use 158 // cmp.Equal instead. Currently, the later panics because x509.Certificate 159 // type defines an Equal method, but does not check for nil. This has been 160 // fixed in 161 // https://github.com/golang/go/commit/89865f8ba64ccb27f439cce6daaa37c9aa38f351, 162 // but this is only available starting go1.14. So, once we remove support 163 // for go1.13, we can make the switch. 164 if !reflect.DeepEqual(got, want) { 165 return fmt.Errorf("provider.KeyMaterial() = %+v, want %+v", got, want) 166 } 167 return nil 168} 169 170func 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. 181func (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. 225func (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. 299func (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 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. 359func (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