vault: adding ability to reload master key
This commit is contained in:
parent
716f8d9979
commit
f6729b29f8
|
@ -43,6 +43,15 @@ const (
|
|||
// are deleted after a few minutes, but this provides enough time for the
|
||||
// standby instances to upgrade without causing any disruption.
|
||||
keyringUpgradePrefix = "core/upgrade/"
|
||||
|
||||
// masterKeyPath is the location of the master key. This is encrypted
|
||||
// by the latest key in the keyring. This is only used by standby instances
|
||||
// to handle the case of a rekey. If the active instance does a rekey,
|
||||
// the standby instances can no longer reload the keyring since they
|
||||
// have the old master key. This key can be decrypted if you have the
|
||||
// keyring to discover the new master key. The new master key is then
|
||||
// used to reload the keyring itself.
|
||||
masterKeyPath = "core/master"
|
||||
)
|
||||
|
||||
// SecurityBarrier is a critical component of Vault. It is used to wrap
|
||||
|
@ -82,6 +91,11 @@ type SecurityBarrier interface {
|
|||
// is present in the leader.
|
||||
ReloadKeyring() error
|
||||
|
||||
// ReloadMasterKey is used to re-read the underlying masterkey.
|
||||
// This is used for HA deployments to ensure the latest master key
|
||||
// is available for keyring reloading.
|
||||
ReloadMasterKey() error
|
||||
|
||||
// Seal is used to re-seal the barrier. This requires the barrier to
|
||||
// be unsealed again to perform any further operations.
|
||||
Seal() error
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
|
@ -150,6 +151,35 @@ func (b *AESGCMBarrier) persistKeyring(keyring *Keyring) error {
|
|||
if err := b.backend.Put(pe); err != nil {
|
||||
return fmt.Errorf("failed to persist keyring: %v", err)
|
||||
}
|
||||
|
||||
// Serialize the master key value
|
||||
key := &Key{
|
||||
Term: 1,
|
||||
Version: 1,
|
||||
Value: keyring.MasterKey(),
|
||||
}
|
||||
buf, err = key.Serialize()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize master key: %v", err)
|
||||
}
|
||||
defer memzero(buf)
|
||||
|
||||
// Encrypt the master key
|
||||
activeKey := keyring.ActiveKey()
|
||||
aead, err := b.aeadFromKey(activeKey.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value = b.encrypt(activeKey.Term, aead, buf)
|
||||
|
||||
// Update the masterKeyPath for standby instances
|
||||
pe = &physical.Entry{
|
||||
Key: masterKeyPath,
|
||||
Value: value,
|
||||
}
|
||||
if err := b.backend.Put(pe); err != nil {
|
||||
return fmt.Errorf("failed to persist master key: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -227,6 +257,42 @@ func (b *AESGCMBarrier) ReloadKeyring() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ReloadMasterKey is used to re-read the underlying masterkey.
|
||||
// This is used for HA deployments to ensure the latest master key
|
||||
// is available for keyring reloading.
|
||||
func (b *AESGCMBarrier) ReloadMasterKey() error {
|
||||
// Read the masterKeyPath upgrade
|
||||
out, err := b.Get(masterKeyPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read master key path: %v", err)
|
||||
}
|
||||
|
||||
// The masterKeyPath could be missing (backwards incompatable),
|
||||
// we can ignore this and attempt to make progress with the current
|
||||
// master key.
|
||||
if out == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deserialize the master key
|
||||
key, err := DeserializeKey(out.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deserialize key: %v", err)
|
||||
}
|
||||
|
||||
b.l.Lock()
|
||||
defer b.l.Unlock()
|
||||
|
||||
// Check if the master key is the same
|
||||
if bytes.Equal(b.keyring.MasterKey(), key.Value) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update the master key
|
||||
b.keyring = b.keyring.SetMasterKey(key.Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unseal is used to provide the master key which permits the barrier
|
||||
// to be unsealed. If the key is not correct, the barrier remains sealed.
|
||||
func (b *AESGCMBarrier) Unseal(key []byte) error {
|
||||
|
|
|
@ -54,6 +54,19 @@ func TestAESGCMBarrier_Upgrade(t *testing.T) {
|
|||
testBarrier_Upgrade(t, b1, b2)
|
||||
}
|
||||
|
||||
func TestAESGCMBarrier_Upgrade_Rekey(t *testing.T) {
|
||||
inm := physical.NewInmem()
|
||||
b1, err := NewAESGCMBarrier(inm)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
b2, err := NewAESGCMBarrier(inm)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
testBarrier_Upgrade_Rekey(t, b1, b2)
|
||||
}
|
||||
|
||||
func TestAESGCMBarrier_Rekey(t *testing.T) {
|
||||
inm := physical.NewInmem()
|
||||
b, err := NewAESGCMBarrier(inm)
|
||||
|
|
|
@ -496,3 +496,36 @@ func testBarrier_Upgrade(t *testing.T, b1, b2 SecurityBarrier) {
|
|||
t.Fatalf("should not have upgrade")
|
||||
}
|
||||
}
|
||||
|
||||
func testBarrier_Upgrade_Rekey(t *testing.T, b1, b2 SecurityBarrier) {
|
||||
// Initialize the barrier
|
||||
key, _ := b1.GenerateKey()
|
||||
b1.Initialize(key)
|
||||
err := b1.Unseal(key)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
err = b2.Unseal(key)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Rekey to a new key
|
||||
newKey, _ := b1.GenerateKey()
|
||||
err = b1.Rekey(newKey)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Reload the master key
|
||||
err = b2.ReloadMasterKey()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Reload the keyring
|
||||
err = b2.ReloadKeyring()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue