Unseal HA changes, CE side (#23192)

* Unseal HA changes, CE side

* Transit wrapper update
This commit is contained in:
Scott Miller
2023-09-20 10:58:12 -05:00
committed by GitHub
parent cb993931b3
commit c08b645d8f
12 changed files with 177 additions and 81 deletions

View File

@@ -1275,6 +1275,9 @@ func (c *ServerCommand) Run(args []string) int {
c.UI.Error(err.Error()) c.UI.Error(err.Error())
return 1 return 1
} }
if setSealResponse.sealConfigWarning != nil {
c.UI.Warn(fmt.Sprintf("Warnings during seal configuration: %v", setSealResponse.sealConfigWarning))
}
for _, seal := range setSealResponse.getCreatedSeals() { for _, seal := range setSealResponse.getCreatedSeals() {
seal := seal // capture range variable seal := seal // capture range variable
@@ -2557,7 +2560,8 @@ type SetSealResponse struct {
unwrapSeal vault.Seal unwrapSeal vault.Seal
// sealConfigError is present if there was an error configuring wrappers, other than KeyNotFound. // sealConfigError is present if there was an error configuring wrappers, other than KeyNotFound.
sealConfigError error sealConfigError error
sealConfigWarning error
} }
func (r *SetSealResponse) getCreatedSeals() []*vault.Seal { func (r *SetSealResponse) getCreatedSeals() []*vault.Seal {
@@ -2602,9 +2606,13 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma
} }
var sealConfigError error var sealConfigError error
var sealConfigWarning error
recordSealConfigError := func(err error) { recordSealConfigError := func(err error) {
sealConfigError = errors.Join(sealConfigError, err) sealConfigError = errors.Join(sealConfigError, err)
} }
recordSealConfigWarning := func(err error) {
sealConfigWarning = errors.Join(sealConfigWarning, err)
}
enabledSealWrappers := make([]*vaultseal.SealWrapper, 0) enabledSealWrappers := make([]*vaultseal.SealWrapper, 0)
disabledSealWrappers := make([]*vaultseal.SealWrapper, 0) disabledSealWrappers := make([]*vaultseal.SealWrapper, 0)
allSealKmsConfigs := make([]*configutil.KMS, 0) allSealKmsConfigs := make([]*configutil.KMS, 0)
@@ -2615,6 +2623,7 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma
} }
sealWrapperInfoKeysMap := make(map[string]infoKeysAndMap) sealWrapperInfoKeysMap := make(map[string]infoKeysAndMap)
configuredSeals := 0
for _, configSeal := range config.Seals { for _, configSeal := range config.Seals {
sealTypeEnvVarName := "VAULT_SEAL_TYPE" sealTypeEnvVarName := "VAULT_SEAL_TYPE"
if configSeal.Priority > 1 { if configSeal.Priority > 1 {
@@ -2628,24 +2637,18 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma
sealLogger := c.logger.ResetNamed(fmt.Sprintf("seal.%s", configSeal.Type)) sealLogger := c.logger.ResetNamed(fmt.Sprintf("seal.%s", configSeal.Type))
allSealKmsConfigs = append(allSealKmsConfigs, configSeal)
var wrapperInfoKeys []string var wrapperInfoKeys []string
wrapperInfoMap := map[string]string{} wrapperInfoMap := map[string]string{}
wrapper, wrapperConfigError := configutil.ConfigureWrapper(configSeal, &wrapperInfoKeys, &wrapperInfoMap, sealLogger) wrapper, wrapperConfigError := configutil.ConfigureWrapper(configSeal, &wrapperInfoKeys, &wrapperInfoMap, sealLogger)
if wrapperConfigError != nil { if wrapperConfigError == nil {
// It seems that we are checking for this particular error here is to distinguish between a // for some reason configureWrapper in kms.go returns nil wrapper and nil error for wrapping.WrapperTypeShamir
// mis-configured seal vs one that fails for another reason. Apparently the only other reason is if wrapper == nil {
// a key not found error. It seems the intention is for the key not found error to be returned wrapper = aeadwrapper.NewShamirWrapper()
// as a seal specific error later
if !errwrap.ContainsType(wrapperConfigError, new(logical.KeyNotFoundError)) {
return nil, fmt.Errorf("error parsing Seal configuration: %s", wrapperConfigError)
} else {
sealLogger.Error("error configuring seal", "name", configSeal.Name, "err", wrapperConfigError)
recordSealConfigError(wrapperConfigError)
} }
} configuredSeals++
// for some reason configureWrapper in kms.go returns nil wrapper and nil error for wrapping.WrapperTypeShamir } else {
if wrapper == nil && wrapperConfigError == nil { recordSealConfigWarning(fmt.Errorf("error configuring seal: %v", wrapperConfigError))
wrapper = aeadwrapper.NewShamirWrapper()
} }
sealWrapper := vaultseal.NewSealWrapper( sealWrapper := vaultseal.NewSealWrapper(
@@ -2654,6 +2657,7 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma
configSeal.Name, configSeal.Name,
configSeal.Type, configSeal.Type,
configSeal.Disabled, configSeal.Disabled,
wrapperConfigError == nil,
) )
if configSeal.Disabled { if configSeal.Disabled {
@@ -2661,7 +2665,6 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma
} else { } else {
enabledSealWrappers = append(enabledSealWrappers, sealWrapper) enabledSealWrappers = append(enabledSealWrappers, sealWrapper)
} }
allSealKmsConfigs = append(allSealKmsConfigs, configSeal)
sealWrapperInfoKeysMap[sealWrapper.Name] = infoKeysAndMap{ sealWrapperInfoKeysMap[sealWrapper.Name] = infoKeysAndMap{
keys: wrapperInfoKeys, keys: wrapperInfoKeys,
@@ -2669,6 +2672,12 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma
} }
} }
if len(enabledSealWrappers) == 0 && len(disabledSealWrappers) == 0 && sealConfigWarning != nil {
// All of them errored out, so warnings are now errors
recordSealConfigError(sealConfigWarning)
sealConfigWarning = nil
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Set the info keys, this modifies the function arguments `info` and `infoKeys` // Set the info keys, this modifies the function arguments `info` and `infoKeys`
// TODO(SEALHA): Why are we doing this? What is its use? // TODO(SEALHA): Why are we doing this? What is its use?
@@ -2724,10 +2733,11 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma
sealLogger := c.logger sealLogger := c.logger
switch { switch {
case len(enabledSealWrappers) == 0: case len(enabledSealWrappers) == 0:
return nil, errors.New("no enabled Seals in configuration") return nil, errors.Join(sealConfigWarning, errors.New("no enabled Seals in configuration"))
case configuredSeals == 0:
return nil, errors.Join(sealConfigWarning, errors.New("no seals were successfully initialized"))
case containsShamir(enabledSealWrappers) && containsShamir(disabledSealWrappers): case containsShamir(enabledSealWrappers) && containsShamir(disabledSealWrappers):
return nil, errors.New("shamir seals cannot be set disabled (they should simply not be set)") return nil, errors.Join(sealConfigWarning, errors.New("shamir seals cannot be set disabled (they should simply not be set)"))
case len(enabledSealWrappers) == 1 && containsShamir(enabledSealWrappers): case len(enabledSealWrappers) == 1 && containsShamir(enabledSealWrappers):
// The barrier seal is Shamir. If there are any disabled seals, then we put them all in the same // The barrier seal is Shamir. If there are any disabled seals, then we put them all in the same
@@ -2747,7 +2757,9 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma
// so just put enabled and disabled wrappers on the same seal Access // so just put enabled and disabled wrappers on the same seal Access
allSealWrappers := append(enabledSealWrappers, disabledSealWrappers...) allSealWrappers := append(enabledSealWrappers, disabledSealWrappers...)
barrierSeal = vault.NewAutoSeal(vaultseal.NewAccess(sealLogger, sealGenerationInfo, allSealWrappers)) barrierSeal = vault.NewAutoSeal(vaultseal.NewAccess(sealLogger, sealGenerationInfo, allSealWrappers))
if configuredSeals < len(enabledSealWrappers) {
c.UI.Warn("WARNING: running with fewer than all configured seals during unseal. Will not be fully highly available until errors are corrected and Vault restarted.")
}
case len(enabledSealWrappers) == 1: case len(enabledSealWrappers) == 1:
// We may have multiple seals disabled, but we know Shamir is not one of them. // We may have multiple seals disabled, but we know Shamir is not one of them.
barrierSeal = vault.NewAutoSeal(vaultseal.NewAccess(sealLogger, sealGenerationInfo, enabledSealWrappers)) barrierSeal = vault.NewAutoSeal(vaultseal.NewAccess(sealLogger, sealGenerationInfo, enabledSealWrappers))
@@ -2757,13 +2769,14 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma
default: default:
// We know there are multiple enabled seals and that the seal HA beta is not enabled // We know there are multiple enabled seals and that the seal HA beta is not enabled
return nil, errors.New("error: more than one enabled seal found") return nil, errors.Join(sealConfigWarning, errors.New("error: more than one enabled seal found"))
} }
return &SetSealResponse{ return &SetSealResponse{
barrierSeal: barrierSeal, barrierSeal: barrierSeal,
unwrapSeal: unwrapSeal, unwrapSeal: unwrapSeal,
sealConfigError: sealConfigError, sealConfigError: sealConfigError,
sealConfigWarning: sealConfigWarning,
}, nil }, nil
} }

2
go.mod
View File

@@ -93,7 +93,7 @@ require (
github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.8 github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.8
github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8 github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8
github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.7 github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.7
github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.7 github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.8
github.com/hashicorp/go-memdb v1.3.4 github.com/hashicorp/go-memdb v1.3.4
github.com/hashicorp/go-msgpack v1.1.5 github.com/hashicorp/go-msgpack v1.1.5
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1

4
go.sum
View File

@@ -1995,8 +1995,8 @@ github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8 h1:16I8OqBEuxZIo
github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8/go.mod h1:6QUMo5BrXAtbzSuZilqmx0A4px2u6PeFK7vfp2WIzeM= github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8/go.mod h1:6QUMo5BrXAtbzSuZilqmx0A4px2u6PeFK7vfp2WIzeM=
github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.7 h1:KeG3QGrbxbr2qAqCJdf3NR4ijAYwdcWLTmwSbR0yusM= github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.7 h1:KeG3QGrbxbr2qAqCJdf3NR4ijAYwdcWLTmwSbR0yusM=
github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.7/go.mod h1:rXxYzjjGw4HltEwxPp9zYSRIo6R+rBf1MSPk01bvodc= github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.7/go.mod h1:rXxYzjjGw4HltEwxPp9zYSRIo6R+rBf1MSPk01bvodc=
github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.7 h1:G25tZFw/LrAzJWxvS0/BFI7V1xAP/UsAIsgBwiE0mwo= github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.8 h1:uvdmC28xaqklqRQ3HWvq9HP4jX7Vy4M5JrJqAxfo5ig=
github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.7/go.mod h1:hxNA5oTfAvwPacWVg1axtF/lvTafwlAa6a6K4uzWHhw= github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.8/go.mod h1:zHX8LhkrU/M8sJkIQ85Dsrg7aqNEonY1+l6MlUC+6O4=
github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c=
github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg=
github.com/hashicorp/go-metrics v0.5.1 h1:rfPwUqFU6uZXNvGl4hzjY8LEBsqFVU4si1H9/Hqck/U= github.com/hashicorp/go-metrics v0.5.1 h1:rfPwUqFU6uZXNvGl4hzjY8LEBsqFVU4si1H9/Hqck/U=

View File

@@ -388,7 +388,17 @@ func GetOCIKMSKMSFunc(kms *KMS, opts ...wrapping.Option) (wrapping.Wrapper, map[
var GetTransitKMSFunc = func(kms *KMS, opts ...wrapping.Option) (wrapping.Wrapper, map[string]string, error) { var GetTransitKMSFunc = func(kms *KMS, opts ...wrapping.Option) (wrapping.Wrapper, map[string]string, error) {
wrapper := transit.NewWrapper() wrapper := transit.NewWrapper()
wrapperInfo, err := wrapper.SetConfig(context.Background(), append(opts, wrapping.WithDisallowEnvVars(true), wrapping.WithConfigMap(kms.Config))...) var prefix string
if p, ok := kms.Config["key_id_prefix"]; ok {
prefix = p
} else {
prefix = kms.Name
}
if !strings.HasSuffix(prefix, "/") {
prefix = prefix + "/"
}
wrapperInfo, err := wrapper.SetConfig(context.Background(), append(opts, wrapping.WithDisallowEnvVars(true), wrapping.WithConfigMap(kms.Config),
transit.WithKeyIdPrefix(prefix))...)
if err != nil { if err != nil {
// If the error is any other than logical.KeyNotFoundError, return the error // If the error is any other than logical.KeyNotFoundError, return the error
if !errwrap.ContainsType(err, new(logical.KeyNotFoundError)) { if !errwrap.ContainsType(err, new(logical.KeyNotFoundError)) {

View File

@@ -5027,7 +5027,7 @@ func (c *Core) GetSealBackendStatus(ctx context.Context) (*SealBackendStatusResp
if a, ok := c.seal.(*autoSeal); ok { if a, ok := c.seal.(*autoSeal); ok {
r.Healthy = c.seal.Healthy() r.Healthy = c.seal.Healthy()
var uhMin time.Time var uhMin time.Time
for _, sealWrapper := range a.GetAllSealWrappersByPriority() { for _, sealWrapper := range a.GetConfiguredSealWrappersByPriority() {
b := SealBackendStatus{ b := SealBackendStatus{
Name: sealWrapper.Name, Name: sealWrapper.Name,
Healthy: sealWrapper.IsHealthy(), Healthy: sealWrapper.IsHealthy(),

View File

@@ -6272,6 +6272,7 @@ func TestGetSealBackendStatus(t *testing.T) {
Wrapper: aeadwrapper.NewShamirWrapper(), Wrapper: aeadwrapper.NewShamirWrapper(),
SealConfigType: wrapping.WrapperTypeShamir.String(), SealConfigType: wrapping.WrapperTypeShamir.String(),
Priority: 1, Priority: 1,
Configured: true,
}, },
}, },
)) ))

View File

@@ -405,7 +405,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string)
if err != nil { if err != nil {
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to setup test seal: %w", err).Error()) return nil, logical.CodedError(http.StatusInternalServerError, fmt.Errorf("failed to setup test seal: %w", err).Error())
} }
access.GetAllSealWrappersByPriority()[0].Name = existingConfig.Name access.GetConfiguredSealWrappersByPriority()[0].Name = existingConfig.Name
testseal := NewDefaultSeal(access) testseal := NewDefaultSeal(access)
testseal.SetCore(c) testseal.SetCore(c)

View File

@@ -34,6 +34,11 @@ const (
StoredKeysSupportedShamirRoot StoredKeysSupportedShamirRoot
) )
var (
ErrUnconfiguredWrapper = errors.New("unconfigured wrapper")
ErrNoHealthySeals = errors.New("no healthy seals!")
)
func (s StoredKeysSupport) String() string { func (s StoredKeysSupport) String() string {
switch s { switch s {
case StoredKeysNotSupported: case StoredKeysNotSupported:
@@ -204,9 +209,12 @@ type Access interface {
SetShamirSealKey([]byte) error SetShamirSealKey([]byte) error
GetShamirKeyBytes(ctx context.Context) ([]byte, error) GetShamirKeyBytes(ctx context.Context) ([]byte, error)
// GetAllSealWrappersByPriority returns all the SealWrapper for all the seal wrappers, including disabled ones. // GetConfiguredSealWrappersByPriority returns all the SealWrappers including disabled and unconfigured wrappers.
GetAllSealWrappersByPriority() []*SealWrapper GetAllSealWrappersByPriority() []*SealWrapper
// GetConfiguredSealWrappersByPriority returns all the configured SealWrappers for all the seal wrappers, including disabled ones.
GetConfiguredSealWrappersByPriority() []*SealWrapper
// GetEnabledSealWrappersByPriority returns the SealWrapper for the enabled seal wrappers. // GetEnabledSealWrappersByPriority returns the SealWrapper for the enabled seal wrappers.
GetEnabledSealWrappersByPriority() []*SealWrapper GetEnabledSealWrappersByPriority() []*SealWrapper
@@ -274,46 +282,60 @@ func NewAccessFromSealWrappers(logger hclog.Logger, generation uint64, rewrapped
// The SealWrapper created uses the seal config type as the name, has priority set to 1 and the // The SealWrapper created uses the seal config type as the name, has priority set to 1 and the
// disabled flag set to false. // disabled flag set to false.
func NewAccessFromWrapper(logger hclog.Logger, wrapper wrapping.Wrapper, sealConfigType string) (Access, error) { func NewAccessFromWrapper(logger hclog.Logger, wrapper wrapping.Wrapper, sealConfigType string) (Access, error) {
sealWrapper := NewSealWrapper(wrapper, 1, sealConfigType, sealConfigType, false) sealWrapper := NewSealWrapper(wrapper, 1, sealConfigType, sealConfigType, false, true)
return NewAccessFromSealWrappers(logger, 1, true, []*SealWrapper{sealWrapper}) return NewAccessFromSealWrappers(logger, 1, true, []*SealWrapper{sealWrapper})
} }
func (a *access) GetAllSealWrappersByPriority() []*SealWrapper { func (a *access) GetAllSealWrappersByPriority() []*SealWrapper {
return a.filterSealWrappers(enabledAndDisabled, healthyAndUnhealthy) return a.filterSealWrappers(allWrappers)
}
func (a *access) GetConfiguredSealWrappersByPriority() []*SealWrapper {
return a.filterSealWrappers(configuredWrappers, allWrappers)
} }
func (a *access) GetEnabledSealWrappersByPriority() []*SealWrapper { func (a *access) GetEnabledSealWrappersByPriority() []*SealWrapper {
return a.filterSealWrappers(enabledOnly, healthyAndUnhealthy) return a.filterSealWrappers(configuredWrappers, enabledWrappers)
} }
func (a *access) AllSealWrappersHealthy() bool { func (a *access) AllSealWrappersHealthy() bool {
return len(a.wrappersByPriority) == len(a.filterSealWrappers(enabledAndDisabled, healthyOnly)) return len(a.wrappersByPriority) == len(a.filterSealWrappers(configuredWrappers, healthyWrappers))
} }
type ( type sealWrapperFilter func(*SealWrapper) bool
enabledFilter bool
healthyFilter bool
)
const ( func allWrappers(wrapper *SealWrapper) bool {
enabledOnly = enabledFilter(true) return true
enabledAndDisabled = !enabledOnly }
healthyOnly = healthyFilter(true)
healthyAndUnhealthy = !healthyOnly
)
func (a *access) filterSealWrappers(enabled enabledFilter, healthy healthyFilter) []*SealWrapper { func healthyWrappers(wrapper *SealWrapper) bool {
ret := make([]*SealWrapper, 0, len(a.wrappersByPriority)) return wrapper.IsHealthy()
for _, sw := range a.wrappersByPriority { }
switch {
case enabled == enabledOnly && sw.Disabled: func enabledWrappers(wrapper *SealWrapper) bool {
continue return !wrapper.Disabled
case healthy == healthyOnly && !sw.IsHealthy(): }
continue
default: func configuredWrappers(wrapper *SealWrapper) bool {
ret = append(ret, sw) return wrapper.Configured
}
// Returns a slice of wrappers who satisfy all filters
func (a *access) filterSealWrappers(filters ...sealWrapperFilter) []*SealWrapper {
return filterSealWrappers(a.wrappersByPriority, filters...)
}
func filterSealWrappers(wrappers []*SealWrapper, filters ...sealWrapperFilter) []*SealWrapper {
ret := make([]*SealWrapper, 0, len(wrappers))
outer:
for _, sw := range wrappers {
for _, f := range filters {
if !f(sw) {
continue outer
}
} }
ret = append(ret, sw)
} }
return ret return ret
} }
@@ -336,7 +358,7 @@ func (a *access) GetEnabledWrappers() []wrapping.Wrapper {
func (a *access) Init(ctx context.Context, options ...wrapping.Option) error { func (a *access) Init(ctx context.Context, options ...wrapping.Option) error {
var keyIds []string var keyIds []string
for _, sealWrapper := range a.GetAllSealWrappersByPriority() { for _, sealWrapper := range a.GetConfiguredSealWrappersByPriority() {
if initWrapper, ok := sealWrapper.Wrapper.(wrapping.InitFinalizer); ok { if initWrapper, ok := sealWrapper.Wrapper.(wrapping.InitFinalizer); ok {
if err := initWrapper.Init(ctx, options...); err != nil { if err := initWrapper.Init(ctx, options...); err != nil {
return err return err
@@ -390,11 +412,12 @@ const (
// Encrypt uses the underlying seal to encrypt the plaintext and returns it. // Encrypt uses the underlying seal to encrypt the plaintext and returns it.
func (a *access) Encrypt(ctx context.Context, plaintext []byte, options ...wrapping.Option) (*MultiWrapValue, map[string]error) { func (a *access) Encrypt(ctx context.Context, plaintext []byte, options ...wrapping.Option) (*MultiWrapValue, map[string]error) {
// Note that we do not encrypt with disabled wrappers. Disabled wrappers are only used to decrypt. // Note that we do not encrypt with disabled wrappers. Disabled wrappers are only used to decrypt.
enabledWrappersByPriority := a.filterSealWrappers(enabledOnly, healthyOnly) candidateWrappers := a.filterSealWrappers(enabledWrappers, healthyWrappers)
if len(enabledWrappersByPriority) == 0 { if len(candidateWrappers) == 0 {
// If all seals are unhealthy, try any way since a seal may have recovered // If all seals are unhealthy, try any way since a seal may have recovered
enabledWrappersByPriority = a.filterSealWrappers(enabledOnly, healthyAndUnhealthy) candidateWrappers = a.filterSealWrappers(enabledWrappers)
} }
enabledWrappersByPriority := filterSealWrappers(candidateWrappers, configuredWrappers)
type result struct { type result struct {
name string name string
@@ -474,6 +497,13 @@ GATHER_RESULTS:
a.keyIdSet.set(ret) a.keyIdSet.set(ret)
} }
// Add errors for unconfigured wrappers
for _, sw := range candidateWrappers {
if !sw.Configured {
errs[sw.Name] = ErrUnconfiguredWrapper
}
}
return ret, errs return ret, errs
} }
@@ -520,10 +550,13 @@ func (a *access) Decrypt(ctx context.Context, ciphertext *MultiWrapValue, option
return nil, false, err return nil, false, err
} }
wrappersByPriority := a.filterSealWrappers(enabledAndDisabled, healthyOnly) wrappersByPriority := a.filterSealWrappers(configuredWrappers, healthyWrappers)
if len(wrappersByPriority) == 0 { if len(wrappersByPriority) == 0 {
// If all seals are unhealthy, try any way since a seal may have recovered // If all seals are unhealthy, try any way since a seal may have recovered
wrappersByPriority = a.filterSealWrappers(enabledAndDisabled, healthyAndUnhealthy) wrappersByPriority = a.filterSealWrappers(configuredWrappers)
}
if len(wrappersByPriority) == 0 {
return nil, false, ErrNoHealthySeals
} }
type result struct { type result struct {
@@ -534,23 +567,45 @@ func (a *access) Decrypt(ctx context.Context, ciphertext *MultiWrapValue, option
} }
resultCh := make(chan *result) resultCh := make(chan *result)
decrypt := func(sealWrapper *SealWrapper) { reportResult := func(name string, plaintext []byte, oldKey bool, err error) {
pt, oldKey, err := a.tryDecrypt(ctx, sealWrapper, blobInfoMap, options)
resultCh <- &result{ resultCh <- &result{
name: sealWrapper.Name, name: name,
pt: pt, pt: plaintext,
oldKey: oldKey, oldKey: oldKey,
err: err, err: err,
} }
} }
decrypt := func(sealWrapper *SealWrapper) {
pt, oldKey, err := a.tryDecrypt(ctx, sealWrapper, blobInfoMap, options)
reportResult(sealWrapper.Name, pt, oldKey, err)
}
// Start goroutines to decrypt the value // Start goroutines to decrypt the value
for i, sealWrapper := range wrappersByPriority {
first := wrappersByPriority[0]
// First, if we only have one slot, try matching by keyId
if len(blobInfoMap) == 1 {
outer:
for k := range blobInfoMap {
for _, sealWrapper := range wrappersByPriority {
keyId, err := sealWrapper.Wrapper.KeyId(ctx)
if err != nil {
reportResult(sealWrapper.Name, nil, false, err)
continue
}
if keyId == k {
first = sealWrapper
break outer
}
}
}
}
go decrypt(first)
for _, sealWrapper := range wrappersByPriority {
sealWrapper := sealWrapper sealWrapper := sealWrapper
if i == 0 { if sealWrapper != first {
// start the highest priority wrapper right away
go decrypt(sealWrapper)
} else {
timer := time.AfterFunc(wrapperDecryptHighPriorityHeadStart, func() { timer := time.AfterFunc(wrapperDecryptHighPriorityHeadStart, func() {
decrypt(sealWrapper) decrypt(sealWrapper)
}) })
@@ -667,7 +722,7 @@ func JoinSealWrapErrors(msg string, errorMap map[string]error) error {
func (a *access) Finalize(ctx context.Context, options ...wrapping.Option) error { func (a *access) Finalize(ctx context.Context, options ...wrapping.Option) error {
var errs []error var errs []error
for _, w := range a.GetAllSealWrappersByPriority() { for _, w := range a.GetConfiguredSealWrappersByPriority() {
if finalizeWrapper, ok := w.Wrapper.(wrapping.InitFinalizer); ok { if finalizeWrapper, ok := w.Wrapper.(wrapping.InitFinalizer); ok {
if err := finalizeWrapper.Finalize(ctx, options...); err != nil { if err := finalizeWrapper.Finalize(ctx, options...); err != nil {
errs = append(errs, err) errs = append(errs, err)

View File

@@ -57,6 +57,7 @@ func NewTestSeal(opts *TestSealOpts) (Access, []*ToggleableWrapper) {
fmt.Sprintf("%s-%d", opts.Name, i+1), fmt.Sprintf("%s-%d", opts.Name, i+1),
wrapperType.String(), wrapperType.String(),
false, false,
true,
) )
} }
@@ -88,6 +89,7 @@ func NewToggleableTestSeal(opts *TestSealOpts) (Access, []func(error)) {
fmt.Sprintf("%s-%d", opts.Name, i+1), fmt.Sprintf("%s-%d", opts.Name, i+1),
wrapperType.String(), wrapperType.String(),
false, false,
true,
) )
funcs[i] = w.SetError funcs[i] = w.SetError
} }

View File

@@ -26,6 +26,9 @@ type SealWrapper struct {
// Disabled indicates, when true indicates that this wrapper should only be used for decryption. // Disabled indicates, when true indicates that this wrapper should only be used for decryption.
Disabled bool Disabled bool
// Configured indicates the wrapper was successfully configured at initialization
Configured bool
// hcLock protects lastHealthy, lastSeenHealthy, and healthy. // hcLock protects lastHealthy, lastSeenHealthy, and healthy.
// Do not modify those fields directly, use setHealth instead. // Do not modify those fields directly, use setHealth instead.
// Do not access these fields directly, use getHealth instead. // Do not access these fields directly, use getHealth instead.
@@ -35,16 +38,21 @@ type SealWrapper struct {
healthy bool healthy bool
} }
func NewSealWrapper(wrapper wrapping.Wrapper, priority int, name string, sealConfigType string, disabled bool) *SealWrapper { func NewSealWrapper(wrapper wrapping.Wrapper, priority int, name string, sealConfigType string, disabled bool, configured bool) *SealWrapper {
ret := &SealWrapper{ ret := &SealWrapper{
Wrapper: wrapper, Wrapper: wrapper,
Priority: priority, Priority: priority,
Name: name, Name: name,
SealConfigType: sealConfigType, SealConfigType: sealConfigType,
Disabled: disabled, Disabled: disabled,
Configured: configured,
} }
setHealth(ret, true, time.Now(), ret.lastHealthCheck) if configured {
setHealth(ret, true, time.Now(), ret.lastHealthCheck)
} else {
setHealth(ret, false, time.Now(), ret.lastHealthCheck)
}
return ret return ret
} }

View File

@@ -37,9 +37,10 @@ type autoSeal struct {
core *Core core *Core
logger log.Logger logger log.Logger
allSealsHealthy bool unsealedWithUnhealthySeal bool
hcLock sync.RWMutex allSealsHealthy bool
healthCheckStop chan struct{} hcLock sync.RWMutex
healthCheckStop chan struct{}
} }
// Ensure we are implementing the Seal interface // Ensure we are implementing the Seal interface
@@ -193,7 +194,7 @@ func (d *autoSeal) BarrierConfig(ctx context.Context) (*SealConfig, error) {
barrierTypeUpgradeCheck(d.BarrierSealConfigType(), conf) barrierTypeUpgradeCheck(d.BarrierSealConfigType(), conf)
if conf.Type != d.BarrierSealConfigType().String() { if conf.Type != d.BarrierSealConfigType().String() && conf.Type != "multiseal" {
d.logger.Error("barrier seal type does not match loaded type", "seal_type", conf.Type, "loaded_type", d.BarrierSealConfigType()) d.logger.Error("barrier seal type does not match loaded type", "seal_type", conf.Type, "loaded_type", d.BarrierSealConfigType())
return nil, fmt.Errorf("barrier seal type of %q does not match loaded type of %q", conf.Type, d.BarrierSealConfigType()) return nil, fmt.Errorf("barrier seal type of %q does not match loaded type of %q", conf.Type, d.BarrierSealConfigType())
} }
@@ -471,10 +472,16 @@ func (d *autoSeal) StartHealthCheck() {
defer cancel() defer cancel()
d.logger.Trace("performing a seal health check") d.logger.Trace("performing a seal health check")
for _, sw := range d.Access.GetAllSealWrappersByPriority() {
if !sw.Configured {
d.logger.Warn(`WARNING: running in degraded mode, one or more seals failed to configure at
startup, and Vault is running with fewer than the number of configured seals for high availability. Please correct the
error and restart Vault.`)
}
}
allHealthy := true allHealthy := true
allUnhealthy := true allUnhealthy := true
for _, sealWrapper := range d.Access.GetAllSealWrappersByPriority() { for _, sealWrapper := range d.Access.GetConfiguredSealWrappersByPriority() {
mLabels := []metrics.Label{{Name: "seal_wrapper_name", Value: sealWrapper.Name}} mLabels := []metrics.Label{{Name: "seal_wrapper_name", Value: sealWrapper.Name}}
wasHealthy := sealWrapper.IsHealthy() wasHealthy := sealWrapper.IsHealthy()

View File

@@ -18,7 +18,7 @@ func NewTestSeal(t testing.T, opts *seal.TestSealOpts) Seal {
switch opts.StoredKeys { switch opts.StoredKeys {
case seal.StoredKeysSupportedShamirRoot: case seal.StoredKeysSupportedShamirRoot:
sealAccess, err := seal.NewAccessFromSealWrappers(logger, opts.Generation, true, []*seal.SealWrapper{ sealAccess, err := seal.NewAccessFromSealWrappers(logger, opts.Generation, true, []*seal.SealWrapper{
seal.NewSealWrapper(aead.NewShamirWrapper(), 1, "shamir", "shamir", false), seal.NewSealWrapper(aead.NewShamirWrapper(), 1, "shamir", "shamir", false, true),
}) })
if err != nil { if err != nil {
t.Fatal("error creating test seal", err) t.Fatal("error creating test seal", err)
@@ -33,7 +33,7 @@ func NewTestSeal(t testing.T, opts *seal.TestSealOpts) Seal {
return newSeal return newSeal
case seal.StoredKeysNotSupported: case seal.StoredKeysNotSupported:
sealAccess, err := seal.NewAccessFromSealWrappers(logger, opts.Generation, true, []*seal.SealWrapper{ sealAccess, err := seal.NewAccessFromSealWrappers(logger, opts.Generation, true, []*seal.SealWrapper{
seal.NewSealWrapper(aead.NewShamirWrapper(), 1, "shamir", "shamir", false), seal.NewSealWrapper(aead.NewShamirWrapper(), 1, "shamir", "shamir", false, true),
}) })
if err != nil { if err != nil {
t.Fatal("error creating test seal", err) t.Fatal("error creating test seal", err)