Update recovery mode guard to account for migrating away from Shamir (#24443)

It is not sufficient to check that function setSeal in server.go does not return
an "unwrap seal". For migrations away from a Shamir seal, NewCore constructor
sets up an unwrap seal by calling method adjustForSealMigration.

Factor out new method checkForSealMigration out of adjustForSealMigration so
that NewCore can verify that there won't be a migration when returning early due
to running in recovery mode.
This commit is contained in:
Victor Rodriguez
2023-12-08 16:25:52 -05:00
committed by GitHub
parent b5e9f3f32c
commit 128152ee28
2 changed files with 119 additions and 74 deletions

View File

@@ -562,10 +562,6 @@ func (c *ServerCommand) runRecoveryMode() int {
c.UI.Error(fmt.Sprintf("Error setting up seal: %v", setSealResponse.sealConfigError))
return 1
}
if setSealResponse.unwrapSeal != nil {
c.UI.Error("Recovery mode cannot be started with configuration for seal migration")
return 1
}
barrierSeal = setSealResponse.barrierSeal
// Ensure that the seal finalizer is called, even if using verify-only
@@ -580,6 +576,7 @@ func (c *ServerCommand) runRecoveryMode() int {
Physical: backend,
StorageType: config.Storage.Type,
Seal: barrierSeal,
UnwrapSeal: setSealResponse.unwrapSeal,
LogLevel: config.LogLevel,
Logger: c.logger,
DisableMlock: config.DisableMlock,

View File

@@ -1211,6 +1211,13 @@ func NewCore(conf *CoreConfig) (*Core, error) {
// For recovery mode we've now configured enough to return early.
if c.recoveryMode {
checkResult, err := c.checkForSealMigration(context.Background(), conf.UnwrapSeal)
if err != nil {
return nil, fmt.Errorf("error checking if a seal migration is needed")
}
if conf.UnwrapSeal != nil || checkResult == sealMigrationCheckAdjust {
return nil, errors.New("cannot run in recovery mode when a seal migration is needed")
}
return c, nil
}
@@ -2905,6 +2912,87 @@ func setPhysicalSealConfig(ctx context.Context, c *Core, label, configPath strin
return nil
}
type sealMigrationCheckResult int
const (
sealMigrationCheckError sealMigrationCheckResult = iota
sealMigrationCheckSkip
sealMigrationCheckAdjust
sealMigrationCheckDoNotAjust
)
func (c *Core) checkForSealMigration(ctx context.Context, unwrapSeal Seal) (sealMigrationCheckResult, error) {
existBarrierSealConfig, _, err := c.PhysicalSealConfigs(ctx)
if err != nil {
return sealMigrationCheckError, fmt.Errorf("Error checking for existing seal: %s", err)
}
// If we don't have an existing config or if it's the deprecated auto seal
// which needs an upgrade, skip out
if existBarrierSealConfig == nil || existBarrierSealConfig.Type == WrapperTypeHsmAutoDeprecated.String() {
return sealMigrationCheckSkip, nil
}
if unwrapSeal == nil {
// With unwrapSeal==nil, either we're not migrating, or we're migrating
// from shamir.
storedType := SealConfigType(existBarrierSealConfig.Type)
configuredType := c.seal.BarrierSealConfigType()
switch {
case storedType == configuredType:
// We have the same barrier type and the unwrap seal is nil so we're not
// migrating from same to same, IOW we assume it's not a migration.
return sealMigrationCheckDoNotAjust, nil
case configuredType == SealConfigTypeShamir:
// The stored barrier config is not shamir, there is no disabled seal
// in config, and either no configured seal (which equates to Shamir)
// or an explicitly configured Shamir seal.
return sealMigrationCheckError, fmt.Errorf("cannot seal migrate from %q to Shamir, no disabled seal in configuration",
existBarrierSealConfig.Type)
case storedType == SealConfigTypeShamir:
// The configured seal is not Shamir, the stored seal config is Shamir.
// This is a migration away from Shamir.
return sealMigrationCheckAdjust, nil
case configuredType == SealConfigTypeMultiseal && server.IsMultisealSupported():
// We are going from a single non-shamir seal to multiseal, and multi seal is supported.
// This scenario is not considered a migration in the sense of requiring an unwrapSeal,
// but we will update the stored SealConfig later (see Core.migrateMultiSealConfig).
return sealMigrationCheckDoNotAjust, nil
case configuredType == SealConfigTypeMultiseal:
// The configured seal is multiseal and we know the stored type is not shamir, thus
// we are going from auto seal to multiseal.
return sealMigrationCheckError, fmt.Errorf("cannot seal migrate from %q to %q, multiple seals are not supported",
existBarrierSealConfig.Type, c.seal.BarrierSealConfigType())
case storedType == SealConfigTypeMultiseal:
// The stored type is multiseal and we know the type the configured type is not shamir,
// thus we are going from multiseal to autoseal.
//
// This scenario is not considered a migration in the sense of requiring an unwrapSeal,
// but we will update the stored SealConfig later (see Core.migrateMultiSealConfig).
return sealMigrationCheckDoNotAjust, nil
default:
// We know at this point that there is a configured non-Shamir seal,
// that it does not match the stored non-Shamir seal config, and that
// there is no explicitly disabled seal stanza.
return sealMigrationCheckError, fmt.Errorf("cannot seal migrate from %q to %q, no disabled seal in configuration",
existBarrierSealConfig.Type, c.seal.BarrierSealConfigType())
}
} else {
// If we're not coming from Shamir we expect the previous seal to be
// in the config and disabled.
if unwrapSeal.BarrierSealConfigType() == SealConfigTypeShamir {
return sealMigrationCheckError, errors.New("Shamir seals cannot be set disabled (they should simply not be set)")
}
return sealMigrationCheckDoNotAjust, nil
}
}
// adjustForSealMigration takes the unwrapSeal, which is nil if (a) we're not
// configured for seal migration or (b) we might be doing a seal migration away
// from shamir. It will only be non-nil if there is a configured seal with
@@ -2926,81 +3014,41 @@ func setPhysicalSealConfig(ctx context.Context, c *Core, label, configPath strin
// to write the new barrier/recovery stored seal config.
func (c *Core) adjustForSealMigration(unwrapSeal Seal) error {
ctx := context.Background()
checkResult, err := c.checkForSealMigration(ctx, unwrapSeal)
if err != nil {
return err
}
switch checkResult {
case sealMigrationCheckSkip:
// If we don't have an existing config or if it's the deprecated auto seal
// which needs an upgrade, skip out
return nil
case sealMigrationCheckAdjust:
// The configured seal is not Shamir, the stored seal config is Shamir.
// This is a migration away from Shamir.
// See note about creating a SealGenerationInfo for the unwrap seal in
// function setSeal in server.go.
sealAccess, err := vaultseal.NewAccessFromWrapper(c.logger, aeadwrapper.NewShamirWrapper(), SealConfigTypeShamir.String())
if err != nil {
return err
}
unwrapSeal = NewDefaultSeal(sealAccess)
case sealMigrationCheckDoNotAjust:
// unwrapSeal stays as is
if unwrapSeal == nil {
return nil
}
}
existBarrierSealConfig, existRecoverySealConfig, err := c.PhysicalSealConfigs(ctx)
if err != nil {
return fmt.Errorf("Error checking for existing seal: %s", err)
}
// If we don't have an existing config or if it's the deprecated auto seal
// which needs an upgrade, skip out
if existBarrierSealConfig == nil || existBarrierSealConfig.Type == WrapperTypeHsmAutoDeprecated.String() {
return nil
}
if unwrapSeal == nil {
// With unwrapSeal==nil, either we're not migrating, or we're migrating
// from shamir.
storedType := SealConfigType(existBarrierSealConfig.Type)
configuredType := c.seal.BarrierSealConfigType()
switch {
case storedType == configuredType:
// We have the same barrier type and the unwrap seal is nil so we're not
// migrating from same to same, IOW we assume it's not a migration.
return nil
case configuredType == SealConfigTypeShamir:
// The stored barrier config is not shamir, there is no disabled seal
// in config, and either no configured seal (which equates to Shamir)
// or an explicitly configured Shamir seal.
return fmt.Errorf("cannot seal migrate from %q to Shamir, no disabled seal in configuration",
existBarrierSealConfig.Type)
case storedType == SealConfigTypeShamir:
// The configured seal is not Shamir, the stored seal config is Shamir.
// This is a migration away from Shamir.
// See note about creating a SealGenerationInfo for the unwrap seal in
// function setSeal in server.go.
sealAccess, err := vaultseal.NewAccessFromWrapper(c.logger, aeadwrapper.NewShamirWrapper(), SealConfigTypeShamir.String())
if err != nil {
return err
}
unwrapSeal = NewDefaultSeal(sealAccess)
case configuredType == SealConfigTypeMultiseal && server.IsMultisealSupported():
// We are going from a single non-shamir seal to multiseal, and multi seal is supported.
// This scenario is not considered a migration in the sense of requiring an unwrapSeal,
// but we will update the stored SealConfig later (see Core.migrateMultiSealConfig).
return nil
case configuredType == SealConfigTypeMultiseal:
// The configured seal is multiseal and we know the stored type is not shamir, thus
// we are going from auto seal to multiseal.
return fmt.Errorf("cannot seal migrate from %q to %q, multiple seals are not supported",
existBarrierSealConfig.Type, c.seal.BarrierSealConfigType())
case storedType == SealConfigTypeMultiseal:
// The stored type is multiseal and we know the type the configured type is not shamir,
// thus we are going from multiseal to autoseal.
//
// This scenario is not considered a migration in the sense of requiring an unwrapSeal,
// but we will update the stored SealConfig later (see Core.migrateMultiSealConfig).
return nil
default:
// We know at this point that there is a configured non-Shamir seal,
// that it does not match the stored non-Shamir seal config, and that
// there is no explicitly disabled seal stanza.
return fmt.Errorf("cannot seal migrate from %q to %q, no disabled seal in configuration",
existBarrierSealConfig.Type, c.seal.BarrierSealConfigType())
}
} else {
// If we're not coming from Shamir we expect the previous seal to be
// in the config and disabled.
if unwrapSeal.BarrierSealConfigType() == SealConfigTypeShamir {
return errors.New("Shamir seals cannot be set disabled (they should simply not be set)")
}
}
// If we've reached this point it's a migration attempt and we should have both
// c.migrationInfo.seal (old seal) and c.seal (new seal) populated.
unwrapSeal.SetCore(c)