Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shamir seals now come in two varieties: legacy and new-style. #7694

Merged
merged 6 commits into from
Oct 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion command/operator_generate_root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) {
keys[len(keys)-1], // the last unseal key
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
t.Fatalf("expected %d to be %d, out=%q, err=%q", code, exp, ui.OutputWriter, ui.ErrorWriter)
}

reToken := regexp.MustCompile(`Encoded Token\s+(.+)`)
Expand Down
2 changes: 1 addition & 1 deletion command/operator_init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ func TestOperatorInitCommand_Run(t *testing.T) {
"-root-token-pgp-key", pubFiles[0],
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String())
t.Fatalf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String())
}

re := regexp.MustCompile(`Unseal Key \d+: (.+)`)
Expand Down
28 changes: 14 additions & 14 deletions command/seal_migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
)

func TestSealMigration(t *testing.T) {
logger := logging.NewVaultLogger(hclog.Trace)
logger := logging.NewVaultLogger(hclog.Trace).Named(t.Name())
phys, err := physInmem.NewInmem(nil, logger)
if err != nil {
t.Fatal(err)
Expand All @@ -47,8 +47,8 @@ func TestSealMigration(t *testing.T) {
var keys []string
var rootToken string

// First: start up as normal with shamir seal, init it
{
logger.Info("integ: start up as normal with shamir seal, init it")
cluster := vault.NewTestCluster(t, coreConfig, clusterConfig)
cluster.Start()
defer cluster.Cleanup()
Expand All @@ -73,9 +73,9 @@ func TestSealMigration(t *testing.T) {
cluster.Cores = nil
}

// Second: start up as normal with shamir seal and unseal, make sure
// everything is normal
{
logger.SetLevel(hclog.Trace)
logger.Info("integ: start up as normal with shamir seal and unseal, make sure everything is normal")
cluster := vault.NewTestCluster(t, coreConfig, clusterConfig)
cluster.Start()
defer cluster.Cleanup()
Expand Down Expand Up @@ -103,8 +103,9 @@ func TestSealMigration(t *testing.T) {

var autoSeal vault.Seal

// Third: create an autoseal and activate migration
{
logger.SetLevel(hclog.Trace)
logger.Info("integ: creating an autoseal and activating migration")
cluster := vault.NewTestCluster(t, coreConfig, clusterConfig)
cluster.Start()
defer cluster.Cleanup()
Expand Down Expand Up @@ -147,8 +148,9 @@ func TestSealMigration(t *testing.T) {
cluster.Cores = nil
}

// Fourth: verify autoseal and recovery key usage
{
logger.SetLevel(hclog.Trace)
logger.Info("integ: verify autoseal and recovery key usage")
coreConfig.Seal = autoSeal
cluster := vault.NewTestCluster(t, coreConfig, clusterConfig)
cluster.Start()
Expand Down Expand Up @@ -202,8 +204,9 @@ func TestSealMigration(t *testing.T) {
altTestSeal.Type = "test-alternate"
altSeal := vault.NewAutoSeal(altTestSeal)

// Fifth: migrate from auto-seal to auto-seal
{
logger.SetLevel(hclog.Trace)
logger.Info("integ: migrate from auto-seal to auto-seal")
coreConfig.Seal = autoSeal
cluster := vault.NewTestCluster(t, coreConfig, clusterConfig)
cluster.Start()
Expand Down Expand Up @@ -239,9 +242,9 @@ func TestSealMigration(t *testing.T) {
cluster.Cores = nil
}

// Sixth: create an Shamir seal and activate migration. Verify it doesn't work
// if disabled isn't set.
{
logger.SetLevel(hclog.Trace)
logger.Info("integ: create a Shamir seal and activate migration; verify it doesn't work if disabled isn't set.")
coreConfig.Seal = altSeal
cluster := vault.NewTestCluster(t, coreConfig, clusterConfig)
cluster.Start()
Expand Down Expand Up @@ -282,12 +285,9 @@ func TestSealMigration(t *testing.T) {
cluster.Cores = nil
}

if entry, err := phys.Get(ctx, vault.StoredBarrierKeysPath); err != nil || entry != nil {
t.Fatalf("expected nil error and nil entry, got error %#v and entry %#v", err, entry)
}

// Seventh: verify autoseal is off and the expected key shares work
{
logger.SetLevel(hclog.Trace)
logger.Info("integ: verify autoseal is off and the expected key shares work")
coreConfig.Seal = shamirSeal
cluster := vault.NewTestCluster(t, coreConfig, clusterConfig)
cluster.Start()
Expand Down
4 changes: 2 additions & 2 deletions command/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1822,7 +1822,7 @@ func (c *ServerCommand) enableDev(core *vault.Core, coreConfig *vault.CoreConfig
}
}

if core.SealAccess().StoredKeysSupported() {
if core.SealAccess().StoredKeysSupported() != vault.StoredKeysNotSupported {
barrierConfig.StoredShares = 1
}

Expand All @@ -1836,7 +1836,7 @@ func (c *ServerCommand) enableDev(core *vault.Core, coreConfig *vault.CoreConfig
}

// Handle unseal with stored keys
if core.SealAccess().StoredKeysSupported() {
if core.SealAccess().StoredKeysSupported() == vault.StoredKeysSupportedGeneric {
err := core.UnsealWithStoredKeys(ctx)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion command/server_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func adjustCoreForSealMigration(logger log.Logger, core *vault.Core, barrierSeal
return errors.New("Migrating from autoseal to Shamir seal is not currently supported on Vault Enterprise")
}

// If we're not cominng from Shamir we expect the previous seal to be
// If we're not coming from Shamir we expect the previous seal to be
// in the config and disabled.
existSeal = unwrapSeal
newSeal = barrierSeal
Expand Down
68 changes: 51 additions & 17 deletions helper/testhelpers/testhelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,32 @@ func EnsureCoreSealed(t testing.T, core *vault.TestClusterCore) {

func EnsureCoresUnsealed(t testing.T, c *vault.TestCluster) {
t.Helper()
for _, core := range c.Cores {
EnsureCoreUnsealed(t, c, core)
for i, core := range c.Cores {
err := AttemptUnsealCore(c, core)
if err != nil {
t.Fatalf("failed to unseal core %d: %v", i, err)
}
}
}

func AttemptUnsealCores(c *vault.TestCluster) error {
for i, core := range c.Cores {
err := AttemptUnsealCore(c, core)
if err != nil {
return fmt.Errorf("failed to unseal core %d: %v", i, err)
}
}
return nil
}
func EnsureCoreUnsealed(t testing.T, c *vault.TestCluster, core *vault.TestClusterCore) {

func AttemptUnsealCore(c *vault.TestCluster, core *vault.TestClusterCore) error {
if !core.Sealed() {
return
return nil
}

core.SealAccess().ClearCaches(context.Background())
if err := core.UnsealWithStoredKeys(context.Background()); err != nil {
t.Fatal(err)
return err
}

client := core.Client
Expand All @@ -153,20 +167,22 @@ func EnsureCoreUnsealed(t testing.T, c *vault.TestCluster, core *vault.TestClust
// Sometimes when we get here it's already unsealed on its own
// and then this fails for DR secondaries so check again
if core.Sealed() {
t.Fatal(err)
return err
} else {
return nil
}
break
}
if statusResp == nil {
t.Fatal("nil status response during unseal")
return fmt.Errorf("nil status response during unseal")
}
if !statusResp.Sealed {
break
}
}
if core.Sealed() {
t.Fatal("core is still sealed")
return fmt.Errorf("core is still sealed")
}
return nil
}

func EnsureStableActiveNode(t testing.T, cluster *vault.TestCluster) {
Expand Down Expand Up @@ -270,10 +286,16 @@ func WaitForActiveNode(t testing.T, cluster *vault.TestCluster) *vault.TestClust
return nil
}

func RekeyCluster(t testing.T, cluster *vault.TestCluster) {
func RekeyCluster(t testing.T, cluster *vault.TestCluster, recovery bool) [][]byte {
t.Helper()
cluster.Logger.Info("rekeying cluster", "recovery", recovery)
client := cluster.Cores[0].Client

init, err := client.Sys().RekeyInit(&api.RekeyInitRequest{
initFunc := client.Sys().RekeyInit
if recovery {
initFunc = client.Sys().RekeyRecoveryKeyInit
}
init, err := initFunc(&api.RekeyInitRequest{
SecretShares: 5,
SecretThreshold: 3,
})
Expand All @@ -282,8 +304,17 @@ func RekeyCluster(t testing.T, cluster *vault.TestCluster) {
}

var statusResp *api.RekeyUpdateResponse
for j := 0; j < len(cluster.BarrierKeys); j++ {
statusResp, err = client.Sys().RekeyUpdate(base64.StdEncoding.EncodeToString(cluster.BarrierKeys[j]), init.Nonce)
var keys = cluster.BarrierKeys
if cluster.Cores[0].Core.SealAccess().RecoveryKeySupported() {
keys = cluster.RecoveryKeys
}

updateFunc := client.Sys().RekeyUpdate
if recovery {
updateFunc = client.Sys().RekeyRecoveryKeyUpdate
}
for j := 0; j < len(keys); j++ {
statusResp, err = updateFunc(base64.StdEncoding.EncodeToString(keys[j]), init.Nonce)
if err != nil {
t.Fatal(err)
}
Expand All @@ -294,20 +325,23 @@ func RekeyCluster(t testing.T, cluster *vault.TestCluster) {
break
}
}
cluster.Logger.Info("cluster rekeyed", "recovery", recovery)

if cluster.Cores[0].Core.SealAccess().RecoveryKeySupported() && !recovery {
return nil
}
if len(statusResp.KeysB64) != 5 {
t.Fatal("wrong number of keys")
}

newBarrierKeys := make([][]byte, 5)
newKeys := make([][]byte, 5)
for i, key := range statusResp.KeysB64 {
newBarrierKeys[i], err = base64.StdEncoding.DecodeString(key)
newKeys[i], err = base64.StdEncoding.DecodeString(key)
if err != nil {
t.Fatal(err)
}
}

cluster.BarrierKeys = newBarrierKeys
return newKeys
}

type TestRaftServerAddressProvider struct {
Expand Down
37 changes: 0 additions & 37 deletions http/sys_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"encoding/base64"
"encoding/hex"
"fmt"
"net/http"

"github.com/hashicorp/vault/vault"
Expand Down Expand Up @@ -59,42 +58,6 @@ func handleSysInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request)
PGPKeys: req.RecoveryPGPKeys,
}

// N.B. Although the core is capable of handling situations where some keys
// are stored and some aren't, in practice, replication + HSMs makes this
// extremely hard to reason about, to the point that it will probably never
// be supported. The reason is that each HSM needs to encode the master key
// separately, which means the shares must be generated independently,
// which means both that the shares will be different *AND* there would
// need to be a way to actually allow fetching of the generated keys by
// operators.
if core.SealAccess().StoredKeysSupported() {
if len(barrierConfig.PGPKeys) > 0 {
respondError(w, http.StatusBadRequest, fmt.Errorf("PGP keys not supported when storing shares"))
return
}
barrierConfig.SecretShares = 1
barrierConfig.SecretThreshold = 1
barrierConfig.StoredShares = 1
core.Logger().Warn("stored keys supported on init, forcing shares/threshold to 1")
} else {
if barrierConfig.StoredShares > 0 {
respondError(w, http.StatusBadRequest, fmt.Errorf("stored keys are not supported by the current seal type"))
return
}
}

if len(barrierConfig.PGPKeys) > 0 && len(barrierConfig.PGPKeys) != barrierConfig.SecretShares {
respondError(w, http.StatusBadRequest, fmt.Errorf("incorrect number of PGP keys"))
return
}

if core.SealAccess().RecoveryKeySupported() {
if len(recoveryConfig.PGPKeys) > 0 && len(recoveryConfig.PGPKeys) != recoveryConfig.SecretShares {
respondError(w, http.StatusBadRequest, fmt.Errorf("incorrect number of PGP keys for recovery"))
return
}
}

initParams := &vault.InitParams{
BarrierConfig: barrierConfig,
RecoveryConfig: recoveryConfig,
Expand Down
12 changes: 10 additions & 2 deletions vault/barrier.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ const (
// keyring to discover the new master key. The new master key is then
// used to reload the keyring itself.
masterKeyPath = "core/master"

// shamirKekPath is used with Shamir in v1.3+ to store a copy of the
// unseal key behind the barrier. As with masterKeyPath this is primarily
// used by standbys to handle rekeys. It also comes into play when restoring
// raft snapshots.
shamirKekPath = "core/shamir-kek"
)

// SecurityBarrier is a critical component of Vault. It is used to wrap
Expand All @@ -69,8 +75,10 @@ type SecurityBarrier interface {
Initialized(ctx context.Context) (bool, error)

// Initialize works only if the barrier has not been initialized
// and makes use of the given master key.
Initialize(context.Context, []byte, io.Reader) error
// and makes use of the given master key. When sealKey is provided
// it's because we're using a new-style Shamir seal, and masterKey
// is to be stored using sealKey to encrypt it.
Initialize(ctx context.Context, masterKey []byte, sealKey []byte, random io.Reader) error

// GenerateKey is used to generate a new key
GenerateKey(io.Reader) ([]byte, error)
Expand Down
29 changes: 27 additions & 2 deletions vault/barrier_aes_gcm.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (b *AESGCMBarrier) Initialized(ctx context.Context) (bool, error) {

// Initialize works only if the barrier has not been initialized
// and makes use of the given master key.
func (b *AESGCMBarrier) Initialize(ctx context.Context, key []byte, reader io.Reader) error {
func (b *AESGCMBarrier) Initialize(ctx context.Context, key, sealKey []byte, reader io.Reader) error {
// Verify the key size
min, max := b.KeyLength()
if len(key) < min || len(key) > max {
Expand Down Expand Up @@ -146,7 +146,28 @@ func (b *AESGCMBarrier) Initialize(ctx context.Context, key []byte, reader io.Re
if err != nil {
return errwrap.Wrapf("failed to create keyring: {{err}}", err)
}
return b.persistKeyring(ctx, keyring)

err = b.persistKeyring(ctx, keyring)
if err != nil {
return err
}

if len(sealKey) > 0 {
primary, err := b.aeadFromKey(encrypt)
if err != nil {
return err
}

err = b.putInternal(ctx, 1, primary, &logical.StorageEntry{
Key: shamirKekPath,
Value: sealKey,
})
if err != nil {
return errwrap.Wrapf("failed to store new seal key: {{err}}", err)
}
}

return nil
}

// persistKeyring is used to write out the keyring using the
Expand Down Expand Up @@ -720,6 +741,10 @@ func (b *AESGCMBarrier) Put(ctx context.Context, entry *logical.StorageEntry) er
return err
}

return b.putInternal(ctx, term, primary, entry)
}

func (b *AESGCMBarrier) putInternal(ctx context.Context, term uint32, primary cipher.AEAD, entry *logical.StorageEntry) error {
value, err := b.encrypt(entry.Key, term, primary, entry.Value)
if err != nil {
return err
Expand Down
Loading