Skip to content

Commit 15baea5

Browse files
hghaf099ccapurso
andauthored
loading MFA configs upont restart (#15261) (#15308)
* loading MFA configs upont restart * Adding CL * feedback * Update vault/core.go Co-authored-by: Chris Capurso <[email protected]> Co-authored-by: Chris Capurso <[email protected]> Co-authored-by: Chris Capurso <[email protected]>
1 parent 40fc2c1 commit 15baea5

File tree

4 files changed

+201
-38
lines changed

4 files changed

+201
-38
lines changed

changelog/15261.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
auth: load login MFA configuration upon restart
3+
```

vault/core.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"github.com/hashicorp/vault/api"
3636
"github.com/hashicorp/vault/audit"
3737
"github.com/hashicorp/vault/command/server"
38+
"github.com/hashicorp/vault/helper/identity/mfa"
3839
"github.com/hashicorp/vault/helper/metricsutil"
3940
"github.com/hashicorp/vault/helper/namespace"
4041
"github.com/hashicorp/vault/physical/raft"
@@ -2113,7 +2114,6 @@ func (s standardUnsealStrategy) unseal(ctx context.Context, logger log.Logger, c
21132114
if err := c.setupQuotas(ctx, false); err != nil {
21142115
return err
21152116
}
2116-
c.setupCachedMFAResponseAuth()
21172117

21182118
if err := c.setupHeaderHMACKey(ctx, false); err != nil {
21192119
return err
@@ -2135,9 +2135,14 @@ func (s standardUnsealStrategy) unseal(ctx context.Context, logger log.Logger, c
21352135
if err := c.loadIdentityStoreArtifacts(ctx); err != nil {
21362136
return err
21372137
}
2138-
if err := loadMFAConfigs(ctx, c); err != nil {
2138+
if err := loadPolicyMFAConfigs(ctx, c); err != nil {
21392139
return err
21402140
}
2141+
c.setupCachedMFAResponseAuth()
2142+
if err := c.loadLoginMFAConfigs(ctx); err != nil {
2143+
return err
2144+
}
2145+
21412146
if err := c.setupAuditedHeadersConfig(ctx); err != nil {
21422147
return err
21432148
}
@@ -2299,10 +2304,6 @@ func (c *Core) preSeal() error {
22992304
result = multierror.Append(result, fmt.Errorf("error stopping expiration: %w", err))
23002305
}
23012306
c.stopActivityLog()
2302-
// Clear any cached auth response
2303-
c.mfaResponseAuthQueueLock.Lock()
2304-
c.mfaResponseAuthQueue = nil
2305-
c.mfaResponseAuthQueueLock.Unlock()
23062307

23072308
if err := c.teardownCredentials(context.Background()); err != nil {
23082309
result = multierror.Append(result, fmt.Errorf("error tearing down credentials: %w", err))
@@ -2330,10 +2331,13 @@ func (c *Core) preSeal() error {
23302331
seal.StopHealthCheck()
23312332
}
23322333

2333-
c.loginMFABackend.usedCodes = nil
23342334
if c.systemBackend != nil && c.systemBackend.mfaBackend != nil {
23352335
c.systemBackend.mfaBackend.usedCodes = nil
23362336
}
2337+
if err := c.teardownLoginMFA(); err != nil {
2338+
result = multierror.Append(result, fmt.Errorf("error tearing down login MFA, error: %w", err))
2339+
}
2340+
23372341
preSealPhysical(c)
23382342

23392343
c.logger.Info("pre-seal teardown complete")
@@ -3047,6 +3051,31 @@ type LicenseState struct {
30473051
Terminated bool
30483052
}
30493053

3054+
func (c *Core) loadLoginMFAConfigs(ctx context.Context) error {
3055+
eConfigs := make([]*mfa.MFAEnforcementConfig, 0)
3056+
allNamespaces := c.collectNamespaces()
3057+
for _, ns := range allNamespaces {
3058+
err := c.loginMFABackend.loadMFAMethodConfigs(ctx, ns)
3059+
if err != nil {
3060+
return fmt.Errorf("error loading MFA method Config, namespaceid %s, error: %w", ns.ID, err)
3061+
}
3062+
3063+
loadedConfigs, err := c.loginMFABackend.loadMFAEnforcementConfigs(ctx, ns)
3064+
if err != nil {
3065+
return fmt.Errorf("error loading MFA enforcement Config, namespaceid %s, error: %w", ns.ID, err)
3066+
}
3067+
3068+
eConfigs = append(eConfigs, loadedConfigs...)
3069+
}
3070+
3071+
for _, conf := range eConfigs {
3072+
if err := c.loginMFABackend.loginMFAMethodExistenceCheck(conf); err != nil {
3073+
c.loginMFABackend.mfaLogger.Error("failed to find all MFA methods that exist in MFA enforcement configs", "configID", conf.ID, "namespaceID", conf.NamespaceID, "error", err.Error())
3074+
}
3075+
}
3076+
return nil
3077+
}
3078+
30503079
type MFACachedAuthResponse struct {
30513080
CachedAuth *logical.Auth
30523081
RequestPath string

vault/core_util.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func postUnsealPhysical(c *Core) error {
115115
return nil
116116
}
117117

118-
func loadMFAConfigs(context.Context, *Core) error { return nil }
118+
func loadPolicyMFAConfigs(context.Context, *Core) error { return nil }
119119

120120
func shouldStartClusterListener(*Core) bool { return true }
121121

vault/login_mfa.go

Lines changed: 161 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,18 @@ func NewLoginMFABackend(core *Core, logger hclog.Logger) *LoginMFABackend {
122122
}
123123

124124
func NewMFABackend(core *Core, logger hclog.Logger, prefix string, schemaFuncs []func() *memdb.TableSchema) *MFABackend {
125+
db, _ := SetupMFAMemDB(schemaFuncs)
126+
return &MFABackend{
127+
Core: core,
128+
mfaLock: &sync.RWMutex{},
129+
db: db,
130+
mfaLogger: logger.Named("mfa"),
131+
namespacer: core,
132+
methodTable: prefix,
133+
}
134+
}
135+
136+
func SetupMFAMemDB(schemaFuncs []func() *memdb.TableSchema) (*memdb.MemDB, error) {
125137
mfaSchemas := &memdb.DBSchema{
126138
Tables: make(map[string]*memdb.TableSchema),
127139
}
@@ -134,15 +146,24 @@ func NewMFABackend(core *Core, logger hclog.Logger, prefix string, schemaFuncs [
134146
mfaSchemas.Tables[schema.Name] = schema
135147
}
136148

137-
db, _ := memdb.NewMemDB(mfaSchemas)
138-
return &MFABackend{
139-
Core: core,
140-
mfaLock: &sync.RWMutex{},
141-
db: db,
142-
mfaLogger: logger.Named("mfa"),
143-
namespacer: core,
144-
methodTable: prefix,
149+
db, err := memdb.NewMemDB(mfaSchemas)
150+
if err != nil {
151+
return nil, err
145152
}
153+
return db, nil
154+
}
155+
156+
func (b *LoginMFABackend) ResetLoginMFAMemDB() error {
157+
var err error
158+
159+
db, err := SetupMFAMemDB(loginMFASchemaFuncs())
160+
if err != nil {
161+
return err
162+
}
163+
164+
b.db = db
165+
166+
return nil
146167
}
147168

148169
func (i *IdentityStore) handleMFAMethodListTOTP(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
@@ -474,6 +495,103 @@ func (i *IdentityStore) handleLoginMFAAdminDestroyUpdate(ctx context.Context, re
474495
return nil, nil
475496
}
476497

498+
// loadMFAMethodConfigs loads MFA method configs for login MFA
499+
func (b *LoginMFABackend) loadMFAMethodConfigs(ctx context.Context, ns *namespace.Namespace) error {
500+
b.mfaLogger.Trace("loading login MFA configurations")
501+
barrierView, err := b.Core.barrierViewForNamespace(ns.ID)
502+
if err != nil {
503+
return fmt.Errorf("error getting namespace view, namespaceid %s, error %w", ns.ID, err)
504+
}
505+
existing, err := barrierView.List(ctx, loginMFAConfigPrefix)
506+
if err != nil {
507+
return fmt.Errorf("failed to list MFA configurations for namespace path %s and prefix %s: %w", ns.Path, loginMFAConfigPrefix, err)
508+
}
509+
b.mfaLogger.Trace("methods collected", "num_existing", len(existing))
510+
511+
for _, key := range existing {
512+
b.mfaLogger.Trace("loading method", "method", key)
513+
514+
// Read the config from storage
515+
mConfig, err := b.getMFAConfig(ctx, loginMFAConfigPrefix+key, barrierView)
516+
if err != nil {
517+
return err
518+
}
519+
520+
if mConfig == nil {
521+
b.mfaLogger.Trace("failed to find the config related to a method", "namespace", ns.Path, "prefix", loginMFAConfigPrefix, "method", key)
522+
continue
523+
}
524+
525+
// Load the config in MemDB
526+
err = b.MemDBUpsertMFAConfig(ctx, mConfig)
527+
if err != nil {
528+
return fmt.Errorf("failed to load configuration ID %s prefix %s in MemDB: %w", mConfig.ID, loginMFAConfigPrefix, err)
529+
}
530+
}
531+
532+
b.mfaLogger.Trace("configurations restored", "namespace", ns.Path, "prefix", loginMFAConfigPrefix)
533+
534+
return nil
535+
}
536+
537+
// loadMFAEnforcementConfigs loads MFA method configs for login MFA
538+
func (b *LoginMFABackend) loadMFAEnforcementConfigs(ctx context.Context, ns *namespace.Namespace) ([]*mfa.MFAEnforcementConfig, error) {
539+
b.mfaLogger.Trace("loading login MFA enforcement configurations")
540+
barrierView, err := b.Core.barrierViewForNamespace(ns.ID)
541+
if err != nil {
542+
return nil, fmt.Errorf("error getting namespace view, namespaceid %s, error %w", ns.ID, err)
543+
}
544+
existing, err := barrierView.List(ctx, mfaLoginEnforcementPrefix)
545+
if err != nil {
546+
return nil, fmt.Errorf("failed to list MFA enforcement configurations for namespace %s with prefix %s: %w", ns.Path, mfaLoginEnforcementPrefix, err)
547+
}
548+
b.mfaLogger.Trace("enforcements configs collected", "num_existing", len(existing))
549+
550+
eConfigs := make([]*mfa.MFAEnforcementConfig, 0)
551+
for _, key := range existing {
552+
b.mfaLogger.Trace("loading enforcement", "config", key)
553+
554+
// Read the config from storage
555+
mConfig, err := b.getMFALoginEnforcementConfig(ctx, mfaLoginEnforcementPrefix+key, barrierView)
556+
if err != nil {
557+
return nil, err
558+
}
559+
560+
if mConfig == nil {
561+
b.mfaLogger.Trace("failed to find an enforcement config", "namespace", ns.Path, "prefix", mfaLoginEnforcementPrefix, "config", key)
562+
continue
563+
}
564+
565+
// Load the config in MemDB
566+
err = b.MemDBUpsertMFALoginEnforcementConfig(ctx, mConfig)
567+
if err != nil {
568+
return nil, fmt.Errorf("failed to load enforcement configuration ID %s with prefix %s in MemDB: %w", mConfig.ID, mfaLoginEnforcementPrefix, err)
569+
}
570+
571+
eConfigs = append(eConfigs, mConfig)
572+
}
573+
574+
b.mfaLogger.Trace("enforcement configurations restored", "namespace", ns.Path, "prefix", mfaLoginEnforcementPrefix)
575+
576+
return eConfigs, nil
577+
}
578+
579+
func (b *LoginMFABackend) loginMFAMethodExistenceCheck(eConfig *mfa.MFAEnforcementConfig) error {
580+
var aggErr *multierror.Error
581+
for _, confID := range eConfig.MFAMethodIDs {
582+
config, memErr := b.MemDBMFAConfigByID(confID)
583+
if memErr != nil {
584+
aggErr = multierror.Append(aggErr, memErr)
585+
return aggErr.ErrorOrNil()
586+
}
587+
if config == nil {
588+
aggErr = multierror.Append(aggErr, fmt.Errorf("found an MFA method ID in enforcement config, but failed to find the MFA method config method ID %s", confID))
589+
}
590+
}
591+
592+
return aggErr.ErrorOrNil()
593+
}
594+
477595
func (b *LoginMFABackend) handleMFALoginValidate(ctx context.Context, req *logical.Request, d *framework.FieldData) (retResp *logical.Response, retErr error) {
478596
// mfaReqID is the ID of the login request
479597
mfaReqID := d.Get("mfa_request_id").(string)
@@ -551,6 +669,22 @@ func (b *LoginMFABackend) handleMFALoginValidate(ctx context.Context, req *logic
551669
return resp, nil
552670
}
553671

672+
func (c *Core) teardownLoginMFA() error {
673+
if !c.IsDRSecondary() {
674+
// Clear any cached auth response
675+
c.mfaResponseAuthQueueLock.Lock()
676+
c.mfaResponseAuthQueue = nil
677+
c.mfaResponseAuthQueueLock.Unlock()
678+
679+
c.loginMFABackend.usedCodes = nil
680+
681+
if err := c.loginMFABackend.ResetLoginMFAMemDB(); err != nil {
682+
return err
683+
}
684+
}
685+
return nil
686+
}
687+
554688
// LoginMFACreateToken creates a token after the login MFA is validated.
555689
// It also applies the lease quotas on the original login request path.
556690
func (c *Core) LoginMFACreateToken(ctx context.Context, reqPath string, cachedAuth *logical.Auth) (*logical.Response, error) {
@@ -2529,6 +2663,25 @@ func (b *MFABackend) getMFAConfig(ctx context.Context, path string, barrierView
25292663
return &mConfig, nil
25302664
}
25312665

2666+
func (b *LoginMFABackend) getMFALoginEnforcementConfig(ctx context.Context, path string, barrierView *BarrierView) (*mfa.MFAEnforcementConfig, error) {
2667+
entry, err := barrierView.Get(ctx, path)
2668+
if err != nil {
2669+
return nil, err
2670+
}
2671+
2672+
if entry == nil {
2673+
return nil, nil
2674+
}
2675+
2676+
var mConfig mfa.MFAEnforcementConfig
2677+
err = proto.Unmarshal(entry.Value, &mConfig)
2678+
if err != nil {
2679+
return nil, err
2680+
}
2681+
2682+
return &mConfig, nil
2683+
}
2684+
25322685
func (b *LoginMFABackend) putMFALoginEnforcementConfig(ctx context.Context, eConfig *mfa.MFAEnforcementConfig) error {
25332686
entryIndex := mfaLoginEnforcementPrefix + eConfig.ID
25342687
marshaledEntry, err := proto.Marshal(eConfig)
@@ -2547,28 +2700,6 @@ func (b *LoginMFABackend) putMFALoginEnforcementConfig(ctx context.Context, eCon
25472700
})
25482701
}
25492702

2550-
func (b *LoginMFABackend) getMFALoginEnforcementConfig(ctx context.Context, key, namespaceId string) (*mfa.MFAEnforcementConfig, error) {
2551-
barrierView, err := b.Core.barrierViewForNamespace(namespaceId)
2552-
if err != nil {
2553-
return nil, err
2554-
}
2555-
entry, err := barrierView.Get(ctx, mfaLoginEnforcementPrefix+key)
2556-
if err != nil {
2557-
return nil, err
2558-
}
2559-
if entry == nil {
2560-
return nil, nil
2561-
}
2562-
2563-
var eConfig mfa.MFAEnforcementConfig
2564-
err = proto.Unmarshal(entry.Value, &eConfig)
2565-
if err != nil {
2566-
return nil, err
2567-
}
2568-
2569-
return &eConfig, nil
2570-
}
2571-
25722703
var mfaHelp = map[string][2]string{
25732704
"methods-list": {
25742705
"Lists all the available MFA methods by their name.",

0 commit comments

Comments
 (0)