Finish non-recovery test
This commit is contained in:
parent
b5868a1de7
commit
6340add8c1
|
@ -26,6 +26,32 @@ func (c *Sys) RekeyRecoveryKeyStatus() (*RekeyStatusResponse, error) {
|
|||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyVerificationStatus() (*RekeyVerificationStatusResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/rekey/verify")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyVerificationStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyVerificationStatus() (*RekeyVerificationStatusResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/rekey-recovery-key/verify")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyVerificationStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyInit(config *RekeyInitRequest) (*RekeyStatusResponse, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey/init")
|
||||
if err := r.SetJSONBody(config); err != nil {
|
||||
|
@ -78,6 +104,24 @@ func (c *Sys) RekeyRecoveryKeyCancel() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyVerificationCancel() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey/verify")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyVerificationCancel() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey-recovery-key/verify")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyUpdate(shard, nonce string) (*RekeyUpdateResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"key": shard,
|
||||
|
@ -168,6 +212,50 @@ func (c *Sys) RekeyDeleteRecoveryBackup() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyVerificationUpdate(shard, nonce string) (*RekeyVerificationUpdateResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"key": shard,
|
||||
"nonce": nonce,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey/verify")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyVerificationUpdateResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyVerificationUpdate(shard, nonce string) (*RekeyVerificationUpdateResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"key": shard,
|
||||
"nonce": nonce,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey-recovery-key/verify")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyVerificationUpdateResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
type RekeyInitRequest struct {
|
||||
SecretShares int `json:"secret_shares"`
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package api_test
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -25,7 +26,6 @@ func TestSysRekey_Verification(t *testing.T) {
|
|||
// the keys aren't returned
|
||||
vault.DefaultSealPretendsToAllowRecoveryKeys = true
|
||||
vault.DefaultSealPretendsToAllowStoredShares = true
|
||||
vault.DefaultSealPretendRecoveryConfig = &vault.SealConfig{}
|
||||
status, err := client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
StoredShares: 1,
|
||||
RequireVerification: true,
|
||||
|
@ -40,20 +40,169 @@ func TestSysRekey_Verification(t *testing.T) {
|
|||
// Now we set things back and start a normal rekey with the verification process
|
||||
vault.DefaultSealPretendsToAllowRecoveryKeys = false
|
||||
vault.DefaultSealPretendsToAllowStoredShares = false
|
||||
vault.DefaultSealPretendRecoveryConfig = nil
|
||||
status, err = client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
|
||||
var verificationNonce string
|
||||
var newKeys []string
|
||||
doRekeyInitialSteps := func() {
|
||||
status, err = client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
SecretShares: 5,
|
||||
SecretThreshold: 3,
|
||||
RequireVerification: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if status == nil {
|
||||
t.Fatal("nil status")
|
||||
}
|
||||
if !status.VerificationRequired {
|
||||
t.Fatal("expected verification required")
|
||||
}
|
||||
|
||||
var resp *api.RekeyUpdateResponse
|
||||
for i := 0; i < 3; i++ {
|
||||
resp, err = client.Sys().RekeyUpdate(base64.StdEncoding.EncodeToString(cluster.BarrierKeys[i]), status.Nonce)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case !resp.Complete:
|
||||
t.Fatal("expected completion")
|
||||
case !resp.VerificationRequired:
|
||||
t.Fatal("expected verification required")
|
||||
case resp.VerificationNonce == "":
|
||||
t.Fatal("verification nonce expected")
|
||||
}
|
||||
verificationNonce = resp.VerificationNonce
|
||||
newKeys = resp.KeysB64
|
||||
t.Logf("verification nonce: %q", verificationNonce)
|
||||
}
|
||||
|
||||
doRekeyInitialSteps()
|
||||
|
||||
// We are still going, so should not be able to init again
|
||||
_, err = client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
SecretShares: 5,
|
||||
SecretThreshold: 3,
|
||||
RequireVerification: true,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
// Sealing should clear state, so after this we should be able to perform
|
||||
// the above again
|
||||
cluster.EnsureCoresSealed(t)
|
||||
cluster.UnsealCores(t)
|
||||
doRekeyInitialSteps()
|
||||
|
||||
doStartVerify := func() {
|
||||
// Start the process
|
||||
for i := 0; i < 2; i++ {
|
||||
status, err := client.Sys().RekeyVerificationUpdate(newKeys[i], verificationNonce)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
switch {
|
||||
case status.Nonce != verificationNonce:
|
||||
t.Fatalf("unexpected nonce, expected %q, got %q", verificationNonce, status.Nonce)
|
||||
case status.Complete:
|
||||
t.Fatal("unexpected completion")
|
||||
}
|
||||
}
|
||||
|
||||
// Check status
|
||||
vStatus, err := client.Sys().RekeyVerificationStatus()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
switch {
|
||||
case vStatus.Nonce != verificationNonce:
|
||||
t.Fatalf("unexpected nonce, expected %q, got %q", verificationNonce, vStatus.Nonce)
|
||||
case vStatus.T != 3:
|
||||
t.Fatal("unexpected threshold")
|
||||
case vStatus.N != 5:
|
||||
t.Fatal("unexpected number of new keys")
|
||||
case vStatus.Progress != 2:
|
||||
t.Fatal("unexpected progress")
|
||||
}
|
||||
}
|
||||
|
||||
doStartVerify()
|
||||
|
||||
// Cancel; this should still keep the rekey process going but just cancel
|
||||
// the verification operation
|
||||
err = client.Sys().RekeyVerificationCancel()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if status == nil {
|
||||
t.Fatal("nil status")
|
||||
// Verify cannot init again
|
||||
_, err = client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
SecretShares: 5,
|
||||
SecretThreshold: 3,
|
||||
RequireVerification: true,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
vStatus, err := client.Sys().RekeyVerificationStatus()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
switch {
|
||||
case vStatus.Nonce == verificationNonce:
|
||||
t.Fatalf("unexpected nonce, expected not-%q but got it", verificationNonce)
|
||||
case vStatus.T != 3:
|
||||
t.Fatal("unexpected threshold")
|
||||
case vStatus.N != 5:
|
||||
t.Fatal("unexpected number of new keys")
|
||||
case vStatus.Progress != 0:
|
||||
t.Fatal("unexpected progress")
|
||||
}
|
||||
|
||||
verificationNonce = vStatus.Nonce
|
||||
doStartVerify()
|
||||
|
||||
// Sealing should clear state, but we never actually finished, so it should
|
||||
// still be the old keys (which are still currently set)
|
||||
cluster.EnsureCoresSealed(t)
|
||||
cluster.UnsealCores(t)
|
||||
|
||||
// Should be able to init again and get back to where we were
|
||||
doRekeyInitialSteps()
|
||||
doStartVerify()
|
||||
|
||||
// Provide the final new key
|
||||
vuStatus, err := client.Sys().RekeyVerificationUpdate(newKeys[2], verificationNonce)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
switch {
|
||||
case vuStatus.Nonce != verificationNonce:
|
||||
t.Fatalf("unexpected nonce, expected %q, got %q", verificationNonce, vuStatus.Nonce)
|
||||
case !vuStatus.Complete:
|
||||
t.Fatal("expected completion")
|
||||
}
|
||||
|
||||
// Seal and unseal -- it should fail to unseal because the key has now been
|
||||
// rotated
|
||||
cluster.EnsureCoresSealed(t)
|
||||
if err := cluster.UnsealCoresWithError(); err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
// Swap out the keys with our new ones and try again
|
||||
var newKeyBytes [][]byte
|
||||
for _, key := range newKeys {
|
||||
val, err := base64.StdEncoding.DecodeString(key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
newKeyBytes = append(newKeyBytes, val)
|
||||
}
|
||||
cluster.BarrierKeys = newKeyBytes
|
||||
if err := cluster.UnsealCoresWithError(); err != nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
/*
|
||||
cluster.EnsureCoresSealed(t)
|
||||
cluster.UnsealCores(t)
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -299,7 +299,7 @@ func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery boo
|
|||
|
||||
// Format the status
|
||||
status := &RekeyVerificationStatusResponse{
|
||||
Nonce: rekeyConf.Nonce,
|
||||
Nonce: rekeyConf.VerificationNonce,
|
||||
T: rekeyConf.SecretThreshold,
|
||||
N: rekeyConf.SecretShares,
|
||||
Progress: progress,
|
||||
|
@ -354,7 +354,7 @@ func handleSysRekeyVerifyPut(ctx context.Context, core *vault.Core, recovery boo
|
|||
defer cancel()
|
||||
|
||||
// Use the key to make progress on rekey
|
||||
result, rekeyErr := core.RekeyVerify(ctx, key, recovery)
|
||||
result, rekeyErr := core.RekeyVerify(ctx, key, req.Nonce, recovery)
|
||||
if rekeyErr != nil {
|
||||
respondError(w, rekeyErr.Code(), err)
|
||||
return
|
||||
|
@ -409,7 +409,8 @@ type RekeyUpdateResponse struct {
|
|||
}
|
||||
|
||||
type RekeyVerificationUpdateRequest struct {
|
||||
Key string
|
||||
Nonce string `json:"nonce"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
type RekeyVerificationStatusResponse struct {
|
||||
|
|
|
@ -726,14 +726,14 @@ func (c *Core) performRecoveryRekey(ctx context.Context, newMasterKey []byte) lo
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Core) RekeyVerify(ctx context.Context, key []byte, recovery bool) (*RekeyVerifyResult, logical.HTTPCodedError) {
|
||||
func (c *Core) RekeyVerify(ctx context.Context, key []byte, nonce string, recovery bool) (*RekeyVerifyResult, logical.HTTPCodedError) {
|
||||
if recovery {
|
||||
return c.RecoveryRekeyVerify(ctx, key)
|
||||
return c.RecoveryRekeyVerify(ctx, key, nonce)
|
||||
}
|
||||
return c.BarrierRekeyVerify(ctx, key)
|
||||
return c.BarrierRekeyVerify(ctx, key, nonce)
|
||||
}
|
||||
|
||||
func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerifyResult, logical.HTTPCodedError) {
|
||||
func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte, nonce string) (ret *RekeyVerifyResult, retErr logical.HTTPCodedError) {
|
||||
// Ensure we are already unsealed
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
|
@ -762,6 +762,10 @@ func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerify
|
|||
return nil, logical.CodedError(http.StatusBadRequest, "no barrier rekey in progress")
|
||||
}
|
||||
|
||||
if nonce != c.barrierRekeyConfig.VerificationNonce {
|
||||
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("incorrect nonce supplied; nonce for this verify operation is %q", c.barrierRekeyConfig.VerificationNonce))
|
||||
}
|
||||
|
||||
// Check if we already have this piece
|
||||
for _, existing := range c.barrierRekeyVerifyProgress {
|
||||
if subtle.ConstantTimeCompare(existing, key) == 1 {
|
||||
|
@ -787,6 +791,9 @@ func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerify
|
|||
nonce, err := uuid.GenerateUUID()
|
||||
if err == nil {
|
||||
c.barrierRekeyConfig.VerificationNonce = nonce
|
||||
if ret != nil {
|
||||
ret.Nonce = nonce
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -820,7 +827,7 @@ func (c *Core) BarrierRekeyVerify(ctx context.Context, key []byte) (*RekeyVerify
|
|||
return res, nil
|
||||
}
|
||||
|
||||
func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerifyResult, logical.HTTPCodedError) {
|
||||
func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte, nonce string) (ret *RekeyVerifyResult, retErr logical.HTTPCodedError) {
|
||||
// Ensure we are already unsealed
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
|
@ -849,6 +856,10 @@ func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerif
|
|||
return nil, logical.CodedError(http.StatusBadRequest, "no recovery rekey in progress")
|
||||
}
|
||||
|
||||
if nonce != c.recoveryRekeyConfig.VerificationNonce {
|
||||
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("incorrect nonce supplied; nonce for this verify operation is %q", c.recoveryRekeyConfig.VerificationNonce))
|
||||
}
|
||||
|
||||
// Check if we already have this piece
|
||||
for _, existing := range c.recoveryRekeyVerifyProgress {
|
||||
if subtle.ConstantTimeCompare(existing, key) == 1 {
|
||||
|
@ -874,6 +885,9 @@ func (c *Core) RecoveryRekeyVerify(ctx context.Context, key []byte) (*RekeyVerif
|
|||
nonce, err := uuid.GenerateUUID()
|
||||
if err == nil {
|
||||
c.recoveryRekeyConfig.VerificationNonce = nonce
|
||||
if ret != nil {
|
||||
ret.Nonce = nonce
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -89,7 +89,6 @@ type Seal interface {
|
|||
var (
|
||||
DefaultSealPretendsToAllowRecoveryKeys bool
|
||||
DefaultSealPretendsToAllowStoredShares bool
|
||||
DefaultSealPretendRecoveryConfig *SealConfig
|
||||
)
|
||||
|
||||
type defaultSeal struct {
|
||||
|
@ -229,15 +228,15 @@ func (d *defaultSeal) SetBarrierConfig(ctx context.Context, config *SealConfig)
|
|||
}
|
||||
|
||||
func (d *defaultSeal) RecoveryType() string {
|
||||
if DefaultSealPretendRecoveryConfig != nil {
|
||||
if DefaultSealPretendsToAllowRecoveryKeys {
|
||||
return RecoveryTypeShamir
|
||||
}
|
||||
return RecoveryTypeUnsupported
|
||||
}
|
||||
|
||||
func (d *defaultSeal) RecoveryConfig(ctx context.Context) (*SealConfig, error) {
|
||||
if DefaultSealPretendRecoveryConfig != nil {
|
||||
return DefaultSealPretendRecoveryConfig, nil
|
||||
if DefaultSealPretendsToAllowRecoveryKeys {
|
||||
return &SealConfig{}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("recovery not supported")
|
||||
}
|
||||
|
@ -349,6 +348,7 @@ func (s *SealConfig) Clone() *SealConfig {
|
|||
Backup: s.Backup,
|
||||
StoredShares: s.StoredShares,
|
||||
VerificationRequired: s.VerificationRequired,
|
||||
VerificationNonce: s.VerificationNonce,
|
||||
}
|
||||
if len(s.PGPKeys) > 0 {
|
||||
ret.PGPKeys = make([]string, len(s.PGPKeys))
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -724,21 +725,28 @@ func GenerateRandBytes(length int) ([]byte, error) {
|
|||
|
||||
func TestWaitActive(t testing.T, core *Core) {
|
||||
t.Helper()
|
||||
if err := TestWaitActiveWithError(core); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitActiveWithError(core *Core) error {
|
||||
start := time.Now()
|
||||
var standby bool
|
||||
var err error
|
||||
for time.Now().Sub(start) < time.Second {
|
||||
standby, err = core.Standby()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
return err
|
||||
}
|
||||
if !standby {
|
||||
break
|
||||
}
|
||||
}
|
||||
if standby {
|
||||
t.Fatalf("should not be in standby mode")
|
||||
return errors.New("should not be in standby mode")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TestCluster struct {
|
||||
|
@ -769,31 +777,39 @@ func (c *TestCluster) Start() {
|
|||
|
||||
// UnsealCores uses the cluster barrier keys to unseal the test cluster cores
|
||||
func (c *TestCluster) UnsealCores(t testing.T) {
|
||||
if err := c.UnsealCoresWithError(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TestCluster) UnsealCoresWithError() error {
|
||||
numCores := len(c.Cores)
|
||||
|
||||
// Unseal first core
|
||||
for _, key := range c.BarrierKeys {
|
||||
if _, err := c.Cores[0].Unseal(TestKeyCopy(key)); err != nil {
|
||||
t.Fatalf("unseal err: %s", err)
|
||||
return fmt.Errorf("unseal err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify unsealed
|
||||
sealed, err := c.Cores[0].Sealed()
|
||||
if err != nil {
|
||||
t.Fatalf("err checking seal status: %s", err)
|
||||
return fmt.Errorf("err checking seal status: %s", err)
|
||||
}
|
||||
if sealed {
|
||||
t.Fatal("should not be sealed")
|
||||
return fmt.Errorf("should not be sealed")
|
||||
}
|
||||
|
||||
TestWaitActive(t, c.Cores[0].Core)
|
||||
if err := TestWaitActiveWithError(c.Cores[0].Core); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unseal other cores
|
||||
for i := 1; i < numCores; i++ {
|
||||
for _, key := range c.BarrierKeys {
|
||||
if _, err := c.Cores[i].Core.Unseal(TestKeyCopy(key)); err != nil {
|
||||
t.Fatalf("unseal err: %s", err)
|
||||
return fmt.Errorf("unseal err: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -806,12 +822,14 @@ func (c *TestCluster) UnsealCores(t testing.T) {
|
|||
for i := 1; i < numCores; i++ {
|
||||
isLeader, _, _, err := c.Cores[i].Leader()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return err
|
||||
}
|
||||
if isLeader {
|
||||
t.Fatalf("core[%d] should not be leader", i)
|
||||
return fmt.Errorf("core[%d] should not be leader", i)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TestCluster) EnsureCoresSealed(t testing.T) {
|
||||
|
|
Loading…
Reference in New Issue