Skip to content

Commit

Permalink
Abstract generate-root authentication into the strategy interface (#7698
Browse files Browse the repository at this point in the history
)

* Abstract generate-root authentication into the strategy interface

* Generate root strategy ncabatoff (#7700)

* Adapt to new shamir-as-kek reality.

* Don't try to verify the master key when we might still be sealed (in
recovery mode).  Instead, verify it in the authenticate methods.
  • Loading branch information
vishalnayak authored and briankassouf committed Oct 23, 2019
1 parent 2f65be4 commit fe8cfc1
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 67 deletions.
47 changes: 47 additions & 0 deletions vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -2041,6 +2041,53 @@ func (c *Core) SetSealsForMigration(migrationSeal, newSeal, unwrapSeal Seal) {
}
}

// unsealKeyToMasterKey takes a key provided by the user, either a recovery key
// if using an autoseal or an unseal key with Shamir. It returns a nil error
// if the key is valid and an error otherwise. It also returns the master key
// that can be used to unseal the barrier.
func (c *Core) unsealKeyToMasterKey(ctx context.Context, combinedKey []byte) ([]byte, error) {
switch c.seal.StoredKeysSupported() {
case StoredKeysSupportedGeneric:
if err := c.seal.VerifyRecoveryKey(ctx, combinedKey); err != nil {
return nil, errwrap.Wrapf("recovery key verification failed: {{err}}", err)
}

storedKeys, err := c.seal.GetStoredKeys(ctx)
if err == nil && len(storedKeys) != 1 {
err = fmt.Errorf("expected exactly one stored key, got %d", len(storedKeys))
}
if err != nil {
return nil, errwrap.Wrapf("unable to retrieve stored keys", err)
}
return storedKeys[0], nil

case StoredKeysSupportedShamirMaster:
testseal := NewDefaultSeal(shamirseal.NewSeal(c.logger.Named("testseal")))
testseal.SetCore(c)
cfg, err := c.seal.BarrierConfig(ctx)
if err != nil {
return nil, errwrap.Wrapf("failed to setup test barrier config: {{err}}", err)
}
testseal.SetCachedBarrierConfig(cfg)
err = testseal.GetAccess().(*shamirseal.ShamirSeal).SetKey(combinedKey)
if err != nil {
return nil, errwrap.Wrapf("failed to setup unseal key: {{err}}", err)
}
storedKeys, err := testseal.GetStoredKeys(ctx)
if err == nil && len(storedKeys) != 1 {
err = fmt.Errorf("expected exactly one stored key, got %d", len(storedKeys))
}
if err != nil {
return nil, errwrap.Wrapf("unable to retrieve stored keys", err)
}
return storedKeys[0], nil

case StoredKeysNotSupported:
return combinedKey, nil
}
return nil, fmt.Errorf("invalid seal")
}

func (c *Core) IsInSealMigration() bool {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
Expand Down
82 changes: 16 additions & 66 deletions vault/generate_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/hashicorp/vault/helper/xor"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/shamir"
shamirseal "github.com/hashicorp/vault/vault/seal/shamir"
)

const coreDROperationTokenPath = "core/dr-operation-token"
Expand All @@ -32,12 +31,25 @@ var (
// create a token upon completion of the generate root process.
type GenerateRootStrategy interface {
generate(context.Context, *Core) (string, func(), error)
authenticate(context.Context, *Core, []byte) error
}

// generateStandardRootToken implements the GenerateRootStrategy and is in
// charge of creating standard root tokens.
type generateStandardRootToken struct{}

func (g generateStandardRootToken) authenticate(ctx context.Context, c *Core, combinedKey []byte) error {
_, err := c.unsealKeyToMasterKey(ctx, combinedKey)
if err != nil {
return errwrap.Wrapf("unable to authenticate: {{err}}", err)
}
if err := c.barrier.VerifyMaster(combinedKey); err != nil {
return errwrap.Wrapf("master key verification failed: {{err}}", err)
}

return nil
}

func (g generateStandardRootToken) generate(ctx context.Context, c *Core) (string, func(), error) {
te, err := c.tokenStore.rootToken(ctx)
if err != nil {
Expand Down Expand Up @@ -296,71 +308,9 @@ func (c *Core) GenerateRootUpdate(ctx context.Context, key []byte, nonce string,
}
}

switch {
case c.seal.RecoveryKeySupported():
// Ensure that the combined recovery key is valid
if err := c.seal.VerifyRecoveryKey(ctx, combinedKey); err != nil {
c.logger.Error("root generation aborted, recovery key verification failed", "error", err)
return nil, err
}
// If we are in recovery mode, then retrieve
// the stored keys and unseal the barrier
if c.recoveryMode {
storedKeys, err := c.seal.GetStoredKeys(ctx)
if err != nil {
return nil, errwrap.Wrapf("unable to retrieve stored keys in recovery mode: {{err}}", err)
}

// Use the retrieved master key to unseal the barrier
if err := c.barrier.Unseal(ctx, storedKeys[0]); err != nil {
c.logger.Error("root generation aborted, recovery operation token verification failed", "error", err)
return nil, err
}
}
default:
masterKey := combinedKey
if c.seal.StoredKeysSupported() == StoredKeysSupportedShamirMaster {
testseal := NewDefaultSeal(shamirseal.NewSeal(c.logger.Named("testseal")))
testseal.SetCore(c)
cfg, err := c.seal.BarrierConfig(ctx)
if err != nil {
return nil, errwrap.Wrapf("failed to setup test barrier config: {{err}}", err)
}
testseal.SetCachedBarrierConfig(cfg)
err = testseal.GetAccess().(*shamirseal.ShamirSeal).SetKey(combinedKey)
if err != nil {
return nil, errwrap.Wrapf("failed to setup unseal key: {{err}}", err)
}
stored, err := testseal.GetStoredKeys(ctx)
if err != nil {
return nil, errwrap.Wrapf("failed to read master key: {{err}}", err)
}
masterKey = stored[0]
}
switch {
case c.recoveryMode:
// If we are in recovery mode, being able to unseal
// the barrier is how we establish authentication
if err := c.barrier.Unseal(ctx, masterKey); err != nil {
c.logger.Error("root generation aborted, recovery operation token verification failed", "error", err)
return nil, err
}
default:
if err := c.barrier.VerifyMaster(masterKey); err != nil {
c.logger.Error("root generation aborted, master key verification failed", "error", err)
return nil, err
}
}
}

// Authentication in recovery mode is successful
if c.recoveryMode {
// Run any post unseal functions that are set
for _, v := range c.postRecoveryUnsealFuncs {
if err := v(); err != nil {
return nil, errwrap.Wrapf("failed to run post unseal func: {{err}}", err)
}
}
if err := strategy.authenticate(ctx, c, combinedKey); err != nil {
c.logger.Error("root generation aborted", "error", err.Error())
return nil, errwrap.Wrapf("root generation aborted: {{err}}", err)
}

// Run the generate strategy
Expand Down
21 changes: 20 additions & 1 deletion vault/generate_root_recovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package vault

import (
"context"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/sdk/helper/base62"
"go.uber.org/atomic"
)
Expand All @@ -19,6 +19,25 @@ type generateRecoveryToken struct {
token *atomic.String
}

func (g *generateRecoveryToken) authenticate(ctx context.Context, c *Core, combinedKey []byte) error {
key, err := c.unsealKeyToMasterKey(ctx, combinedKey)
if err != nil {
return errwrap.Wrapf("unable to authenticate: {{err}}", err)
}

// Use the retrieved master key to unseal the barrier
if err := c.barrier.Unseal(ctx, key); err != nil {
return errwrap.Wrapf("recovery operation token generation failed, cannot unseal barrier: {{err}}", err)
}

for _, v := range c.postRecoveryUnsealFuncs {
if err := v(); err != nil {
return errwrap.Wrapf("failed to run post unseal func: {{err}}", err)
}
}
return nil
}

func (g *generateRecoveryToken) generate(ctx context.Context, c *Core) (string, func(), error) {
id, err := base62.Random(TokenLength)
if err != nil {
Expand Down

0 comments on commit fe8cfc1

Please sign in to comment.