201 lines
7.9 KiB
Go
201 lines
7.9 KiB
Go
|
// Copyright (c) HashiCorp, Inc.
|
||
|
// SPDX-License-Identifier: MPL-2.0
|
||
|
|
||
|
package vault
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"errors"
|
||
|
"io"
|
||
|
"time"
|
||
|
|
||
|
"github.com/hashicorp/vault/sdk/logical"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// ErrBarrierSealed is returned if an operation is performed on
|
||
|
// a sealed barrier. No operation is expected to succeed before unsealing
|
||
|
ErrBarrierSealed = errors.New("Vault is sealed")
|
||
|
|
||
|
// ErrBarrierAlreadyInit is returned if the barrier is already
|
||
|
// initialized. This prevents a re-initialization.
|
||
|
ErrBarrierAlreadyInit = errors.New("Vault is already initialized")
|
||
|
|
||
|
// ErrBarrierNotInit is returned if a non-initialized barrier
|
||
|
// is attempted to be unsealed.
|
||
|
ErrBarrierNotInit = errors.New("Vault is not initialized")
|
||
|
|
||
|
// ErrBarrierInvalidKey is returned if the Unseal key is invalid
|
||
|
ErrBarrierInvalidKey = errors.New("Unseal failed, invalid key")
|
||
|
|
||
|
// ErrPlaintextTooLarge is returned if a plaintext is offered for encryption
|
||
|
// that is too large to encrypt in memory
|
||
|
ErrPlaintextTooLarge = errors.New("plaintext value too large")
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// barrierInitPath is the path used to store our init sentinel file
|
||
|
barrierInitPath = "barrier/init"
|
||
|
|
||
|
// keyringPath is the location of the keyring data. This is encrypted
|
||
|
// by the root key.
|
||
|
keyringPath = "core/keyring"
|
||
|
keyringPrefix = "core/"
|
||
|
|
||
|
// 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 root key because
|
||
|
// in the event of a rekey, that root 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/"
|
||
|
|
||
|
// rootKeyPath is the location of the root 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 root key. This key can be decrypted if you have the
|
||
|
// keyring to discover the new root key. The new root key is then
|
||
|
// used to reload the keyring itself.
|
||
|
rootKeyPath = "core/master"
|
||
|
|
||
|
// shamirKekPath is used with Shamir in v1.3+ to store a copy of the
|
||
|
// unseal key behind the barrier. As with rootKeyPath this is primarily
|
||
|
// used by standbys to handle rekeys. It also comes into play when restoring
|
||
|
// raft snapshots.
|
||
|
shamirKekPath = "core/shamir-kek"
|
||
|
)
|
||
|
|
||
|
// SecurityBarrier is a critical component of Vault. It is used to wrap
|
||
|
// an untrusted physical backend and provide a single point of encryption,
|
||
|
// decryption and checksum verification. The goal is to ensure that any
|
||
|
// data written to the barrier is confidential and that integrity is preserved.
|
||
|
// As a real-world analogy, this is the steel and concrete wrapper around
|
||
|
// a Vault. The barrier should only be Unlockable given its key.
|
||
|
type SecurityBarrier interface {
|
||
|
// Initialized checks if the barrier has been initialized
|
||
|
// and has a root key set.
|
||
|
Initialized(ctx context.Context) (bool, error)
|
||
|
|
||
|
// Initialize works only if the barrier has not been initialized
|
||
|
// and makes use of the given root key. When sealKey is provided
|
||
|
// it's because we're using a new-style Shamir seal, and rootKey
|
||
|
// is to be stored using sealKey to encrypt it.
|
||
|
Initialize(ctx context.Context, rootKey []byte, sealKey []byte, random io.Reader) error
|
||
|
|
||
|
// GenerateKey is used to generate a new key
|
||
|
GenerateKey(io.Reader) ([]byte, error)
|
||
|
|
||
|
// KeyLength is used to sanity check a key
|
||
|
KeyLength() (int, int)
|
||
|
|
||
|
// Sealed checks if the barrier has been unlocked yet. The Barrier
|
||
|
// is not expected to be able to perform any CRUD until it is unsealed.
|
||
|
Sealed() (bool, error)
|
||
|
|
||
|
// Unseal is used to provide the unseal key which permits the barrier
|
||
|
// to be unsealed. If the key is not correct, the barrier remains sealed.
|
||
|
Unseal(ctx context.Context, key []byte) error
|
||
|
|
||
|
// VerifyRoot is used to check if the given key matches the root key
|
||
|
VerifyRoot(key []byte) error
|
||
|
|
||
|
// SetRootKey is used to directly set a new root key. This is used in
|
||
|
// replicated scenarios due to the chicken and egg problem of reloading the
|
||
|
// keyring from disk before we have the root key to decrypt it.
|
||
|
SetRootKey(key []byte) error
|
||
|
|
||
|
// ReloadKeyring is used to re-read the underlying keyring.
|
||
|
// This is used for HA deployments to ensure the latest keyring
|
||
|
// is present in the leader.
|
||
|
ReloadKeyring(ctx context.Context) error
|
||
|
|
||
|
// ReloadRootKey is used to re-read the underlying root key.
|
||
|
// This is used for HA deployments to ensure the latest root key
|
||
|
// is available for keyring reloading.
|
||
|
ReloadRootKey(ctx context.Context) error
|
||
|
|
||
|
// Seal is used to re-seal the barrier. This requires the barrier to
|
||
|
// be unsealed again to perform any further operations.
|
||
|
Seal() error
|
||
|
|
||
|
// Rotate is used to create a new encryption key. All future writes
|
||
|
// should use the new key, while old values should still be decryptable.
|
||
|
Rotate(ctx context.Context, reader io.Reader) (uint32, error)
|
||
|
|
||
|
// CreateUpgrade creates an upgrade path key to the given term from the previous term
|
||
|
CreateUpgrade(ctx context.Context, term uint32) error
|
||
|
|
||
|
// DestroyUpgrade destroys the upgrade path key to the given term
|
||
|
DestroyUpgrade(ctx context.Context, term uint32) error
|
||
|
|
||
|
// CheckUpgrade looks for an upgrade to the current term and installs it
|
||
|
CheckUpgrade(ctx context.Context) (bool, uint32, error)
|
||
|
|
||
|
// ActiveKeyInfo is used to inform details about the active key
|
||
|
ActiveKeyInfo() (*KeyInfo, error)
|
||
|
|
||
|
// RotationConfig returns the auto-rotation config for the barrier key
|
||
|
RotationConfig() (KeyRotationConfig, error)
|
||
|
|
||
|
// SetRotationConfig updates the auto-rotation config for the barrier key
|
||
|
SetRotationConfig(ctx context.Context, config KeyRotationConfig) error
|
||
|
|
||
|
// Rekey is used to change the master key used to protect the keyring
|
||
|
Rekey(context.Context, []byte) error
|
||
|
|
||
|
// For replication we must send over the keyring, so this must be available
|
||
|
Keyring() (*Keyring, error)
|
||
|
|
||
|
// For encryption count shipping, a function which handles updating local encryption counts if the consumer succeeds.
|
||
|
// This isolates the barrier code from the replication system
|
||
|
ConsumeEncryptionCount(consumer func(int64) error) error
|
||
|
|
||
|
// Add encryption counts from a remote source (downstream cluster node)
|
||
|
AddRemoteEncryptions(encryptions int64)
|
||
|
|
||
|
// Check whether an automatic rotation is due
|
||
|
CheckBarrierAutoRotate(ctx context.Context) (string, error)
|
||
|
|
||
|
// SecurityBarrier must provide the storage APIs
|
||
|
logical.Storage
|
||
|
|
||
|
// SecurityBarrier must provide the encryption APIs
|
||
|
BarrierEncryptor
|
||
|
}
|
||
|
|
||
|
// BarrierStorage is the storage only interface required for a Barrier.
|
||
|
type BarrierStorage interface {
|
||
|
// Put is used to insert or update an entry
|
||
|
Put(ctx context.Context, entry *logical.StorageEntry) error
|
||
|
|
||
|
// Get is used to fetch an entry
|
||
|
Get(ctx context.Context, key string) (*logical.StorageEntry, error)
|
||
|
|
||
|
// Delete is used to permanently delete an entry
|
||
|
Delete(ctx context.Context, key string) error
|
||
|
|
||
|
// List is used ot list all the keys under a given
|
||
|
// prefix, up to the next prefix.
|
||
|
List(ctx context.Context, prefix string) ([]string, error)
|
||
|
}
|
||
|
|
||
|
// BarrierEncryptor is the in memory only interface that does not actually
|
||
|
// use the underlying barrier. It is used for lower level modules like the
|
||
|
// Write-Ahead-Log and Merkle index to allow them to use the barrier.
|
||
|
type BarrierEncryptor interface {
|
||
|
Encrypt(ctx context.Context, key string, plaintext []byte) ([]byte, error)
|
||
|
Decrypt(ctx context.Context, key string, ciphertext []byte) ([]byte, error)
|
||
|
}
|
||
|
|
||
|
// KeyInfo is used to convey information about the encryption key
|
||
|
type KeyInfo struct {
|
||
|
Term int
|
||
|
InstallTime time.Time
|
||
|
Encryptions int64
|
||
|
}
|