Skip to content

Commit

Permalink
certprovider: API update to include certificate name.
Browse files Browse the repository at this point in the history
  • Loading branch information
easwars committed Aug 6, 2020
1 parent b2f0b79 commit bae0fb2
Show file tree
Hide file tree
Showing 5 changed files with 424 additions and 272 deletions.
40 changes: 24 additions & 16 deletions credentials/tls/certprovider/distributor.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,27 @@ import (
"google.golang.org/grpc/internal/grpcsync"
)

// Distributor makes it easy for provider implementations to furnish new key
// materials by handling synchronization between the producer and consumers of
// the key material.
// Distributor is a simple, single key materials cache which makes it easy for
// provider implementations to furnish new key materials by handling
// synchronization between the producer and consumers of the key material.
//
// Provider implementations which choose to use a Distributor should do the
// following:
// - create a new Distributor using the NewDistributor() function.
// - invoke the Set() method whenever they have new key material or errors to
// report.
// - delegate to the distributor when handing calls to KeyMaterial().
// - invoke the Stop() method when they are done using the distributor.
// Distributor implements the KeyMaterialReader interface. Provider
// implementations may choose to do the following:
// - return a distributor as part of their KeyMaterialReader() method.
// - invoke the distributor's Set() method whenever they have new key material.
// - watch the channel returned by the distributor's Done() method to get
// notified when the distributor is closed.
type Distributor struct {
// mu protects the underlying key material.
mu sync.Mutex
km *KeyMaterial
pErr error

ready *grpcsync.Event
// ready channel to unblock KeyMaterialReader() invocations blocked on
// availability of key material.
ready *grpcsync.Event
// done channel to notify provider implementations and unblock any
// KeyMaterialReader() calls, once the distributor is closed.
closed *grpcsync.Event
}

Expand All @@ -57,12 +60,12 @@ func NewDistributor() *Distributor {
// Set updates the key material in the distributor with km.
//
// Provider implementations which use the distributor must not modify the
// contents of the KeyMaterial struct pointed to by km.
// contents of the KeyMaterialReader struct pointed to by km.
//
// A non-nil err value indicates the error that the provider implementation ran
// into when trying to fetch key material, and makes it possible to surface the
// error to the user. A non-nil error value passed here causes distributor's
// KeyMaterial() method to return nil key material.
// KeyMaterialReader() method to return nil key material.
func (d *Distributor) Set(km *KeyMaterial, err error) {
d.mu.Lock()
d.km = km
Expand Down Expand Up @@ -103,8 +106,13 @@ func (d *Distributor) keyMaterial() (*KeyMaterial, error) {
return d.km, d.pErr
}

// Stop turns down the distributor, releases allocated resources and fails any
// active KeyMaterial() call waiting for new key material.
func (d *Distributor) Stop() {
// Close turns down the distributor, releases allocated resources and fails any
// active KeyMaterialReader() call waiting for new key material.
func (d *Distributor) Close() {
d.closed.Fire()
}

// Done returns a channel which will be closed when the distributor is closed.
func (d *Distributor) Done() <-chan struct{} {
return d.closed.Done()
}
21 changes: 9 additions & 12 deletions credentials/tls/certprovider/distributor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ func (s) TestDistributor(t *testing.T) {
dist := NewDistributor()

// Read cert/key files from testdata.
km, err := loadKeyMaterials()
if err != nil {
t.Fatal(err)
}
km := loadKeyMaterials(t, "x509/server1_cert.pem", "x509/server1_key.pem", "x509/client_ca_cert.pem")

// wantKM1 has both local and root certs.
wantKM1 := *km
// wantKM2 has only local certs. Roots are nil-ed out.
Expand All @@ -56,7 +54,7 @@ func (s) TestDistributor(t *testing.T) {
go func() {
defer wg.Done()

// The first call to KeyMaterial() should timeout because no key
// The first call to KeyMaterialReader() should timeout because no key
// material has been set on the distributor as yet.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout/2)
defer cancel()
Expand All @@ -66,7 +64,7 @@ func (s) TestDistributor(t *testing.T) {
}
proceedCh <- struct{}{}

// This call to KeyMaterial() should return the key material with both
// This call to KeyMaterialReader() should return the key material with both
// the local certs and the root certs.
ctx, cancel = context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
Expand All @@ -76,11 +74,11 @@ func (s) TestDistributor(t *testing.T) {
return
}
if !reflect.DeepEqual(gotKM, &wantKM1) {
errCh <- fmt.Errorf("provider.KeyMaterial() = %+v, want %+v", gotKM, wantKM1)
errCh <- fmt.Errorf("provider.KeyMaterialReader() = %+v, want %+v", gotKM, wantKM1)
}
proceedCh <- struct{}{}

// This call to KeyMaterial() should eventually return key material with
// This call to KeyMaterialReader() should eventually return key material with
// only the local certs.
ctx, cancel = context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
Expand All @@ -96,7 +94,7 @@ func (s) TestDistributor(t *testing.T) {
}
proceedCh <- struct{}{}

// This call to KeyMaterial() should return nil key material and a
// This call to KeyMaterialReader() should return nil key material and a
// non-nil error.
ctx, cancel = context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
Expand All @@ -114,7 +112,7 @@ func (s) TestDistributor(t *testing.T) {
}
proceedCh <- struct{}{}

// This call to KeyMaterial() should eventually return errProviderClosed
// This call to KeyMaterialReader() should eventually return errProviderClosed
// error.
ctx, cancel = context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
Expand All @@ -139,9 +137,8 @@ func (s) TestDistributor(t *testing.T) {
})

waitAndDo(t, proceedCh, errCh, func() {
dist.Stop()
dist.Close()
})

}

func waitAndDo(t *testing.T, proceedCh chan struct{}, errCh chan error, do func()) {
Expand Down
33 changes: 29 additions & 4 deletions credentials/tls/certprovider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (
)

var (
// errProviderClosed is returned by Distributor.KeyMaterial when it is
// errProviderClosed is returned by Distributor.KeyMaterialReader when it is
// closed.
errProviderClosed = errors.New("provider instance is closed")

Expand Down Expand Up @@ -87,18 +87,43 @@ type StableConfig interface {
// the latest secrets, and free to share any state between different
// instantiations as they deem fit.
type Provider interface {
// KeyMaterial returns the key material sourced by the provider.
// Callers are expected to use the returned value as read-only.
KeyMaterial(ctx context.Context) (*KeyMaterial, error)
// KeyMaterialReader returns a reader to read key material sourced by the
// provider. Callers are expected to call the Close() method on the returned
// reader when they are no longer interested in the underlying key material.
KeyMaterialReader(opts KeyMaterialOptions) (KeyMaterialReader, error)

// Close cleans up resources allocated by the provider.
Close()
}

// KeyMaterialReader is used to read key material at handshake time.
// Implementations must be thread-safe.
type KeyMaterialReader interface {
// Returns the key material when they are ready.
// Implementations must honor any deadline set in the context.
KeyMaterial(ctx context.Context) (*KeyMaterial, error)

// Close provides a way for callers to indicate that they are no longer
// interested in the underlying key material.
Close()
}

// KeyMaterial wraps the certificates and keys returned by a provider instance.
type KeyMaterial struct {
// Certs contains a slice of cert/key pairs used to prove local identity.
Certs []tls.Certificate
// Roots contains the set of trusted roots to validate the peer's identity.
Roots *x509.CertPool
}

// KeyMaterialOptions wraps options passed to the KeyMaterialReader() method.
type KeyMaterialOptions struct {
// CertName holds the certificate name, whose key material is of interest to
// the caller.
CertName string
// WantRoot indicates if the caller is interested in the root certificate.
WantRoot bool
// WantIdentity indicates if the caller is interested in the identity
// certificate.
WantIdentity bool
}
Loading

0 comments on commit bae0fb2

Please sign in to comment.