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
|
// are deleted after a few minutes, but this provides enough time for the
|
||||||
// standby instances to upgrade without causing any disruption.
|
// standby instances to upgrade without causing any disruption.
|
||||||
keyringUpgradePrefix = "core/upgrade/"
|
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
|
// SecurityBarrier is a critical component of Vault. It is used to wrap
|
||||||
|
@ -82,6 +91,11 @@ type SecurityBarrier interface {
|
||||||
// is present in the leader.
|
// is present in the leader.
|
||||||
ReloadKeyring() error
|
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
|
// Seal is used to re-seal the barrier. This requires the barrier to
|
||||||
// be unsealed again to perform any further operations.
|
// be unsealed again to perform any further operations.
|
||||||
Seal() error
|
Seal() error
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package vault
|
package vault
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
@ -150,6 +151,35 @@ func (b *AESGCMBarrier) persistKeyring(keyring *Keyring) error {
|
||||||
if err := b.backend.Put(pe); err != nil {
|
if err := b.backend.Put(pe); err != nil {
|
||||||
return fmt.Errorf("failed to persist keyring: %v", err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +257,42 @@ func (b *AESGCMBarrier) ReloadKeyring() error {
|
||||||
return nil
|
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
|
// 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.
|
// to be unsealed. If the key is not correct, the barrier remains sealed.
|
||||||
func (b *AESGCMBarrier) Unseal(key []byte) error {
|
func (b *AESGCMBarrier) Unseal(key []byte) error {
|
||||||
|
|
|
@ -54,6 +54,19 @@ func TestAESGCMBarrier_Upgrade(t *testing.T) {
|
||||||
testBarrier_Upgrade(t, b1, b2)
|
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) {
|
func TestAESGCMBarrier_Rekey(t *testing.T) {
|
||||||
inm := physical.NewInmem()
|
inm := physical.NewInmem()
|
||||||
b, err := NewAESGCMBarrier(inm)
|
b, err := NewAESGCMBarrier(inm)
|
||||||
|
|
|
@ -496,3 +496,36 @@ func testBarrier_Upgrade(t *testing.T, b1, b2 SecurityBarrier) {
|
||||||
t.Fatalf("should not have upgrade")
|
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