Add seal tests and update generate-root and others to handle dualseal.
This commit is contained in:
parent
30ba5b7887
commit
98d09b0dc6
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue