diff --git a/vault/barrier.go b/vault/barrier.go index c2a04cd0a1..b6eeff056b 100644 --- a/vault/barrier.go +++ b/vault/barrier.go @@ -31,6 +31,18 @@ const ( // keyringPath is the location of the keyring data. This is encrypted // by the master key. keyringPath = "core/keyring" + + // keyringUpgradePrefix is the path used to store keyring update entries. + // When running in HA mode, the active instance will install the new key + // and re-write the keyring. For standby instances, they need an upgrade + // path from key N to N+1. They cannot just use the master key because + // in the event of a rekey, that master key can no longer decrypt the keyring. + // When key N+1 is installed, we create an entry at "prefix/N" which uses + // encryption key N to provide the N+1 key. The standby instances scan + // for this periodically and refresh their keyring. The upgrade keys + // are deleted after a few minutes, but this provides enough time for the + // standby instances to upgrade without causing any disruption. + keyringUpgradePrefix = "core/upgrade/" ) // SecurityBarrier is a critical component of Vault. It is used to wrap @@ -73,6 +85,9 @@ type SecurityBarrier interface { // should use the new key, while old values should still be decryptable. Rotate() error + // AddKey is used to add a new key to the keyring + AddKey(k *Key) error + // ActiveKeyInfo is used to inform details about the active key ActiveKeyInfo() (*KeyInfo, error) diff --git a/vault/barrier_aes_gcm.go b/vault/barrier_aes_gcm.go index ca42b964e6..3e1de04805 100644 --- a/vault/barrier_aes_gcm.go +++ b/vault/barrier_aes_gcm.go @@ -335,6 +335,20 @@ func (b *AESGCMBarrier) Rotate() error { return nil } +// AddKey is used to add a new key to the keyring. This assumes the keyring +// has already been updated and does not persist a new keyring. +func (b *AESGCMBarrier) AddKey(k *Key) error { + b.l.Lock() + defer b.l.Unlock() + + newKeyring, err := b.keyring.AddKey(k) + if err != nil { + return fmt.Errorf("failed to add new encryption key: %v", err) + } + b.keyring = newKeyring + return nil +} + // ActiveKeyInfo is used to inform details about the active key func (b *AESGCMBarrier) ActiveKeyInfo() (*KeyInfo, error) { b.l.RLock() diff --git a/vault/barrier_test.go b/vault/barrier_test.go index a5410268e4..742bdf7089 100644 --- a/vault/barrier_test.go +++ b/vault/barrier_test.go @@ -335,6 +335,28 @@ func testBarrier_Rotate(t *testing.T, b SecurityBarrier) { if out == nil { t.Fatalf("bad: %v", out) } + + // Attempt to do AddKey + randKey, _ := b.GenerateKey() + newKey := &Key{ + Term: 3, + Version: 1, + Value: randKey, + InstallTime: time.Now(), + } + err = b.AddKey(newKey) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Check the key info + info, err = b.ActiveKeyInfo() + if err != nil { + t.Fatalf("err: %v", err) + } + if info.Term != 3 { + t.Fatalf("Bad term: %d", info.Term) + } } func testBarrier_Rekey(t *testing.T, b SecurityBarrier) {