Backport Provide more information around seal migrations into ce/main (#14345)

This commit is contained in:
Vault Automation 2026-04-28 09:54:15 -06:00 committed by GitHub
parent 1d4d9bc61c
commit ceada1d29a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 77 additions and 67 deletions

View File

@ -96,25 +96,26 @@ func sealStatusRequestWithContext(ctx context.Context, c *Sys, r *Request) (*Sea
}
type SealStatusResponse struct {
Type string `json:"type"`
Initialized bool `json:"initialized"`
Sealed bool `json:"sealed"`
T int `json:"t"`
N int `json:"n"`
Progress int `json:"progress"`
Nonce string `json:"nonce"`
Version string `json:"version"`
BuildDate string `json:"build_date"`
Migration bool `json:"migration"`
ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`
RecoverySeal bool `json:"recovery_seal"`
RecoverySealType string `json:"recovery_seal_type,omitempty"`
StorageType string `json:"storage_type,omitempty"`
HCPLinkStatus string `json:"hcp_link_status,omitempty"`
HCPLinkResourceID string `json:"hcp_link_resource_ID,omitempty"`
RemovedFromCluster *bool `json:"removed_from_cluster,omitempty"`
Warnings []string `json:"warnings,omitempty"`
Type string `json:"type"`
Initialized bool `json:"initialized"`
Sealed bool `json:"sealed"`
T int `json:"t"`
N int `json:"n"`
Progress int `json:"progress"`
Nonce string `json:"nonce"`
Version string `json:"version"`
BuildDate string `json:"build_date"`
Migration bool `json:"migration"`
ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`
RecoverySeal bool `json:"recovery_seal"`
RecoverySealType string `json:"recovery_seal_type,omitempty"`
StorageType string `json:"storage_type,omitempty"`
HCPLinkStatus string `json:"hcp_link_status,omitempty"`
HCPLinkResourceID string `json:"hcp_link_resource_ID,omitempty"`
RemovedFromCluster *bool `json:"removed_from_cluster,omitempty"`
Warnings []string `json:"warnings,omitempty"`
MigrationDoneAtEpoch int64 `json:"migration_done_at_epoch,omitempty"`
}
type UnsealOpts struct {

3
changelog/_14334.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
api: Add migration_done_at_epoch to sys/seal-status response.
```

View File

@ -1337,7 +1337,7 @@ 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)
checkResult, _, err := c.checkForSealMigration(context.Background(), conf.UnwrapSeal)
if err != nil {
return nil, fmt.Errorf("error checking if a seal migration is needed: %w", err)
}
@ -1840,7 +1840,7 @@ func (c *Core) unsealFragment(key []byte, migrate bool) error {
return fmt.Errorf("can't perform a seal migration while joining a raft cluster")
}
if !migrate && c.migrationInfo != nil {
done, err := c.sealMigrated(ctx)
done, _, err := c.sealMigrated(ctx)
if err != nil {
return fmt.Errorf("error checking to see if seal is migrated: %w", err)
}
@ -2072,26 +2072,28 @@ func (c *Core) getUnsealKey(ctx context.Context, seal Seal) ([]byte, error) {
// For the auto->auto same seal migration scenario, it will return false even
// if the preceding conditions are true but we cannot decrypt the master key
// in storage using the configured seal.
func (c *Core) sealMigrated(ctx context.Context) (bool, error) {
// When no error is returned, returns a string that gives more information about
// why the bool return value is set as it is.
func (c *Core) sealMigrated(ctx context.Context) (bool, string, error) {
sealMigDone := c.sealMigrationDone.Load()
if sealMigDone != nil && !sealMigDone.IsZero() {
return true, nil
return true, "sealMigrationDone nonzero", nil
}
existBarrierSealConfig, existRecoverySealConfig, err := c.PhysicalSealConfigs(ctx)
if err != nil {
return false, err
return false, "", err
}
if !c.seal.BarrierSealConfigType().IsSameAs(existBarrierSealConfig.Type) {
return false, nil
return false, "barrier seal config type in seal matches what's in storage", nil
}
if c.seal.RecoveryKeySupported() && !SealConfigTypeRecovery.IsSameAs(existRecoverySealConfig.Type) {
return false, nil
return false, "recovery seal config type in seal matches what's in storage", nil
}
if c.seal.BarrierSealConfigType() != c.migrationInfo.seal.BarrierSealConfigType() {
return true, nil
return true, "barrier seal config type in seal doesn't match what's in storage", nil
}
// The above checks can handle the auto->shamir and shamir->auto
@ -2103,13 +2105,13 @@ func (c *Core) sealMigrated(ctx context.Context) (bool, error) {
switch {
case len(keys) > 0 && err == nil:
return true, nil
return true, "seal has stored keys", nil
case len(keysMig) > 0 && errMig == nil:
return false, nil
return false, "migration seal has stored keys", nil
case errors.Is(err, &ErrDecrypt{}) && errors.Is(errMig, &ErrDecrypt{}):
return false, fmt.Errorf("decrypt error, neither the old nor new seal can read stored keys: old seal err=%v, new seal err=%v", errMig, err)
return false, "", fmt.Errorf("decrypt error, neither the old nor new seal can read stored keys: old seal err=%v, new seal err=%v", errMig, err)
default:
return false, fmt.Errorf("neither the old nor new seal can read stored keys: old seal err=%v, new seal err=%v", errMig, err)
return false, "", fmt.Errorf("neither the old nor new seal can read stored keys: old seal err=%v, new seal err=%v", errMig, err)
}
}
@ -2121,17 +2123,17 @@ func (c *Core) migrateSeal(ctx context.Context) error {
return c.migrateMultiSealConfig(ctx)
}
ok, err := c.sealMigrated(ctx)
ok, info, err := c.sealMigrated(ctx)
if err != nil {
return fmt.Errorf("error checking if seal is migrated or not: %w", err)
}
if ok {
c.logger.Info("migration is already performed")
c.logger.Info("migration is already performed", "info", info)
return nil
}
c.logger.Info("seal migration initiated")
c.logger.Info("seal migration initiated", "info", info)
switch {
case c.migrationInfo.seal.RecoveryKeySupported() && c.seal.RecoveryKeySupported():
@ -3345,16 +3347,16 @@ const (
sealMigrationCheckDoNotAjust
)
func (c *Core) checkForSealMigration(ctx context.Context, unwrapSeal Seal) (sealMigrationCheckResult, error) {
func (c *Core) checkForSealMigration(ctx context.Context, unwrapSeal Seal) (sealMigrationCheckResult, string, error) {
existBarrierSealConfig, _, err := c.PhysicalSealConfigs(ctx)
if err != nil {
return sealMigrationCheckError, fmt.Errorf("Error checking for existing seal: %s", err)
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
return sealMigrationCheckSkip, "no seal config or deprecated", nil
}
if unwrapSeal == nil {
@ -3368,28 +3370,28 @@ func (c *Core) checkForSealMigration(ctx context.Context, unwrapSeal Seal) (seal
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
return sealMigrationCheckDoNotAjust, "same barrier and unwrap seal is nil", 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",
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
return sealMigrationCheckAdjust, "configured seal is not shamir and stored seal config is", nil
case configuredType == SealConfigTypeMultiseal && c.IsMultisealEnabled():
// 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
return sealMigrationCheckDoNotAjust, "single non-shamir to multiseal", 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",
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,
@ -3398,12 +3400,12 @@ func (c *Core) checkForSealMigration(ctx context.Context, unwrapSeal Seal) (seal
// 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
return sealMigrationCheckDoNotAjust, "multiseal to autoseal", 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",
return sealMigrationCheckError, "", fmt.Errorf("cannot seal migrate from %q to %q, no disabled seal in configuration",
existBarrierSealConfig.Type, c.seal.BarrierSealConfigType())
}
} else {
@ -3411,9 +3413,9 @@ func (c *Core) checkForSealMigration(ctx context.Context, unwrapSeal Seal) (seal
// 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 sealMigrationCheckError, "", errors.New("Shamir seals cannot be set disabled (they should simply not be set)")
}
return sealMigrationCheckDoNotAjust, nil
return sealMigrationCheckDoNotAjust, "unchanged", nil
}
}
@ -3439,7 +3441,7 @@ func (c *Core) checkForSealMigration(ctx context.Context, unwrapSeal Seal) (seal
func (c *Core) adjustForSealMigration(unwrapSeal Seal) error {
ctx := context.Background()
checkResult, err := c.checkForSealMigration(ctx, unwrapSeal)
checkResult, _, err := c.checkForSealMigration(ctx, unwrapSeal)
if err != nil {
return err
}
@ -3723,7 +3725,7 @@ func (c *Core) IsSealMigrated(lock bool) bool {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
}
done, _ := c.sealMigrated(context.Background())
done, _, _ := c.sealMigrated(context.Background())
return done
}

View File

@ -6026,25 +6026,26 @@ func (b *SystemBackend) pathInternalOpenAPI(ctx context.Context, req *logical.Re
}
type SealStatusResponse struct {
Type string `json:"type"`
Initialized bool `json:"initialized"`
Sealed bool `json:"sealed"`
T int `json:"t"`
N int `json:"n"`
Progress int `json:"progress"`
Nonce string `json:"nonce"`
Version string `json:"version"`
BuildDate string `json:"build_date"`
Migration bool `json:"migration"`
ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`
RecoverySeal bool `json:"recovery_seal"`
StorageType string `json:"storage_type,omitempty"`
HCPLinkStatus string `json:"hcp_link_status,omitempty"`
HCPLinkResourceID string `json:"hcp_link_resource_ID,omitempty"`
Warnings []string `json:"warnings,omitempty"`
RecoverySealType string `json:"recovery_seal_type,omitempty"`
RemovedFromCluster *bool `json:"removed_from_cluster,omitempty"`
Type string `json:"type"`
Initialized bool `json:"initialized"`
Sealed bool `json:"sealed"`
T int `json:"t"`
N int `json:"n"`
Progress int `json:"progress"`
Nonce string `json:"nonce"`
Version string `json:"version"`
BuildDate string `json:"build_date"`
Migration bool `json:"migration"`
ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`
RecoverySeal bool `json:"recovery_seal"`
StorageType string `json:"storage_type,omitempty"`
HCPLinkStatus string `json:"hcp_link_status,omitempty"`
HCPLinkResourceID string `json:"hcp_link_resource_ID,omitempty"`
Warnings []string `json:"warnings,omitempty"`
RecoverySealType string `json:"recovery_seal_type,omitempty"`
RemovedFromCluster *bool `json:"removed_from_cluster,omitempty"`
MigrationDoneAtEpoch int64 `json:"migration_done_at_epoch,omitempty"`
}
type SealBackendStatus struct {
@ -6157,6 +6158,9 @@ func (core *Core) GetSealStatus(ctx context.Context, lock bool) (*SealStatusResp
RecoverySealType: recoverySealType,
StorageType: core.StorageType(),
}
if p := core.sealMigrationDone.Load(); p != nil {
s.MigrationDoneAtEpoch = p.Unix()
}
if resourceIDonHCP != "" {
s.HCPLinkStatus = hcpLinkStatus