From 98d09b0dc6f5fc8b0975bd10bcd383006bc0b2a0 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 25 Apr 2016 19:39:04 +0000 Subject: [PATCH] Add seal tests and update generate-root and others to handle dualseal. --- command/init.go | 8 +- http/sys_init.go | 25 +++++ http/sys_rekey.go | 11 +++ vault/generate_root_test.go | 94 ++++++++++++------ vault/init.go | 32 ++---- vault/init_test.go | 74 +++++++++++--- vault/rekey.go | 7 -- vault/rekey_test.go | 190 +++++++++++++++++++++++------------- vault/seal_test.go | 128 ++++++++++++++++++++++++ vault/testing.go | 61 +++++++++++- vault/token_store.go | 4 +- 11 files changed, 481 insertions(+), 153 deletions(-) create mode 100644 vault/seal_test.go diff --git a/command/init.go b/command/init.go index 51e39e181..1f4cc6b2e 100644 --- a/command/init.go +++ b/command/init.go @@ -24,8 +24,8 @@ func (c *InitCommand) Run(args []string) int { flags.IntVar(&threshold, "key-threshold", 3, "") flags.IntVar(&storedShares, "stored-shares", 0, "") flags.Var(&pgpKeys, "pgp-keys", "") - flags.IntVar(&recoveryShares, "recovery-shares", 0, "") - flags.IntVar(&recoveryThreshold, "recovery-threshold", 0, "") + flags.IntVar(&recoveryShares, "recovery-shares", 5, "") + flags.IntVar(&recoveryThreshold, "recovery-threshold", 3, "") flags.Var(&recoveryPgpKeys, "recovery-pgp-keys", "") flags.BoolVar(&check, "check", false, "") if err := flags.Parse(args); err != nil { @@ -161,10 +161,10 @@ Init Options: 'vault unseal' command, you will need to hex decode and decrypt; this will be the plaintext unseal key. - -recovery-shares=0 The number of key shares to split the recovery key + -recovery-shares=5 The number of key shares to split the recovery key into. This is not normally available. - -recovery-threshold=0 The number of key shares required to reconstruct + -recovery-threshold=3 The number of key shares required to reconstruct the recovery key. This is not normally available. -recovery-pgp-keys If provided, behaves like "pgp-keys" but for the diff --git a/http/sys_init.go b/http/sys_init.go index f902a9ccf..6d99719bb 100644 --- a/http/sys_init.go +++ b/http/sys_init.go @@ -2,6 +2,7 @@ package http import ( "encoding/hex" + "fmt" "net/http" "github.com/hashicorp/errwrap" @@ -55,6 +56,30 @@ func handleSysInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request) PGPKeys: req.RecoveryPGPKeys, } + if core.SealAccess().StoredKeysSupported() { + if barrierConfig.SecretShares != 1 { + respondError(w, http.StatusBadRequest, fmt.Errorf("secret shares must be 1")) + return + } + if barrierConfig.SecretThreshold != barrierConfig.SecretShares { + respondError(w, http.StatusBadRequest, fmt.Errorf("secret threshold must be same as secret shares")) + return + } + if barrierConfig.StoredShares != barrierConfig.SecretShares { + respondError(w, http.StatusBadRequest, fmt.Errorf("stored shares must be same as secret shares")) + return + } + if barrierConfig.PGPKeys != nil && len(barrierConfig.PGPKeys) > 0 { + respondError(w, http.StatusBadRequest, fmt.Errorf("PGP keys not supported when storing shares")) + return + } + } else { + if barrierConfig.StoredShares > 0 { + respondError(w, http.StatusBadRequest, fmt.Errorf("stored keys are not supported")) + return + } + } + result, initErr := core.Initialize(barrierConfig, recoveryConfig) if initErr != nil { if !errwrap.ContainsType(initErr, new(vault.NonFatalError)) { diff --git a/http/sys_rekey.go b/http/sys_rekey.go index 918e38d1e..d813e1b5d 100644 --- a/http/sys_rekey.go +++ b/http/sys_rekey.go @@ -56,6 +56,7 @@ func handleSysRekeyInitGet(core *vault.Core, recovery bool, w http.ResponseWrite sealThreshold, err := core.RekeyThreshold(recovery) if err != nil { respondError(w, http.StatusInternalServerError, err) + return } // Format the status @@ -75,6 +76,7 @@ func handleSysRekeyInitGet(core *vault.Core, recovery bool, w http.ResponseWrite pgpFingerprints, err := pgpkeys.GetFingerprints(rekeyConf.PGPKeys, nil) if err != nil { respondError(w, http.StatusInternalServerError, err) + return } status.PGPFingerprints = pgpFingerprints status.Backup = rekeyConf.Backup @@ -93,6 +95,15 @@ func handleSysRekeyInitPut(core *vault.Core, recovery bool, w http.ResponseWrite if req.Backup && len(req.PGPKeys) == 0 { respondError(w, http.StatusBadRequest, fmt.Errorf("cannot request a backup of the new keys without providing PGP keys for encryption")) + return + } + + // Right now we don't support this, but the rest of the code is ready for + // when we do, hence the check below for this to be false if + // StoredShares is greater than zero + if core.SealAccess().StoredKeysSupported() { + respondError(w, http.StatusBadRequest, fmt.Errorf("rekeying of barrier not supported when stored key support is available")) + return } // Initialize the rekey diff --git a/vault/generate_root_test.go b/vault/generate_root_test.go index 17664e212..932824c79 100644 --- a/vault/generate_root_test.go +++ b/vault/generate_root_test.go @@ -11,9 +11,16 @@ import ( func TestCore_GenerateRoot_Lifecycle(t *testing.T) { c, master, _ := TestCoreUnsealed(t) + testCore_GenerateRoot_Lifecycle_Common(t, c, [][]byte{master}) + bc, rc := TestSealDefConfigs() + c, _, recoveryKeys, _ := TestCoreUnsealedWithConfigs(t, bc, rc) + testCore_GenerateRoot_Lifecycle_Common(t, c, recoveryKeys) +} + +func testCore_GenerateRoot_Lifecycle_Common(t *testing.T, c *Core, keys [][]byte) { // Verify update not allowed - if _, err := c.GenerateRootUpdate(master, ""); err == nil { + if _, err := c.GenerateRootUpdate(keys[0], ""); err == nil { t.Fatalf("no root generation in progress") } @@ -76,7 +83,14 @@ func TestCore_GenerateRoot_Lifecycle(t *testing.T) { func TestCore_GenerateRoot_Init(t *testing.T) { c, _, _ := TestCoreUnsealed(t) + testCore_GenerateRoot_Init_Common(t, c) + bc, rc := TestSealDefConfigs() + c, _, _, _ = TestCoreUnsealedWithConfigs(t, bc, rc) + testCore_GenerateRoot_Init_Common(t, c) +} + +func testCore_GenerateRoot_Init_Common(t *testing.T, c *Core) { otpBytes, err := GenerateRandBytes(16) if err != nil { t.Fatal(err) @@ -94,9 +108,26 @@ func TestCore_GenerateRoot_Init(t *testing.T) { } } -func TestCore_GenerateRoot_InvalidMaster(t *testing.T) { +func TestCore_GenerateRoot_InvalidMasterNonce(t *testing.T) { c, master, _ := TestCoreUnsealed(t) + // Make the master invalid + master[0]++ + testCore_GenerateRoot_InvalidMasterNonce_Common(t, c, [][]byte{master}) + bc, rc := TestSealDefConfigs() + // For ease of use let's make the threshold the same as the shares and also + // no stored shares so we get an error after the full set + bc.StoredShares = 0 + bc.SecretShares = 5 + bc.SecretThreshold = 5 + rc.SecretShares = 5 + rc.SecretThreshold = 5 + // In this case, pass in master keys instead as they'll be invalid + c, masterKeys, _, _ := TestCoreUnsealedWithConfigs(t, bc, rc) + testCore_GenerateRoot_InvalidMasterNonce_Common(t, c, masterKeys) +} + +func testCore_GenerateRoot_InvalidMasterNonce_Common(t *testing.T, c *Core, keys [][]byte) { otpBytes, err := GenerateRandBytes(16) if err != nil { t.Fatal(err) @@ -116,29 +147,16 @@ func TestCore_GenerateRoot_InvalidMaster(t *testing.T) { t.Fatalf("bad: no rekey config received") } - // Provide the master (invalid) - master[0]++ - _, err = c.GenerateRootUpdate(master, rgconf.Nonce) + // Provide the nonce (invalid) + _, err = c.GenerateRootUpdate(keys[0], "abcd") if err == nil { t.Fatalf("expected error") } -} -func TestCore_GenerateRoot_InvalidNonce(t *testing.T) { - c, master, _ := TestCoreUnsealed(t) - - otpBytes, err := GenerateRandBytes(16) - if err != nil { - t.Fatal(err) + // Provide the master (invalid) + for _, key := range keys { + _, err = c.GenerateRootUpdate(key, rgconf.Nonce) } - - err = c.GenerateRootInit(base64.StdEncoding.EncodeToString(otpBytes), "") - if err != nil { - t.Fatalf("err: %v", err) - } - - // Provide the nonce (invalid) - _, err = c.GenerateRootUpdate(master, "abcd") if err == nil { t.Fatalf("expected error") } @@ -146,7 +164,14 @@ func TestCore_GenerateRoot_InvalidNonce(t *testing.T) { func TestCore_GenerateRoot_Update_OTP(t *testing.T) { c, master, _ := TestCoreUnsealed(t) + testCore_GenerateRoot_Update_OTP_Common(t, c, [][]byte{master}) + bc, rc := TestSealDefConfigs() + c, _, recoveryKeys, _ := TestCoreUnsealedWithConfigs(t, bc, rc) + testCore_GenerateRoot_Update_OTP_Common(t, c, recoveryKeys[0:rc.SecretThreshold]) +} + +func testCore_GenerateRoot_Update_OTP_Common(t *testing.T, c *Core, keys [][]byte) { otpBytes, err := GenerateRandBytes(16) if err != nil { t.Fatal(err) @@ -168,10 +193,13 @@ func TestCore_GenerateRoot_Update_OTP(t *testing.T) { t.Fatalf("bad: no root generation config received") } - // Provide the master - result, err := c.GenerateRootUpdate(master, rkconf.Nonce) - if err != nil { - t.Fatalf("err: %v", err) + // Provide the keys + var result *GenerateRootResult + for _, key := range keys { + result, err = c.GenerateRootUpdate(key, rkconf.Nonce) + if err != nil { + t.Fatalf("err: %v", err) + } } if result == nil { t.Fatalf("Bad, result is nil") @@ -222,7 +250,14 @@ func TestCore_GenerateRoot_Update_OTP(t *testing.T) { func TestCore_GenerateRoot_Update_PGP(t *testing.T) { c, master, _ := TestCoreUnsealed(t) + testCore_GenerateRoot_Update_PGP_Common(t, c, [][]byte{master}) + bc, rc := TestSealDefConfigs() + c, _, recoveryKeys, _ := TestCoreUnsealedWithConfigs(t, bc, rc) + testCore_GenerateRoot_Update_PGP_Common(t, c, recoveryKeys[0:rc.SecretThreshold]) +} + +func testCore_GenerateRoot_Update_PGP_Common(t *testing.T, c *Core, keys [][]byte) { // Start a root generation err := c.GenerateRootInit("", pgpkeys.TestPubKey1) if err != nil { @@ -238,10 +273,13 @@ func TestCore_GenerateRoot_Update_PGP(t *testing.T) { t.Fatalf("bad: no root generation config received") } - // Provide the master - result, err := c.GenerateRootUpdate(master, rkconf.Nonce) - if err != nil { - t.Fatalf("err: %v", err) + // Provide the keys + var result *GenerateRootResult + for _, key := range keys { + result, err = c.GenerateRootUpdate(key, rkconf.Nonce) + if err != nil { + t.Fatalf("err: %v", err) + } } if result == nil { t.Fatalf("Bad, result is nil") diff --git a/vault/init.go b/vault/init.go index a943a0422..df8089b65 100644 --- a/vault/init.go +++ b/vault/init.go @@ -85,34 +85,14 @@ func (c *Core) Initialize(barrierConfig, recoveryConfig *SealConfig) (*InitResul return nil, fmt.Errorf("recovery configuration must be supplied") } - if recoveryConfig.SecretShares == 0 { - return nil, fmt.Errorf("recovery configuration must specify a positive number of shares, or a negative number to disable") + if recoveryConfig.SecretShares < 1 { + return nil, fmt.Errorf("recovery configuration must specify a positive number of shares") } - if recoveryConfig.SecretShares > 0 { - // Check if the seal configuraiton is valid - if err := recoveryConfig.Validate(); err != nil { - c.logger.Printf("[ERR] core: invalid recovery configuration: %v", err) - return nil, fmt.Errorf("invalid recovery configuration: %v", err) - } - } - } - if c.seal.StoredKeysSupported() { - if barrierConfig.SecretShares != 1 { - return nil, fmt.Errorf("secret shares must be 1") - } - if barrierConfig.SecretThreshold != barrierConfig.SecretShares { - return nil, fmt.Errorf("secret threshold must be same as secret shares") - } - if barrierConfig.StoredShares != barrierConfig.SecretShares { - return nil, fmt.Errorf("stored shares must be same as secret shares") - } - if barrierConfig.PGPKeys != nil && len(barrierConfig.PGPKeys) > 0 { - return nil, fmt.Errorf("PGP keys not supported when storing shares") - } - } else { - if barrierConfig.StoredShares > 0 { - return nil, fmt.Errorf("stored keys are not supported") + // Check if the seal configuraiton is valid + if err := recoveryConfig.Validate(); err != nil { + c.logger.Printf("[ERR] core: invalid recovery configuration: %v", err) + return nil, fmt.Errorf("invalid recovery configuration: %v", err) } } diff --git a/vault/init_test.go b/vault/init_test.go index ac71a64ea..7a43c0d15 100644 --- a/vault/init_test.go +++ b/vault/init_test.go @@ -9,6 +9,17 @@ import ( ) func TestCore_Init(t *testing.T) { + c, conf := testCore_NewTestCore(t, nil) + testCore_Init_Common(t, c, conf, &SealConfig{SecretShares: 5, SecretThreshold: 3}, nil) + + c, conf = testCore_NewTestCore(t, &TestSeal{}) + bc, rc := TestSealDefConfigs() + rc.SecretShares = 4 + rc.SecretThreshold = 2 + testCore_Init_Common(t, c, conf, bc, rc) +} + +func testCore_NewTestCore(t *testing.T, seal Seal) (*Core, *CoreConfig) { inm := physical.NewInmem() conf := &CoreConfig{ Physical: inm, @@ -16,12 +27,16 @@ func TestCore_Init(t *testing.T) { LogicalBackends: map[string]logical.Factory{ "generic": LeasedPassthroughBackendFactory, }, + Seal: seal, } c, err := NewCore(conf) if err != nil { t.Fatalf("err: %v", err) } + return c, conf +} +func testCore_Init_Common(t *testing.T, c *Core, conf *CoreConfig, barrierConf, recoveryConf *SealConfig) { init, err := c.Initialized() if err != nil { t.Fatalf("err: %v", err) @@ -39,24 +54,35 @@ func TestCore_Init(t *testing.T) { if outConf != nil { t.Fatalf("bad: %v", outConf) } - - sealConf := &SealConfig{ - SecretShares: 1, - SecretThreshold: 1, + if recoveryConf != nil { + outConf, err := c.seal.RecoveryConfig() + if err != nil { + t.Fatalf("err: %v", err) + } + if outConf != nil { + t.Fatalf("bad: %v", outConf) + } } - res, err := c.Initialize(sealConf, nil) + + res, err := c.Initialize(barrierConf, recoveryConf) if err != nil { t.Fatalf("err: %v", err) } - if len(res.SecretShares) != 1 { - t.Fatalf("Bad: %v", res) + if len(res.SecretShares) != (barrierConf.SecretShares - barrierConf.StoredShares) { + t.Fatalf("Bad: got\n%#v\nexpected conf matching\n%#v\n", *res, *barrierConf) } - if res.RootToken == "" { - t.Fatalf("Bad: %v", res) + if recoveryConf != nil { + if len(res.RecoveryShares) != recoveryConf.SecretShares { + t.Fatalf("Bad: got\n%#v\nexpected conf matching\n%#v\n", *res, *recoveryConf) + } } - _, err = c.Initialize(sealConf, nil) + if res.RootToken == "" { + t.Fatalf("Bad: %#v", res) + } + + _, err = c.Initialize(barrierConf, recoveryConf) if err != ErrAlreadyInit { t.Fatalf("err: %v", err) } @@ -75,8 +101,17 @@ func TestCore_Init(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } - if !reflect.DeepEqual(outConf, sealConf) { - t.Fatalf("bad: %v expect: %v", outConf, sealConf) + if !reflect.DeepEqual(outConf, barrierConf) { + t.Fatalf("bad: %v expect: %v", outConf, barrierConf) + } + if recoveryConf != nil { + outConf, err = c.seal.RecoveryConfig() + if err != nil { + t.Fatalf("err: %v", err) + } + if !reflect.DeepEqual(outConf, recoveryConf) { + t.Fatalf("bad: %v expect: %v", outConf, recoveryConf) + } } // New Core, same backend @@ -85,7 +120,7 @@ func TestCore_Init(t *testing.T) { t.Fatalf("err: %v", err) } - _, err = c2.Initialize(sealConf, nil) + _, err = c2.Initialize(barrierConf, recoveryConf) if err != ErrAlreadyInit { t.Fatalf("err: %v", err) } @@ -104,7 +139,16 @@ func TestCore_Init(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } - if !reflect.DeepEqual(outConf, sealConf) { - t.Fatalf("bad: %v expect: %v", outConf, sealConf) + if !reflect.DeepEqual(outConf, barrierConf) { + t.Fatalf("bad: %v expect: %v", outConf, barrierConf) + } + if recoveryConf != nil { + outConf, err = c2.seal.RecoveryConfig() + if err != nil { + t.Fatalf("err: %v", err) + } + if !reflect.DeepEqual(outConf, recoveryConf) { + t.Fatalf("bad: %v expect: %v", outConf, recoveryConf) + } } } diff --git a/vault/rekey.go b/vault/rekey.go index 580182a62..e8eca53f0 100644 --- a/vault/rekey.go +++ b/vault/rekey.go @@ -124,13 +124,6 @@ func (c *Core) RekeyInit(config *SealConfig, recovery bool) error { // BarrierRekeyInit is used to initialize the rekey settings for the barrier key func (c *Core) BarrierRekeyInit(config *SealConfig) error { - // Right now we don't support this, but the rest of the code is ready for - // when we do, hence the check below for this to be false if - // config.StoredShares is greater than zero - if c.seal.StoredKeysSupported() { - return fmt.Errorf("rekeying of barrier not supported when stored key support is available") - } - if config.StoredShares > 0 { if !c.seal.StoredKeysSupported() { return fmt.Errorf("storing keys not supported by barrier seal") diff --git a/vault/rekey_test.go b/vault/rekey_test.go index e57af1281..25862ef46 100644 --- a/vault/rekey_test.go +++ b/vault/rekey_test.go @@ -1,6 +1,7 @@ package vault import ( + "fmt" "reflect" "testing" @@ -9,14 +10,25 @@ import ( func TestCore_Rekey_Lifecycle(t *testing.T) { c, master, _ := TestCoreUnsealed(t) + testCore_Rekey_Lifecycle_Common(t, c, [][]byte{master}, false) + bc, rc := TestSealDefConfigs() + c, masterKeys, recoveryKeys, _ := TestCoreUnsealedWithConfigs(t, bc, rc) + if len(masterKeys) != 3 { + t.Fatalf("expected %d keys, got %d", bc.SecretShares-bc.StoredShares, len(masterKeys)) + } + testCore_Rekey_Lifecycle_Common(t, c, masterKeys, false) + testCore_Rekey_Lifecycle_Common(t, c, recoveryKeys, true) +} + +func testCore_Rekey_Lifecycle_Common(t *testing.T, c *Core, masterKeys [][]byte, recovery bool) { // Verify update not allowed - if _, err := c.RekeyUpdate(master, "", false); err == nil { - t.Fatalf("no rekey in progress") + if _, err := c.RekeyUpdate(masterKeys[0], "", recovery); err == nil { + t.Fatalf("no rekey should be in progress") } // Should be no progress - num, err := c.RekeyProgress(false) + num, err := c.RekeyProgress(recovery) if err != nil { t.Fatalf("err: %v", err) } @@ -25,7 +37,7 @@ func TestCore_Rekey_Lifecycle(t *testing.T) { } // Should be no config - conf, err := c.RekeyConfig(false) + conf, err := c.RekeyConfig(recovery) if err != nil { t.Fatalf("err: %v", err) } @@ -44,13 +56,13 @@ func TestCore_Rekey_Lifecycle(t *testing.T) { SecretThreshold: 3, SecretShares: 5, } - err = c.RekeyInit(newConf, false) + err = c.RekeyInit(newConf, recovery) if err != nil { t.Fatalf("err: %v", err) } // Should get config - conf, err = c.RekeyConfig(false) + conf, err = c.RekeyConfig(recovery) if err != nil { t.Fatalf("err: %v", err) } @@ -60,13 +72,13 @@ func TestCore_Rekey_Lifecycle(t *testing.T) { } // Cancel should be clear - err = c.RekeyCancel(false) + err = c.RekeyCancel(recovery) if err != nil { t.Fatalf("err: %v", err) } // Should be no config - conf, err = c.RekeyConfig(false) + conf, err = c.RekeyConfig(recovery) if err != nil { t.Fatalf("err: %v", err) } @@ -77,13 +89,21 @@ func TestCore_Rekey_Lifecycle(t *testing.T) { func TestCore_Rekey_Init(t *testing.T) { c, _, _ := TestCoreUnsealed(t) + testCore_Rekey_Init_Common(t, c, false) + bc, rc := TestSealDefConfigs() + c, _, _, _ = TestCoreUnsealedWithConfigs(t, bc, rc) + testCore_Rekey_Init_Common(t, c, false) + testCore_Rekey_Init_Common(t, c, true) +} + +func testCore_Rekey_Init_Common(t *testing.T, c *Core, recovery bool) { // Try an invalid config badConf := &SealConfig{ SecretThreshold: 5, SecretShares: 1, } - err := c.RekeyInit(badConf, false) + err := c.RekeyInit(badConf, recovery) if err == nil { t.Fatalf("should fail") } @@ -93,13 +113,13 @@ func TestCore_Rekey_Init(t *testing.T) { SecretThreshold: 3, SecretShares: 5, } - err = c.RekeyInit(newConf, false) + err = c.RekeyInit(newConf, recovery) if err != nil { t.Fatalf("err: %v", err) } // Second should fail - err = c.RekeyInit(newConf, false) + err = c.RekeyInit(newConf, recovery) if err == nil { t.Fatalf("should fail") } @@ -107,20 +127,29 @@ func TestCore_Rekey_Init(t *testing.T) { func TestCore_Rekey_Update(t *testing.T) { c, master, root := TestCoreUnsealed(t) + testCore_Rekey_Update_Common(t, c, [][]byte{master}, root, false) + bc, rc := TestSealDefConfigs() + bc.StoredShares = 0 + c, masterKeys, recoveryKeys, root := TestCoreUnsealedWithConfigs(t, bc, rc) + testCore_Rekey_Update_Common(t, c, masterKeys, root, false) + testCore_Rekey_Update_Common(t, c, recoveryKeys, root, true) +} + +func testCore_Rekey_Update_Common(t *testing.T, c *Core, keys [][]byte, root string, recovery bool) { // Start a rekey newConf := &SealConfig{ Type: "shamir", SecretThreshold: 3, SecretShares: 5, } - err := c.RekeyInit(newConf, false) + err := c.RekeyInit(newConf, recovery) if err != nil { t.Fatalf("err: %v", err) } // Fetch new config with generated nonce - rkconf, err := c.RekeyConfig(false) + rkconf, err := c.RekeyConfig(recovery) if err != nil { t.Fatalf("err: %v", err) } @@ -129,16 +158,22 @@ func TestCore_Rekey_Update(t *testing.T) { } // Provide the master - result, err := c.RekeyUpdate(master, rkconf.Nonce, false) - if err != nil { - t.Fatalf("err: %v", err) + var result *RekeyResult + for _, key := range keys { + result, err = c.RekeyUpdate(key, rkconf.Nonce, recovery) + if err != nil { + t.Fatalf("err: %v", err) + } + if result != nil { + break + } } if result == nil || len(result.SecretShares) != 5 { t.Fatalf("Bad: %#v", result) } // Should be no progress - num, err := c.RekeyProgress(false) + num, err := c.RekeyProgress(recovery) if err != nil { t.Fatalf("err: %v", err) } @@ -147,7 +182,7 @@ func TestCore_Rekey_Update(t *testing.T) { } // Should be no config - conf, err := c.RekeyConfig(false) + conf, err := c.RekeyConfig(recovery) if err != nil { t.Fatalf("err: %v", err) } @@ -156,7 +191,12 @@ func TestCore_Rekey_Update(t *testing.T) { } // SealConfig should update - sealConf, err := c.seal.BarrierConfig() + var sealConf *SealConfig + if recovery { + sealConf, err = c.seal.RecoveryConfig() + } else { + sealConf, err = c.seal.BarrierConfig() + } if err != nil { t.Fatalf("err: %v", err) } @@ -169,19 +209,21 @@ func TestCore_Rekey_Update(t *testing.T) { t.Fatalf("\nexpected: %#v\nactual: %#v\n", newConf, sealConf) } - // Attempt unseal - err = c.Seal(root) - if err != nil { - t.Fatalf("err: %v", err) - } - for i := 0; i < 3; i++ { - _, err = c.Unseal(result.SecretShares[i]) + // Attempt unseal if this was not recovery mode + if !recovery { + err = c.Seal(root) if err != nil { t.Fatalf("err: %v", err) } - } - if sealed, _ := c.Sealed(); sealed { - t.Fatalf("should be unsealed") + for i := 0; i < 3; i++ { + _, err = c.Unseal(result.SecretShares[i]) + if err != nil { + t.Fatalf("err: %v", err) + } + } + if sealed, _ := c.Sealed(); sealed { + t.Fatalf("should be unsealed") + } } // Start another rekey, this time we require a quorum! @@ -190,13 +232,13 @@ func TestCore_Rekey_Update(t *testing.T) { SecretThreshold: 1, SecretShares: 1, } - err = c.RekeyInit(newConf, false) + err = c.RekeyInit(newConf, recovery) if err != nil { t.Fatalf("err: %v", err) } // Fetch new config with generated nonce - rkconf, err = c.RekeyConfig(false) + rkconf, err = c.RekeyConfig(recovery) if err != nil { t.Fatalf("err: %v", err) } @@ -207,13 +249,13 @@ func TestCore_Rekey_Update(t *testing.T) { // Provide the parts master oldResult := result for i := 0; i < 3; i++ { - result, err = c.RekeyUpdate(oldResult.SecretShares[i], rkconf.Nonce, false) + result, err = c.RekeyUpdate(oldResult.SecretShares[i], rkconf.Nonce, recovery) if err != nil { t.Fatalf("err: %v", err) } // Should be progress - num, err := c.RekeyProgress(false) + num, err := c.RekeyProgress(recovery) if err != nil { t.Fatalf("err: %v", err) } @@ -225,21 +267,27 @@ func TestCore_Rekey_Update(t *testing.T) { t.Fatalf("Bad: %#v", result) } - // Attempt unseal - err = c.Seal(root) - if err != nil { - t.Fatalf("err: %v", err) - } - unseal, err := c.Unseal(result.SecretShares[0]) - if err != nil { - t.Fatalf("err: %v", err) - } - if !unseal { - t.Fatalf("should be unsealed") + // Attempt unseal if this was not recovery mode + if !recovery { + err = c.Seal(root) + if err != nil { + t.Fatalf("err: %v", err) + } + unseal, err := c.Unseal(result.SecretShares[0]) + if err != nil { + t.Fatalf("err: %v", err) + } + if !unseal { + t.Fatalf("should be unsealed") + } } // SealConfig should update - sealConf, err = c.seal.BarrierConfig() + if recovery { + sealConf, err = c.seal.RecoveryConfig() + } else { + sealConf, err = c.seal.BarrierConfig() + } if err != nil { t.Fatalf("err: %v", err) } @@ -250,21 +298,34 @@ func TestCore_Rekey_Update(t *testing.T) { } } -func TestCore_Rekey_InvalidMaster(t *testing.T) { +func TestCore_Rekey_Invalid(t *testing.T) { c, master, _ := TestCoreUnsealed(t) + testCore_Rekey_Invalid_Common(t, c, [][]byte{master}, false) + bc, rc := TestSealDefConfigs() + bc.StoredShares = 0 + bc.SecretShares = 1 + bc.SecretThreshold = 1 + rc.SecretShares = 1 + rc.SecretThreshold = 1 + c, masterKeys, recoveryKeys, _ := TestCoreUnsealedWithConfigs(t, bc, rc) + testCore_Rekey_Invalid_Common(t, c, masterKeys, false) + testCore_Rekey_Invalid_Common(t, c, recoveryKeys, true) +} + +func testCore_Rekey_Invalid_Common(t *testing.T, c *Core, keys [][]byte, recovery bool) { // Start a rekey newConf := &SealConfig{ SecretThreshold: 3, SecretShares: 5, } - err := c.RekeyInit(newConf, false) + err := c.RekeyInit(newConf, recovery) if err != nil { t.Fatalf("err: %v", err) } // Fetch new config with generated nonce - rkconf, err := c.RekeyConfig(false) + rkconf, err := c.RekeyConfig(recovery) if err != nil { t.Fatalf("err: %v", err) } @@ -272,32 +333,21 @@ func TestCore_Rekey_InvalidMaster(t *testing.T) { t.Fatalf("bad: no rekey config received") } - // Provide the master (invalid) - master[0]++ - _, err = c.RekeyUpdate(master, rkconf.Nonce, false) - if err == nil { - t.Fatalf("expected error") - } -} - -func TestCore_Rekey_InvalidNonce(t *testing.T) { - c, master, _ := TestCoreUnsealed(t) - - // Start a rekey - newConf := &SealConfig{ - SecretThreshold: 3, - SecretShares: 5, - } - err := c.RekeyInit(newConf, false) - if err != nil { - t.Fatalf("err: %v", err) - } - // Provide the nonce (invalid) - _, err = c.RekeyUpdate(master, "abcd", false) + _, err = c.RekeyUpdate(keys[0], "abcd", recovery) if err == nil { t.Fatalf("expected error") } + + // Provide the key (invalid) + key := keys[0] + oldkeystr := fmt.Sprintf("%#v", key) + key[0]++ + newkeystr := fmt.Sprintf("%#v", key) + ret, err := c.RekeyUpdate(key, rkconf.Nonce, recovery) + if err == nil { + t.Fatalf("expected error, ret is %#v\noldkeystr: %s\nnewkeystr: %s", *ret, oldkeystr, newkeystr) + } } func TestCore_Standby_Rekey(t *testing.T) { diff --git a/vault/seal_test.go b/vault/seal_test.go new file mode 100644 index 000000000..26f37dd3d --- /dev/null +++ b/vault/seal_test.go @@ -0,0 +1,128 @@ +package vault + +import ( + "bytes" + "fmt" + "reflect" + "testing" +) + +type TestSeal struct { + defseal *DefaultSeal + barrierKeys [][]byte + recoveryKey []byte + recoveryConfig *SealConfig +} + +func (d *TestSeal) checkCore() error { + if d.defseal.core == nil { + return fmt.Errorf("seal does not have a core set") + } + return nil +} + +func (d *TestSeal) SetCore(core *Core) { + d.defseal = &DefaultSeal{} + d.defseal.core = core +} + +func (d *TestSeal) Init() error { + d.barrierKeys = [][]byte{} + return d.defseal.Init() +} + +func (d *TestSeal) Finalize() error { + return d.defseal.Finalize() +} + +func (d *TestSeal) BarrierType() string { + return "shamir" +} + +func (d *TestSeal) StoredKeysSupported() bool { + return true +} + +func (d *TestSeal) RecoveryKeySupported() bool { + return true +} + +func (d *TestSeal) SetStoredKeys(keys [][]byte) error { + d.barrierKeys = keys + return nil +} + +func (d *TestSeal) GetStoredKeys() ([][]byte, error) { + return d.barrierKeys, nil +} + +func (d *TestSeal) BarrierConfig() (*SealConfig, error) { + return d.defseal.BarrierConfig() +} + +func (d *TestSeal) SetBarrierConfig(config *SealConfig) error { + return d.defseal.SetBarrierConfig(config) +} + +func (d *TestSeal) RecoveryType() string { + return "shamir" +} + +func (d *TestSeal) RecoveryConfig() (*SealConfig, error) { + return d.recoveryConfig, nil +} + +func (d *TestSeal) SetRecoveryConfig(config *SealConfig) error { + d.recoveryConfig = config + return nil +} + +func (d *TestSeal) VerifyRecoveryKey(key []byte) error { + if bytes.Equal(d.recoveryKey, key) { + return nil + } + return fmt.Errorf("not equivalent") +} + +func (d *TestSeal) SetRecoveryKey(key []byte) error { + newbuf := bytes.NewBuffer(nil) + newbuf.Write(key) + d.recoveryKey = newbuf.Bytes() + return nil +} + +func TestDefaultSeal_Config(t *testing.T) { + bc, _ := TestSealDefConfigs() + // Change these to non-default values to ensure we are seeing the real + // config we set + bc.SecretShares = 4 + bc.SecretThreshold = 2 + + core, _, _ := TestCoreUnsealed(t) + + defSeal := &DefaultSeal{} + defSeal.SetCore(core) + err := defSeal.SetBarrierConfig(bc) + if err != nil { + t.Fatal(err) + } + + newBc, err := defSeal.BarrierConfig() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(*bc, *newBc) { + t.Fatal("config mismatch") + } + + // Now, test without the benefit of the cached value in the seal + defSeal = &DefaultSeal{} + defSeal.SetCore(core) + newBc, err = defSeal.BarrierConfig() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(*bc, *newBc) { + t.Fatal("config mismatch") + } +} diff --git a/vault/testing.go b/vault/testing.go index b2aea4e78..31b694f8c 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -62,6 +62,12 @@ oOyBJU/HMVvBfv4g+OVFLVgSwwm6owwsouZ0+D/LasbuHqYyqYqdyPJQYzWA2Y+F // TestCore returns a pure in-memory, uninitialized core for testing. func TestCore(t *testing.T) *Core { + return TestCoreWithSeal(t, nil) +} + +// TestCoreWithSeal returns a pure in-memory, uninitialized core with the +// specified seal for testing. +func TestCoreWithSeal(t *testing.T, testSeal Seal) *Core { noopAudits := map[string]audit.Factory{ "noop": func(config *audit.BackendConfig) (audit.Backend, error) { view := &logical.InmemStorage{} @@ -101,13 +107,18 @@ func TestCore(t *testing.T) *Core { } physicalBackend := physical.NewInmem() - c, err := NewCore(&CoreConfig{ + conf := &CoreConfig{ Physical: physicalBackend, AuditBackends: noopAudits, LogicalBackends: logicalBackends, CredentialBackends: noopBackends, DisableMlock: true, - }) + } + if testSeal != nil { + conf.Seal = testSeal + } + + c, err := NewCore(conf) if err != nil { t.Fatalf("err: %s", err) } @@ -363,3 +374,49 @@ func GenerateRandBytes(length int) ([]byte, error) { return buf, nil } + +/* + * TestSeal items + */ + +func TestCoreUnsealedWithConfigs(t *testing.T, barrierConf, recoveryConf *SealConfig) (*Core, [][]byte, [][]byte, string) { + seal := &TestSeal{} + core := TestCoreWithSeal(t, seal) + result, err := core.Initialize(barrierConf, recoveryConf) + if err != nil { + t.Fatalf("err: %s", err) + } + err = core.UnsealWithStoredKeys() + if err != nil { + t.Fatalf("err: %s", err) + } + if sealed, _ := core.Sealed(); sealed { + for _, key := range result.SecretShares { + if _, err := core.Unseal(key); err != nil { + + t.Fatalf("unseal err: %s", err) + } + } + + sealed, err = core.Sealed() + if err != nil { + t.Fatalf("err checking seal status: %s", err) + } + if sealed { + t.Fatal("should not be sealed") + } + } + + return core, result.SecretShares, result.RecoveryShares, result.RootToken +} + +func TestSealDefConfigs() (*SealConfig, *SealConfig) { + return &SealConfig{ + SecretShares: 5, + SecretThreshold: 3, + StoredShares: 2, + }, &SealConfig{ + SecretShares: 5, + SecretThreshold: 3, + } +} diff --git a/vault/token_store.go b/vault/token_store.go index dcf58376b..a2a625cba 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -524,7 +524,9 @@ func (ts *TokenStore) storeCommon(entry *TokenEntry, writeSecondary bool) error } // UseToken is used to manage restricted use tokens and decrement -// their available uses. +// their available uses. Note: this is potentially racy, but the simple +// solution of a global lock would be severely detrimental to performance. Also +// note the specific revoke case below. func (ts *TokenStore) UseToken(te *TokenEntry) error { // If the token is not restricted, there is nothing to do if te.NumUses == 0 {