Commit fe8cfc17 authored by Vishal Nayak's avatar Vishal Nayak Committed by Brian Kassouf
Browse files

Abstract generate-root authentication into the strategy interface (#7698)

* 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.
parent 2f65be45
Showing with 83 additions and 67 deletions
+83 -67
......@@ -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()
......
......@@ -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"
......@@ -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 {
......@@ -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
......
......@@ -2,7 +2,7 @@ package vault
import (
"context"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/sdk/helper/base62"
"go.uber.org/atomic"
)
......@@ -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 {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment