Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vault-18638: add seal reload on SIGHUP #23571

Merged
merged 12 commits into from
Nov 30, 2023
149 changes: 111 additions & 38 deletions command/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1258,57 +1258,29 @@ func (c *ServerCommand) Run(args []string) int {
}

ctx := context.Background()
existingSealGenerationInfo, err := vault.PhysicalSealGenInfo(ctx, backend)
if err != nil {
c.UI.Error(fmt.Sprintf("Error getting seal generation info: %v", err))
return 1
}

hasPartialPaths, err := hasPartiallyWrappedPaths(ctx, backend)
if err != nil {
c.UI.Error(fmt.Sprintf("Cannot determine if there are partially seal wrapped entries in storage: %v", err))
victorr marked this conversation as resolved.
Show resolved Hide resolved
return 1
}
setSealResponse, err := setSeal(c, config, infoKeys, info, existingSealGenerationInfo, hasPartialPaths)
setSealResponse, secureRandomReader, err := c.configureSeals(ctx, config, backend, infoKeys, info)
if err != nil {
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
// Ensure that the seal finalizer is called, even if using verify-only
if setSealResponse.barrierSeal != nil {
victorr marked this conversation as resolved.
Show resolved Hide resolved
defer func(seal *vault.Seal) {
err = (*seal).Finalize(ctx)
if err != nil {
c.UI.Error(fmt.Sprintf("Error finalizing seals: %v", err))
}
}(seal)
}

if setSealResponse.barrierSeal == nil {
c.UI.Error("Could not create barrier seal! Most likely proper Seal configuration information was not set, but no error was generated.")
return 1
}(&setSealResponse.barrierSeal)
}

// prepare a secure random reader for core
entropyAugLogger := c.logger.Named("entropy-augmentation")
var entropySources []*configutil.EntropySourcerInfo
for _, sealWrapper := range setSealResponse.barrierSeal.GetAccess().GetEnabledSealWrappersByPriority() {
if s, ok := sealWrapper.Wrapper.(entropy.Sourcer); ok {
entropySources = append(entropySources, &configutil.EntropySourcerInfo{
Sourcer: s,
Name: sealWrapper.Name,
})
}
}
secureRandomReader, err := configutil.CreateSecureRandomReaderFunc(config.SharedConfig, entropySources, entropyAugLogger)
if err != nil {
c.UI.Error(err.Error())
return 1
if setSealResponse.unwrapSeal != nil {
defer func(seal *vault.Seal) {
err = (*seal).Finalize(ctx)
if err != nil {
c.UI.Error(fmt.Sprintf("Error finalizing seals: %v", err))
}
}(&setSealResponse.unwrapSeal)
}

coreConfig := createCoreConfig(c, config, backend, configSR, setSealResponse.barrierSeal, setSealResponse.unwrapSeal, metricsHelper, metricSink, secureRandomReader)
Expand Down Expand Up @@ -1708,6 +1680,32 @@ func (c *ServerCommand) Run(args []string) int {
c.logger.Warn(cErr.String())
}

if !cmp.Equal(core.GetCoreConfigInternal().Seals, config.Seals) {
barrierSeal, unwrapSeal, err := c.reloadSeals(ctx, core, config)
if err != nil {
c.logger.Error(fmt.Errorf("error reloading seal config: %s", err).Error())
config.Seals = core.GetCoreConfigInternal().Seals
}

if barrierSeal != nil {
defer func(seal *vault.Seal) {
err = (*seal).Finalize(context.Background())
if err != nil {
c.UI.Error(fmt.Sprintf("Error finalizing seals: %v", err))
}
}(&barrierSeal)
}

if unwrapSeal != nil {
defer func(seal *vault.Seal) {
err = (*seal).Finalize(context.Background())
if err != nil {
c.UI.Error(fmt.Sprintf("Error finalizing seals: %v", err))
}
}(&unwrapSeal)
}
}

core.SetConfig(config)

// reloading custom response headers to make sure we have
Expand Down Expand Up @@ -1862,6 +1860,47 @@ func (c *ServerCommand) Run(args []string) int {
return retCode
}

func (c *ServerCommand) configureSeals(ctx context.Context, config *server.Config, backend physical.Backend, infoKeys []string, info map[string]string) (*SetSealResponse, io.Reader, error) {
existingSealGenerationInfo, err := vault.PhysicalSealGenInfo(ctx, backend)
if err != nil {
return nil, nil, fmt.Errorf("Error getting seal generation info: %v", err)
}

hasPartialPaths, err := hasPartiallyWrappedPaths(ctx, backend)
if err != nil {
return nil, nil, fmt.Errorf("Cannot determine if there are partially seal wrapped entries in storage: %v", err)
}
setSealResponse, err := setSeal(c, config, infoKeys, info, existingSealGenerationInfo, hasPartialPaths)
if err != nil {
return nil, nil, err
}
if setSealResponse.sealConfigWarning != nil {
c.UI.Warn(fmt.Sprintf("Warnings during seal configuration: %v", setSealResponse.sealConfigWarning))
}

if setSealResponse.barrierSeal == nil {
return nil, nil, errors.New("Could not create barrier seal! Most likely proper Seal configuration information was not set, but no error was generated.")
}

// prepare a secure random reader for core
entropyAugLogger := c.logger.Named("entropy-augmentation")
var entropySources []*configutil.EntropySourcerInfo
for _, sealWrapper := range setSealResponse.barrierSeal.GetAccess().GetEnabledSealWrappersByPriority() {
if s, ok := sealWrapper.Wrapper.(entropy.Sourcer); ok {
entropySources = append(entropySources, &configutil.EntropySourcerInfo{
Sourcer: s,
Name: sealWrapper.Name,
})
}
}
secureRandomReader, err := configutil.CreateSecureRandomReaderFunc(config.SharedConfig, entropySources, entropyAugLogger)
if err != nil {
return nil, nil, err
}

return setSealResponse, secureRandomReader, nil
}

// configureLogging takes the configuration and attempts to parse config values into 'log' friendly configuration values
// If all goes to plan, a logger is created and setup.
func (c *ServerCommand) configureLogging(config *server.Config) (hclog.InterceptLogger, error) {
Expand Down Expand Up @@ -3294,6 +3333,40 @@ func startHttpServers(c *ServerCommand, core *vault.Core, config *server.Config,
return nil
}

func (c *ServerCommand) reloadSeals(ctx context.Context, core *vault.Core, config *server.Config) (vault.Seal, vault.Seal, error) {
if len(config.Seals) == 1 && config.Seals[0].Disabled {
return nil, nil, errors.New("moving from autoseal to shamir requires seal migration")
}

if core.SealAccess().BarrierSealConfigType() == vault.SealConfigTypeShamir {
return nil, nil, errors.New("moving from shamir to autoseal requires seal migration")
}

infoKeysReload := make([]string, 0)
infoReload := make(map[string]string)

setSealResponse, secureRandomReader, err := c.configureSeals(ctx, config, core.PhysicalAccess(), infoKeysReload, infoReload)
if err != nil {
return nil, nil, err
}
if setSealResponse.sealConfigError != nil {
return nil, nil, err
}

err = core.SetSeals(setSealResponse.barrierSeal, secureRandomReader)
if err != nil {
return nil, nil, fmt.Errorf("error setting seal: %s", err)
}

newGen := setSealResponse.barrierSeal.GetAccess().GetSealGenerationInfo()

if err := core.SetPhysicalSealGenInfo(ctx, newGen); err != nil {
c.logger.Warn("could not update seal information in storage", "err", err)
}

return setSealResponse.barrierSeal, setSealResponse.unwrapSeal, nil
}

func SetStorageMigration(b physical.Backend, active bool) error {
if !active {
return b.Delete(context.Background(), storageMigrationLock)
Expand Down
48 changes: 48 additions & 0 deletions command/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package command

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
Expand All @@ -21,8 +22,13 @@ import (
"testing"
"time"

"github.com/hashicorp/vault/command/server"
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
"github.com/hashicorp/vault/internalshared/configutil"
"github.com/hashicorp/vault/sdk/physical"
physInmem "github.com/hashicorp/vault/sdk/physical/inmem"
"github.com/hashicorp/vault/vault"
"github.com/hashicorp/vault/vault/seal"
"github.com/mitchellh/cli"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -400,3 +406,45 @@ func TestConfigureDevTLS(t *testing.T) {
})
}
}

func TestConfigureSeals(t *testing.T) {
testConfig := server.Config{SharedConfig: &configutil.SharedConfig{}}
_, testCommand := testServerCommand(t)

logger := corehelpers.NewTestLogger(t)
backend, err := physInmem.NewInmem(nil, logger)
if err != nil {
t.Fatal(err)
}
testCommand.logger = logger

setSealResponse, _, err := testCommand.configureSeals(context.Background(), &testConfig, backend, []string{}, map[string]string{})
if err != nil {
t.Fatal(err)
}

if len(setSealResponse.barrierSeal.GetAccess().GetAllSealWrappersByPriority()) != 1 {
t.Fatalf("expected 1 seal, got %d", len(setSealResponse.barrierSeal.GetAccess().GetAllSealWrappersByPriority()))
}

if setSealResponse.barrierSeal.BarrierSealConfigType() != vault.SealConfigTypeShamir {
t.Fatalf("expected shamir seal, got seal type %s", setSealResponse.barrierSeal.BarrierSealConfigType())
}
}

func TestReloadSeals(t *testing.T) {
testCore := vault.TestCoreWithSeal(t, vault.NewTestSeal(t, &seal.TestSealOpts{StoredKeys: seal.StoredKeysSupportedShamirRoot}), false)
_, testCommand := testServerCommand(t)
testConfig := server.Config{SharedConfig: &configutil.SharedConfig{}}

_, _, err := testCommand.reloadSeals(context.Background(), testCore, &testConfig)
if err == nil {
t.Fatal("expected error, got nil")
}

testConfig = server.Config{SharedConfig: &configutil.SharedConfig{Seals: []*configutil.KMS{{Disabled: true}}}}
_, _, err = testCommand.reloadSeals(context.Background(), testCore, &testConfig)
if err == nil {
t.Fatal("expected error, got nil")
}
}
43 changes: 43 additions & 0 deletions vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -4280,3 +4280,46 @@ func (c *Core) GetRaftAutopilotState(ctx context.Context) (*raft.AutopilotState,
func (c *Core) Events() *eventbus.EventBus {
return c.events
}

func (c *Core) SetSeals(barrierSeal Seal, secureRandomReader io.Reader) error {
victorr marked this conversation as resolved.
Show resolved Hide resolved
c.stateLock.Lock()
defer c.stateLock.Unlock()

ctx, _ := c.GetContext()

currentSealBarrierConfig, err := c.SealAccess().BarrierConfig(ctx)
if err != nil {
return fmt.Errorf("error retrieving barrier config: %s", err)
}

barrierConfigCopy := currentSealBarrierConfig.Clone()
barrierConfigCopy.Type = barrierSeal.BarrierSealConfigType().String()

barrierSeal.SetCore(c)

rootKey, err := c.seal.GetStoredKeys(ctx)
if err != nil {
return err
}

if len(rootKey) < 1 {
return errors.New("root key not found")
}

barrierConfigCopy.Type = barrierSeal.BarrierSealConfigType().String()
err = barrierSeal.SetBarrierConfig(ctx, barrierConfigCopy)
if err != nil {
return fmt.Errorf("error setting barrier config for new seal: %s", err)
}

err = barrierSeal.SetStoredKeys(ctx, rootKey)
if err != nil {
return fmt.Errorf("error setting root key in new seal: %s", err)
}

c.seal = barrierSeal

c.reloadSealsEnt(secureRandomReader, barrierSeal.GetAccess(), c.logger)

return nil
}
42 changes: 42 additions & 0 deletions vault/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/physical"
"github.com/hashicorp/vault/sdk/physical/inmem"
"github.com/hashicorp/vault/vault/seal"
"github.com/hashicorp/vault/version"
"github.com/sasha-s/go-deadlock"
)
Expand Down Expand Up @@ -3024,3 +3025,44 @@ func InduceDeadlock(t *testing.T, vaultcore *Core, expected uint32) {
t.Fatalf("expected 1 deadlock, detected %d", deadlocks)
}
}

func TestSetSeals(t *testing.T) {
oldSeal := NewTestSeal(t, &seal.TestSealOpts{
StoredKeys: seal.StoredKeysSupportedGeneric,
Name: "old-seal",
WrapperCount: 1,
Generation: 1,
})
testCore := TestCoreWithSeal(t, oldSeal, false)
_, keys, _ := TestCoreInitClusterWrapperSetup(t, testCore, nil)
for _, key := range keys {
if _, err := TestCoreUnseal(testCore, key); err != nil {
t.Fatalf("error unsealing core: %s", err)
}
}

if testCore.Sealed() {
t.Fatal("expected core to be unsealed, but it is sealed")
}

newSeal := NewTestSeal(t, &seal.TestSealOpts{
StoredKeys: seal.StoredKeysSupportedGeneric,
Name: "new-seal",
WrapperCount: 1,
Generation: 2,
})

err := testCore.SetSeals(newSeal, nil)
if err != nil {
t.Fatal(err)
}

wrappers := testCore.seal.GetAccess().GetAllSealWrappersByPriority()
if len(wrappers) != 1 {
t.Fatalf("expected 1 wrapper in seal access, got %d", len(wrappers))
}

if wrappers[0].Name != "new-seal-1" {
t.Fatalf("unexpected seal name: got %s, expected new-seal-1", wrappers[0].Name)
}
}
5 changes: 5 additions & 0 deletions vault/core_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package vault
import (
"context"
"fmt"
"io"

"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/namespace"
Expand All @@ -16,6 +17,7 @@ import (
"github.com/hashicorp/vault/sdk/physical"
"github.com/hashicorp/vault/vault/quotas"
"github.com/hashicorp/vault/vault/replication"
"github.com/hashicorp/vault/vault/seal"
)

const (
Expand Down Expand Up @@ -199,3 +201,6 @@ func (c *Core) MissingRequiredState(raw []string, perfStandby bool) bool {
func DiagnoseCheckLicense(ctx context.Context, vaultCore *Core, coreConfig CoreConfig, generate bool) (bool, []string) {
return false, nil
}

func (c *Core) reloadSealsEnt(secureRandomReader io.Reader, sealAccess seal.Access, logger hclog.Logger) {
victorr marked this conversation as resolved.
Show resolved Hide resolved
}
Loading