From 28fe4283e6bda67371fc65a1f84ce24c93b25b05 Mon Sep 17 00:00:00 2001 From: Scott Miller Date: Wed, 20 Sep 2023 13:45:38 -0500 Subject: [PATCH] Unseal HA changes, CE side (#23192) (#23196) * Unseal HA changes, CE side * Transit wrapper update --- command/server.go | 61 ++++++++------ go.mod | 2 +- go.sum | 4 +- internalshared/configutil/kms.go | 12 ++- vault/logical_system.go | 2 +- vault/logical_system_test.go | 1 + vault/rekey.go | 2 +- vault/seal/seal.go | 137 ++++++++++++++++++++++--------- vault/seal/seal_testing.go | 2 + vault/seal/seal_wrapper.go | 12 ++- vault/seal_autoseal.go | 19 +++-- vault/seal_testing_util.go | 4 +- 12 files changed, 177 insertions(+), 81 deletions(-) diff --git a/command/server.go b/command/server.go index 1c88693437..c921a95f34 100644 --- a/command/server.go +++ b/command/server.go @@ -1275,6 +1275,9 @@ func (c *ServerCommand) Run(args []string) int { c.UI.Error(err.Error()) return 1 } + if setSealResponse.sealConfigWarning != nil { + c.UI.Warn(fmt.Sprintf("Warnings during seal configuration: %v", setSealResponse.sealConfigWarning)) + } for _, seal := range setSealResponse.getCreatedSeals() { seal := seal // capture range variable @@ -2557,7 +2560,8 @@ type SetSealResponse struct { unwrapSeal vault.Seal // 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 { @@ -2602,9 +2606,13 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma } var sealConfigError error + var sealConfigWarning error recordSealConfigError := func(err error) { sealConfigError = errors.Join(sealConfigError, err) } + recordSealConfigWarning := func(err error) { + sealConfigWarning = errors.Join(sealConfigWarning, err) + } enabledSealWrappers := make([]*vaultseal.SealWrapper, 0) disabledSealWrappers := make([]*vaultseal.SealWrapper, 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) + configuredSeals := 0 for _, configSeal := range config.Seals { sealTypeEnvVarName := "VAULT_SEAL_TYPE" 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)) + allSealKmsConfigs = append(allSealKmsConfigs, configSeal) var wrapperInfoKeys []string wrapperInfoMap := map[string]string{} wrapper, wrapperConfigError := configutil.ConfigureWrapper(configSeal, &wrapperInfoKeys, &wrapperInfoMap, sealLogger) - if wrapperConfigError != nil { - // It seems that we are checking for this particular error here is to distinguish between a - // mis-configured seal vs one that fails for another reason. Apparently the only other reason is - // a key not found error. It seems the intention is for the key not found error to be returned - // 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) + if wrapperConfigError == nil { + // for some reason configureWrapper in kms.go returns nil wrapper and nil error for wrapping.WrapperTypeShamir + if wrapper == nil { + wrapper = aeadwrapper.NewShamirWrapper() } - } - // for some reason configureWrapper in kms.go returns nil wrapper and nil error for wrapping.WrapperTypeShamir - if wrapper == nil && wrapperConfigError == nil { - wrapper = aeadwrapper.NewShamirWrapper() + configuredSeals++ + } else { + recordSealConfigWarning(fmt.Errorf("error configuring seal: %v", wrapperConfigError)) } sealWrapper := vaultseal.NewSealWrapper( @@ -2654,6 +2657,7 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma configSeal.Name, configSeal.Type, configSeal.Disabled, + wrapperConfigError == nil, ) if configSeal.Disabled { @@ -2661,7 +2665,6 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma } else { enabledSealWrappers = append(enabledSealWrappers, sealWrapper) } - allSealKmsConfigs = append(allSealKmsConfigs, configSeal) sealWrapperInfoKeysMap[sealWrapper.Name] = infoKeysAndMap{ 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` // 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 switch { 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): - 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): // 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 allSealWrappers := append(enabledSealWrappers, disabledSealWrappers...) 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: // We may have multiple seals disabled, but we know Shamir is not one of them. barrierSeal = vault.NewAutoSeal(vaultseal.NewAccess(sealLogger, sealGenerationInfo, enabledSealWrappers)) @@ -2757,13 +2769,14 @@ func setSeal(c *ServerCommand, config *server.Config, infoKeys []string, info ma default: // 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{ - barrierSeal: barrierSeal, - unwrapSeal: unwrapSeal, - sealConfigError: sealConfigError, + barrierSeal: barrierSeal, + unwrapSeal: unwrapSeal, + sealConfigError: sealConfigError, + sealConfigWarning: sealConfigWarning, }, nil } diff --git a/go.mod b/go.mod index dfc7de3498..8a19cb1f71 100644 --- a/go.mod +++ b/go.mod @@ -92,7 +92,7 @@ require ( 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/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-msgpack v1.1.5 github.com/hashicorp/go-multierror v1.1.1 diff --git a/go.sum b/go.sum index ca90102e2f..03828c7ec4 100644 --- a/go.sum +++ b/go.sum @@ -1994,8 +1994,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/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/transit/v2 v2.0.7 h1:G25tZFw/LrAzJWxvS0/BFI7V1xAP/UsAIsgBwiE0mwo= -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 h1:uvdmC28xaqklqRQ3HWvq9HP4jX7Vy4M5JrJqAxfo5ig= +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/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= github.com/hashicorp/go-metrics v0.5.1 h1:rfPwUqFU6uZXNvGl4hzjY8LEBsqFVU4si1H9/Hqck/U= diff --git a/internalshared/configutil/kms.go b/internalshared/configutil/kms.go index f3f34bf341..d68eaeb187 100644 --- a/internalshared/configutil/kms.go +++ b/internalshared/configutil/kms.go @@ -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) { 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 the error is any other than logical.KeyNotFoundError, return the error if !errwrap.ContainsType(err, new(logical.KeyNotFoundError)) { diff --git a/vault/logical_system.go b/vault/logical_system.go index fea383a6af..adc7b89603 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -5027,7 +5027,7 @@ func (c *Core) GetSealBackendStatus(ctx context.Context) (*SealBackendStatusResp if a, ok := c.seal.(*autoSeal); ok { r.Healthy = c.seal.Healthy() var uhMin time.Time - for _, sealWrapper := range a.GetAllSealWrappersByPriority() { + for _, sealWrapper := range a.GetConfiguredSealWrappersByPriority() { b := SealBackendStatus{ Name: sealWrapper.Name, Healthy: sealWrapper.IsHealthy(), diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index 45b8a8eb02..031a76d8ed 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -6272,6 +6272,7 @@ func TestGetSealBackendStatus(t *testing.T) { Wrapper: aeadwrapper.NewShamirWrapper(), SealConfigType: wrapping.WrapperTypeShamir.String(), Priority: 1, + Configured: true, }, }, )) diff --git a/vault/rekey.go b/vault/rekey.go index d7ec3dadae..4a199bb7fb 100644 --- a/vault/rekey.go +++ b/vault/rekey.go @@ -405,7 +405,7 @@ func (c *Core) BarrierRekeyUpdate(ctx context.Context, key []byte, nonce string) if err != nil { 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.SetCore(c) diff --git a/vault/seal/seal.go b/vault/seal/seal.go index 4c843e3b52..26b2be56c6 100644 --- a/vault/seal/seal.go +++ b/vault/seal/seal.go @@ -34,6 +34,11 @@ const ( StoredKeysSupportedShamirRoot ) +var ( + ErrUnconfiguredWrapper = errors.New("unconfigured wrapper") + ErrNoHealthySeals = errors.New("no healthy seals!") +) + func (s StoredKeysSupport) String() string { switch s { case StoredKeysNotSupported: @@ -204,9 +209,12 @@ type Access interface { SetShamirSealKey([]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 + // 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() []*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 // disabled flag set to false. 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}) } 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 { - return a.filterSealWrappers(enabledOnly, healthyAndUnhealthy) + return a.filterSealWrappers(configuredWrappers, enabledWrappers) } func (a *access) AllSealWrappersHealthy() bool { - return len(a.wrappersByPriority) == len(a.filterSealWrappers(enabledAndDisabled, healthyOnly)) + return len(a.wrappersByPriority) == len(a.filterSealWrappers(configuredWrappers, healthyWrappers)) } -type ( - enabledFilter bool - healthyFilter bool -) +type sealWrapperFilter func(*SealWrapper) bool -const ( - enabledOnly = enabledFilter(true) - enabledAndDisabled = !enabledOnly - healthyOnly = healthyFilter(true) - healthyAndUnhealthy = !healthyOnly -) +func allWrappers(wrapper *SealWrapper) bool { + return true +} -func (a *access) filterSealWrappers(enabled enabledFilter, healthy healthyFilter) []*SealWrapper { - ret := make([]*SealWrapper, 0, len(a.wrappersByPriority)) - for _, sw := range a.wrappersByPriority { - switch { - case enabled == enabledOnly && sw.Disabled: - continue - case healthy == healthyOnly && !sw.IsHealthy(): - continue - default: - ret = append(ret, sw) +func healthyWrappers(wrapper *SealWrapper) bool { + return wrapper.IsHealthy() +} + +func enabledWrappers(wrapper *SealWrapper) bool { + return !wrapper.Disabled +} + +func configuredWrappers(wrapper *SealWrapper) bool { + 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 } @@ -336,7 +358,7 @@ func (a *access) GetEnabledWrappers() []wrapping.Wrapper { func (a *access) Init(ctx context.Context, options ...wrapping.Option) error { var keyIds []string - for _, sealWrapper := range a.GetAllSealWrappersByPriority() { + for _, sealWrapper := range a.GetConfiguredSealWrappersByPriority() { if initWrapper, ok := sealWrapper.Wrapper.(wrapping.InitFinalizer); ok { if err := initWrapper.Init(ctx, options...); err != nil { return err @@ -390,11 +412,12 @@ const ( // 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) { // Note that we do not encrypt with disabled wrappers. Disabled wrappers are only used to decrypt. - enabledWrappersByPriority := a.filterSealWrappers(enabledOnly, healthyOnly) - if len(enabledWrappersByPriority) == 0 { + candidateWrappers := a.filterSealWrappers(enabledWrappers, healthyWrappers) + if len(candidateWrappers) == 0 { // 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 { name string @@ -474,6 +497,13 @@ GATHER_RESULTS: a.keyIdSet.set(ret) } + // Add errors for unconfigured wrappers + for _, sw := range candidateWrappers { + if !sw.Configured { + errs[sw.Name] = ErrUnconfiguredWrapper + } + } + return ret, errs } @@ -520,10 +550,13 @@ func (a *access) Decrypt(ctx context.Context, ciphertext *MultiWrapValue, option return nil, false, err } - wrappersByPriority := a.filterSealWrappers(enabledAndDisabled, healthyOnly) + wrappersByPriority := a.filterSealWrappers(configuredWrappers, healthyWrappers) if len(wrappersByPriority) == 0 { // 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 { @@ -534,23 +567,45 @@ func (a *access) Decrypt(ctx context.Context, ciphertext *MultiWrapValue, option } resultCh := make(chan *result) - decrypt := func(sealWrapper *SealWrapper) { - pt, oldKey, err := a.tryDecrypt(ctx, sealWrapper, blobInfoMap, options) + reportResult := func(name string, plaintext []byte, oldKey bool, err error) { resultCh <- &result{ - name: sealWrapper.Name, - pt: pt, + name: name, + pt: plaintext, oldKey: oldKey, 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 - 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 - if i == 0 { - // start the highest priority wrapper right away - go decrypt(sealWrapper) - } else { + if sealWrapper != first { timer := time.AfterFunc(wrapperDecryptHighPriorityHeadStart, func() { 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 { var errs []error - for _, w := range a.GetAllSealWrappersByPriority() { + for _, w := range a.GetConfiguredSealWrappersByPriority() { if finalizeWrapper, ok := w.Wrapper.(wrapping.InitFinalizer); ok { if err := finalizeWrapper.Finalize(ctx, options...); err != nil { errs = append(errs, err) diff --git a/vault/seal/seal_testing.go b/vault/seal/seal_testing.go index d82e51e659..52241f3e5e 100644 --- a/vault/seal/seal_testing.go +++ b/vault/seal/seal_testing.go @@ -57,6 +57,7 @@ func NewTestSeal(opts *TestSealOpts) (Access, []*ToggleableWrapper) { fmt.Sprintf("%s-%d", opts.Name, i+1), wrapperType.String(), false, + true, ) } @@ -88,6 +89,7 @@ func NewToggleableTestSeal(opts *TestSealOpts) (Access, []func(error)) { fmt.Sprintf("%s-%d", opts.Name, i+1), wrapperType.String(), false, + true, ) funcs[i] = w.SetError } diff --git a/vault/seal/seal_wrapper.go b/vault/seal/seal_wrapper.go index 543407154b..b30ae8e37c 100644 --- a/vault/seal/seal_wrapper.go +++ b/vault/seal/seal_wrapper.go @@ -26,6 +26,9 @@ type SealWrapper struct { // Disabled indicates, when true indicates that this wrapper should only be used for decryption. Disabled bool + // Configured indicates the wrapper was successfully configured at initialization + Configured bool + // hcLock protects lastHealthy, lastSeenHealthy, and healthy. // Do not modify those fields directly, use setHealth instead. // Do not access these fields directly, use getHealth instead. @@ -35,16 +38,21 @@ type SealWrapper struct { 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{ Wrapper: wrapper, Priority: priority, Name: name, SealConfigType: sealConfigType, 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 } diff --git a/vault/seal_autoseal.go b/vault/seal_autoseal.go index 203eda8d70..489b9526d2 100644 --- a/vault/seal_autoseal.go +++ b/vault/seal_autoseal.go @@ -37,9 +37,10 @@ type autoSeal struct { core *Core logger log.Logger - allSealsHealthy bool - hcLock sync.RWMutex - healthCheckStop chan struct{} + unsealedWithUnhealthySeal bool + allSealsHealthy bool + hcLock sync.RWMutex + healthCheckStop chan struct{} } // Ensure we are implementing the Seal interface @@ -193,7 +194,7 @@ func (d *autoSeal) BarrierConfig(ctx context.Context) (*SealConfig, error) { 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()) 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() 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 allUnhealthy := true - for _, sealWrapper := range d.Access.GetAllSealWrappersByPriority() { + for _, sealWrapper := range d.Access.GetConfiguredSealWrappersByPriority() { mLabels := []metrics.Label{{Name: "seal_wrapper_name", Value: sealWrapper.Name}} wasHealthy := sealWrapper.IsHealthy() diff --git a/vault/seal_testing_util.go b/vault/seal_testing_util.go index 8da46b8224..6139ed25eb 100644 --- a/vault/seal_testing_util.go +++ b/vault/seal_testing_util.go @@ -18,7 +18,7 @@ func NewTestSeal(t testing.T, opts *seal.TestSealOpts) Seal { switch opts.StoredKeys { case seal.StoredKeysSupportedShamirRoot: 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 { t.Fatal("error creating test seal", err) @@ -33,7 +33,7 @@ func NewTestSeal(t testing.T, opts *seal.TestSealOpts) Seal { return newSeal case seal.StoredKeysNotSupported: 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 { t.Fatal("error creating test seal", err)