Skip to content

Commit

Permalink
core: re-encrypt barrier and recovery keys if the unseal key is updated
Browse files Browse the repository at this point in the history
Seal keys can be rotated. When this happens, the barrier and recovery
keys should be re-encrypted with the new seal key. This change
automatically re-encrypts the barrier and recovery keys with the latest
seal key on the active node during the 'postUnseal' phase.
  • Loading branch information
mgaffney committed Sep 25, 2019
1 parent 1aa86aa commit 0b11c5e
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 0 deletions.
6 changes: 6 additions & 0 deletions vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -1692,6 +1692,12 @@ func (c *Core) postUnseal(ctx context.Context, ctxCancelFunc context.CancelFunc,
return err
}

if seal, ok := c.seal.(*autoSeal); ok {
if err := seal.UpgradeKeys(c.activeContext); err != nil {
return err
}
}

c.metricsCh = make(chan struct{})
go c.emitMetrics(c.metricsCh)

Expand Down
92 changes: 92 additions & 0 deletions vault/seal_autoseal.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,69 @@ func (d *autoSeal) GetStoredKeys(ctx context.Context) ([][]byte, error) {
return keys, nil
}

func (d *autoSeal) upgradeStoredKeys(ctx context.Context) error {
pe, err := d.core.physical.Get(ctx, StoredBarrierKeysPath)
if err != nil {
return errwrap.Wrapf("failed to fetch stored keys: {{err}}", err)
}

// This is not strictly an error; we may not have any stored keys, for
// instance, if we're not initialized
if pe == nil {
return nil
}

blobInfo := &physical.EncryptedBlobInfo{}
if err := proto.Unmarshal(pe.Value, blobInfo); err != nil {
return errwrap.Wrapf("failed to proto decode stored keys: {{err}}", err)
}

if blobInfo.KeyInfo != nil && blobInfo.KeyInfo.KeyID != d.Access.KeyID() {
d.core.logger.Info("autoseal: upgrading stored keys")

pt, err := d.Decrypt(ctx, blobInfo)
if err != nil {
return errwrap.Wrapf("failed to decrypt encrypted stored keys: {{err}}", err)
}

// Decode the barrier entry
var keys [][]byte
if err := json.Unmarshal(pt, &keys); err != nil {
return errwrap.Wrapf("failed to decode stored keys: {{err}}", err)
}

if err := d.SetStoredKeys(ctx, keys); err != nil {
return errwrap.Wrapf("failed to save upgraded stored keys: {{err}}", err)
}
}
return nil
}

// UpgradeKeys re-encrypts and saves the stored keys and the recovery key
// with the current key if the current KeyID is different from the KeyID
// the stored keys and the recovery key are encrypted with. The provided
// Context must be non-nil.
func (d *autoSeal) UpgradeKeys(ctx context.Context) error {
// Exit if the context has been canceled
if ctx.Err() != nil {
return ctx.Err()
}

// Many of the seals update their keys to the latest KeyID when Encrypt
// is called.
if _, err := d.Encrypt(ctx, []byte("a")); err != nil {
return err
}

if err := d.upgradeRecoveryKey(ctx); err != nil {
return err
}
if err := d.upgradeStoredKeys(ctx); err != nil {
return err
}
return nil
}

func (d *autoSeal) BarrierConfig(ctx context.Context) (*SealConfig, error) {
if d.barrierConfig.Load().(*SealConfig) != nil {
return d.barrierConfig.Load().(*SealConfig).Clone(), nil
Expand Down Expand Up @@ -430,6 +493,35 @@ func (d *autoSeal) getRecoveryKeyInternal(ctx context.Context) ([]byte, error) {
return pt, nil
}

func (d *autoSeal) upgradeRecoveryKey(ctx context.Context) error {
pe, err := d.core.physical.Get(ctx, recoveryKeyPath)
if err != nil {
return errwrap.Wrapf("failed to fetch recovery key: {{err}}", err)
}
if pe == nil {
return fmt.Errorf("no recovery key found")
}

blobInfo := &physical.EncryptedBlobInfo{}
if err := proto.Unmarshal(pe.Value, blobInfo); err != nil {
return errwrap.Wrapf("failed to proto decode recovery key: {{err}}", err)
}

if blobInfo.KeyInfo != nil && blobInfo.KeyInfo.KeyID != d.Access.KeyID() {
d.core.logger.Info("autoseal: upgrading recovery key")

pt, err := d.Decrypt(ctx, blobInfo)
if err != nil {
return errwrap.Wrapf("failed to decrypt encrypted recovery key: {{err}}", err)

}
if err := d.SetRecoveryKey(ctx, pt); err != nil {
return errwrap.Wrapf("failed to save upgraded recovery key: {{err}}", err)
}
}
return nil
}

// migrateRecoveryConfig is a helper func to migrate the recovery config to
// live outside the barrier. This is called from SetRecoveryConfig which is
// always called with the stateLock.
Expand Down

0 comments on commit 0b11c5e

Please sign in to comment.