open-vault/vault/seal_autoseal_test.go
Michael Gaffney 6b1e1909e9
core: re-encrypt barrier and recovery keys if the unseal key is updated (#7493)
Seal keys can be rotated. When this happens, the barrier and recovery
keys should be re-encrypted with the new seal key. This change
automatically re-encrypts the barrier and recovery keys with the latest
seal key on the active node during the 'postUnseal' phase.
2019-10-03 16:40:18 -04:00

159 lines
4.6 KiB
Go

package vault
import (
"bytes"
"context"
"reflect"
"testing"
proto "github.com/golang/protobuf/proto"
"github.com/hashicorp/vault/sdk/physical"
"github.com/hashicorp/vault/vault/seal"
)
// phy implements physical.Backend. It maps keys to a slice of entries.
// Each call to Put appends the entry to the slice of entries for that
// key. No deduplication is done. This allows the test for UpgradeKeys to
// verify entries are only being updated when the underlying encryption key
// has been updated.
type phy struct {
t *testing.T
entries map[string][]*physical.Entry
}
var _ physical.Backend = (*phy)(nil)
func newTestBackend(t *testing.T) *phy {
return &phy{
t: t,
entries: make(map[string][]*physical.Entry),
}
}
func (p *phy) Put(_ context.Context, entry *physical.Entry) error {
p.entries[entry.Key] = append(p.entries[entry.Key], entry)
return nil
}
func (p *phy) Get(_ context.Context, key string) (*physical.Entry, error) {
entries := p.entries[key]
if entries == nil {
return nil, nil
}
return entries[len(entries)-1], nil
}
func (p *phy) Delete(_ context.Context, key string) error {
p.t.Errorf("Delete called on phy: key: %v", key)
return nil
}
func (p *phy) List(_ context.Context, prefix string) ([]string, error) {
p.t.Errorf("List called on phy: prefix: %v", prefix)
return []string{}, nil
}
func (p *phy) Len() int {
return len(p.entries)
}
func TestAutoSeal_UpgradeKeys(t *testing.T) {
core, _, _ := TestCoreUnsealed(t)
testSeal := seal.NewTestSeal(nil)
var encKeys []string
changeKey := func(key string) {
encKeys = append(encKeys, key)
testSeal.SetKeyID(key)
}
// Set initial encryption key.
changeKey("kaz")
autoSeal := NewAutoSeal(testSeal)
autoSeal.SetCore(core)
pBackend := newTestBackend(t)
core.physical = pBackend
ctx := context.Background()
inkeys := [][]byte{[]byte("grist"), []byte("house")}
if err := autoSeal.SetStoredKeys(ctx, inkeys); err != nil {
t.Fatalf("SetStoredKeys: want no error, got %v", err)
}
inRecoveryKey := []byte("falernum")
if err := autoSeal.SetRecoveryKey(ctx, inRecoveryKey); err != nil {
t.Fatalf("SetRecoveryKey: want no error, got %v", err)
}
check := func() {
// The values of the stored keys should never change.
outkeys, err := autoSeal.GetStoredKeys(ctx)
if err != nil {
t.Fatalf("GetStoredKeys: want no error, got %v", err)
}
if !reflect.DeepEqual(inkeys, outkeys) {
t.Errorf("incorrect stored keys: want %v, got %v", inkeys, outkeys)
}
// The value of the recovery key should also never change.
outRecoveryKey, err := autoSeal.RecoveryKey(ctx)
if err != nil {
t.Fatalf("RecoveryKey: want no error, got %v", err)
}
if !bytes.Equal(inRecoveryKey, outRecoveryKey) {
t.Errorf("incorrect recovery key: want %q, got %q", inRecoveryKey, outRecoveryKey)
}
// There should only be 2 entries in the physical backend. One for
// the stored keys and one for the recovery key.
if want, got := 2, pBackend.Len(); want != got {
t.Errorf("backend unexpected Len: want %d, got %d", want, got)
}
for phyKey, phyEntries := range pBackend.entries {
// Calling UpgradeKeys should only add an entry if the key has
// changed.
if keyCount, entryCount := len(encKeys), len(phyEntries); keyCount != entryCount {
t.Errorf("phyKey = %s: encryption key count not equal to entry count: keys=%d, entries=%d", phyKey, keyCount, entryCount)
}
// Each phyEntry should correspond to a key at the same index
// in encKeys. Iterate over each phyEntry and verify it was
// encrypted with its corresponding key in encKeys.
for i, phyEntry := range phyEntries {
blobInfo := &physical.EncryptedBlobInfo{}
if err := proto.Unmarshal(phyEntry.Value, blobInfo); err != nil {
t.Errorf("phyKey = %s: failed to proto decode stored keys: %s", phyKey, err)
}
if blobInfo.KeyInfo == nil {
t.Errorf("phyKey = %s: KeyInfo missing: %+v", phyKey, blobInfo)
}
if want, got := encKeys[i], blobInfo.KeyInfo.KeyID; want != got {
t.Errorf("phyKey = %s: Incorrect encryption key: want %s, got %s", phyKey, want, got)
}
}
}
}
// Verify the current state is correct before calling UpgradeKeys.
check()
// Call UpgradeKeys before changing the encryption key and verify
// nothing has changed.
if err := autoSeal.UpgradeKeys(ctx); err != nil {
t.Fatalf("UpgradeKeys: want no error, got %v", err)
}
check()
// Change the encryption key, call UpgradeKeys, then verify the stored
// keys and recovery key has been re-encrypted with the new encryption
// key.
changeKey("primanti")
if err := autoSeal.UpgradeKeys(ctx); err != nil {
t.Fatalf("UpgradeKeys: want no error, got %v", err)
}
check()
}