From 6f6f24206159a45054151d6d8dee810352da5359 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 5 May 2017 15:01:52 -0400 Subject: [PATCH] Add logic to skip initialization in some cases and some invalidation logic --- builtin/credential/app-id/backend.go | 11 +++++++++++ builtin/credential/approle/backend.go | 17 +++++++++++++++-- builtin/credential/approle/path_role.go | 6 ++++++ builtin/credential/approle/validation.go | 6 ++++++ builtin/credential/aws/backend.go | 15 --------------- builtin/logical/ssh/backend.go | 23 +++++++++++++++++++---- builtin/logical/ssh/path_creds_create.go | 2 ++ builtin/logical/ssh/path_verify.go | 2 ++ builtin/logical/ssh/secret_otp.go | 2 ++ vault/auth.go | 8 +++++--- vault/logical_system.go | 4 ++-- vault/mount.go | 8 +++++--- 12 files changed, 75 insertions(+), 29 deletions(-) diff --git a/builtin/credential/app-id/backend.go b/builtin/credential/app-id/backend.go index 76d9a6e99..2ad4bdc99 100644 --- a/builtin/credential/app-id/backend.go +++ b/builtin/credential/app-id/backend.go @@ -72,6 +72,8 @@ func Backend(conf *logical.BackendConfig) (*framework.Backend, error) { AuthRenew: b.pathLoginRenew, Init: b.initialize, + + Invalidate: b.invalidate, } b.view = conf.StorageView @@ -91,6 +93,7 @@ type backend struct { func (b *backend) initialize() error { salt, err := salt.NewSalt(b.view, &salt.Config{ HashFunc: salt.SHA1Hash, + Location: salt.DefaultLocation, }) if err != nil { return err @@ -174,6 +177,14 @@ func (b *backend) upgradeToSalted(view logical.Storage) error { return nil } +func (b *backend) invalidate(key string) { + switch key { + case salt.DefaultLocation: + // reread the salt + b.initialize() + } +} + const backendHelp = ` The App ID credential provider is used to perform authentication from within applications or machine by pairing together two hard-to-guess diff --git a/builtin/credential/approle/backend.go b/builtin/credential/approle/backend.go index cd5d97b69..89836609d 100644 --- a/builtin/credential/approle/backend.go +++ b/builtin/credential/approle/backend.go @@ -14,7 +14,8 @@ type backend struct { // The salt value to be used by the information to be accessed only // by this backend. - salt *salt.Salt + salt *salt.Salt + saltMutex sync.RWMutex // The view to use when creating the salt view logical.Storage @@ -92,14 +93,18 @@ func Backend(conf *logical.BackendConfig) (*backend, error) { pathTidySecretID(b), }, ), - Init: b.initialize, + Init: b.initialize, + Invalidate: b.invalidate, } return b, nil } func (b *backend) initialize() error { + b.saltMutex.Lock() + defer b.saltMutex.Unlock() salt, err := salt.NewSalt(b.view, &salt.Config{ HashFunc: salt.SHA256Hash, + Location: salt.DefaultLocation, }) if err != nil { return err @@ -108,6 +113,14 @@ func (b *backend) initialize() error { return nil } +func (b *backend) invalidate(key string) { + switch key { + case salt.DefaultLocation: + // reread the salt + b.initialize() + } +} + // periodicFunc of the backend will be invoked once a minute by the RollbackManager. // RoleRole backend utilizes this function to delete expired SecretID entries. // This could mean that the SecretID may live in the backend upto 1 min after its diff --git a/builtin/credential/approle/path_role.go b/builtin/credential/approle/path_role.go index 2a1ff1a3a..ea7d84746 100644 --- a/builtin/credential/approle/path_role.go +++ b/builtin/credential/approle/path_role.go @@ -1939,7 +1939,9 @@ func (b *backend) setRoleIDEntry(s logical.Storage, roleID string, roleIDEntry * lock.Lock() defer lock.Unlock() + b.saltMutex.RLock() entryIndex := "role_id/" + b.salt.SaltID(roleID) + b.saltMutex.RUnlock() entry, err := logical.StorageEntryJSON(entryIndex, roleIDEntry) if err != nil { @@ -1963,7 +1965,9 @@ func (b *backend) roleIDEntry(s logical.Storage, roleID string) (*roleIDStorageE var result roleIDStorageEntry + b.saltMutex.RLock() entryIndex := "role_id/" + b.salt.SaltID(roleID) + b.saltMutex.RUnlock() if entry, err := s.Get(entryIndex); err != nil { return nil, err @@ -1987,7 +1991,9 @@ func (b *backend) roleIDEntryDelete(s logical.Storage, roleID string) error { lock.Lock() defer lock.Unlock() + b.saltMutex.RLock() entryIndex := "role_id/" + b.salt.SaltID(roleID) + b.saltMutex.RUnlock() return s.Delete(entryIndex) } diff --git a/builtin/credential/approle/validation.go b/builtin/credential/approle/validation.go index db668a825..d330b1ece 100644 --- a/builtin/credential/approle/validation.go +++ b/builtin/credential/approle/validation.go @@ -469,7 +469,9 @@ func (b *backend) secretIDAccessorEntry(s logical.Storage, secretIDAccessor stri var result secretIDAccessorStorageEntry // Create index entry, mapping the accessor to the token ID + b.saltMutex.RLock() entryIndex := "accessor/" + b.salt.SaltID(secretIDAccessor) + b.saltMutex.RUnlock() accessorLock := b.secretIDAccessorLock(secretIDAccessor) accessorLock.RLock() @@ -498,7 +500,9 @@ func (b *backend) createSecretIDAccessorEntry(s logical.Storage, entry *secretID entry.SecretIDAccessor = accessorUUID // Create index entry, mapping the accessor to the token ID + b.saltMutex.RLock() entryIndex := "accessor/" + b.salt.SaltID(entry.SecretIDAccessor) + b.saltMutex.RUnlock() accessorLock := b.secretIDAccessorLock(accessorUUID) accessorLock.Lock() @@ -517,7 +521,9 @@ func (b *backend) createSecretIDAccessorEntry(s logical.Storage, entry *secretID // deleteSecretIDAccessorEntry deletes the storage index mapping the accessor to a SecretID. func (b *backend) deleteSecretIDAccessorEntry(s logical.Storage, secretIDAccessor string) error { + b.saltMutex.RLock() accessorEntryIndex := "accessor/" + b.salt.SaltID(secretIDAccessor) + b.saltMutex.RUnlock() accessorLock := b.secretIDAccessorLock(secretIDAccessor) accessorLock.Lock() diff --git a/builtin/credential/aws/backend.go b/builtin/credential/aws/backend.go index fbd62c72a..a690385ab 100644 --- a/builtin/credential/aws/backend.go +++ b/builtin/credential/aws/backend.go @@ -6,7 +6,6 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" - "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" ) @@ -21,7 +20,6 @@ func Factory(conf *logical.BackendConfig) (logical.Backend, error) { type backend struct { *framework.Backend - Salt *salt.Salt // Used during initialization to set the salt view logical.Storage @@ -105,24 +103,11 @@ func Backend(conf *logical.BackendConfig) (*backend, error) { }, Invalidate: b.invalidate, - - Init: b.initialize, } return b, nil } -func (b *backend) initialize() error { - salt, err := salt.NewSalt(b.view, &salt.Config{ - HashFunc: salt.SHA256Hash, - }) - if err != nil { - return err - } - b.Salt = salt - return nil -} - // periodicFunc performs the tasks that the backend wishes to do periodically. // Currently this will be triggered once in a minute by the RollbackManager. // diff --git a/builtin/logical/ssh/backend.go b/builtin/logical/ssh/backend.go index dcfb00d85..0e17effec 100644 --- a/builtin/logical/ssh/backend.go +++ b/builtin/logical/ssh/backend.go @@ -2,6 +2,7 @@ package ssh import ( "strings" + "sync" "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" @@ -10,8 +11,9 @@ import ( type backend struct { *framework.Backend - view logical.Storage - salt *salt.Salt + view logical.Storage + salt *salt.Salt + saltMutex sync.RWMutex } func Factory(conf *logical.BackendConfig) (logical.Backend, error) { @@ -57,14 +59,19 @@ func Backend(conf *logical.BackendConfig) (*backend, error) { secretOTP(&b), }, - Init: b.Initialize, + Init: b.initialize, + + Invalidate: b.invalidate, } return &b, nil } -func (b *backend) Initialize() error { +func (b *backend) initialize() error { + b.saltMutex.Lock() + defer b.saltMutex.Unlock() salt, err := salt.NewSalt(b.view, &salt.Config{ HashFunc: salt.SHA256Hash, + Location: salt.DefaultLocation, }) if err != nil { return err @@ -73,6 +80,14 @@ func (b *backend) Initialize() error { return nil } +func (b *backend) invalidate(key string) { + switch key { + case salt.DefaultLocation: + // reread the salt + b.initialize() + } +} + const backendHelp = ` The SSH backend generates credentials allowing clients to establish SSH connections to remote hosts. diff --git a/builtin/logical/ssh/path_creds_create.go b/builtin/logical/ssh/path_creds_create.go index e2b1e0cc5..50b2147cd 100644 --- a/builtin/logical/ssh/path_creds_create.go +++ b/builtin/logical/ssh/path_creds_create.go @@ -207,6 +207,8 @@ func (b *backend) GenerateSaltedOTP() (string, string, error) { if err != nil { return "", "", err } + b.saltMutex.RLock() + defer b.saltMutex.RUnlock() return str, b.salt.SaltID(str), nil } diff --git a/builtin/logical/ssh/path_verify.go b/builtin/logical/ssh/path_verify.go index 9cb98ade8..6f50facd7 100644 --- a/builtin/logical/ssh/path_verify.go +++ b/builtin/logical/ssh/path_verify.go @@ -57,7 +57,9 @@ func (b *backend) pathVerifyWrite(req *logical.Request, d *framework.FieldData) // Create the salt of OTP because entry would have been create with the // salt and not directly of the OTP. Salt will yield the same value which // because the seed is the same, the backend salt. + b.saltMutex.RLock() otpSalted := b.salt.SaltID(otp) + b.saltMutex.RUnlock() // Return nil if there is no entry found for the OTP otpEntry, err := b.getOTP(req.Storage, otpSalted) diff --git a/builtin/logical/ssh/secret_otp.go b/builtin/logical/ssh/secret_otp.go index d0e4dd58a..c7f73ee3c 100644 --- a/builtin/logical/ssh/secret_otp.go +++ b/builtin/logical/ssh/secret_otp.go @@ -33,6 +33,8 @@ func (b *backend) secretOTPRevoke(req *logical.Request, d *framework.FieldData) return nil, fmt.Errorf("secret is missing internal data") } + b.saltMutex.RLock() + defer b.saltMutex.RUnlock() err := req.Storage.Delete("otp/" + b.salt.SaltID(otp)) if err != nil { return nil, err diff --git a/vault/auth.go b/vault/auth.go index 5a5e68b2f..f0c90ffad 100644 --- a/vault/auth.go +++ b/vault/auth.go @@ -42,7 +42,7 @@ var ( ) // enableCredential is used to enable a new credential backend -func (c *Core) enableCredential(entry *MountEntry) error { +func (c *Core) enableCredential(entry *MountEntry, skipInitialization bool) error { // Ensure we end the path in a slash if !strings.HasSuffix(entry.Path, "/") { entry.Path += "/" @@ -99,8 +99,10 @@ func (c *Core) enableCredential(entry *MountEntry) error { return fmt.Errorf("nil backend returned from %q factory", entry.Type) } - if err := backend.Initialize(); err != nil { - return err + if !skipInitialization { + if err := backend.Initialize(); err != nil { + return err + } } // Update the auth table diff --git a/vault/logical_system.go b/vault/logical_system.go index 98aef6275..6a70e13ca 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -1210,7 +1210,7 @@ func (b *SystemBackend) handleMount( } // Attempt mount - if err := b.Core.mount(me); err != nil { + if err := b.Core.mount(me, false); err != nil { b.Backend.Logger().Error("sys: mount failed", "path", me.Path, "error", err) return handleError(err) } @@ -1642,7 +1642,7 @@ func (b *SystemBackend) handleEnableAuth( } // Attempt enabling - if err := b.Core.enableCredential(me); err != nil { + if err := b.Core.enableCredential(me, false); err != nil { b.Backend.Logger().Error("sys: enable auth mount failed", "path", me.Path, "error", err) return handleError(err) } diff --git a/vault/mount.go b/vault/mount.go index d428eee8b..d477fe89e 100644 --- a/vault/mount.go +++ b/vault/mount.go @@ -169,7 +169,7 @@ func (e *MountEntry) Clone() *MountEntry { } // Mount is used to mount a new backend to the mount table. -func (c *Core) mount(entry *MountEntry) error { +func (c *Core) mount(entry *MountEntry, skipInitialization bool) error { // Ensure we end the path in a slash if !strings.HasSuffix(entry.Path, "/") { entry.Path += "/" @@ -219,8 +219,10 @@ func (c *Core) mount(entry *MountEntry) error { // Call initialize; this takes care of init tasks that must be run after // the ignore paths are collected - if err := backend.Initialize(); err != nil { - return err + if !skipInitialization { + if err := backend.Initialize(); err != nil { + return err + } } newTable := c.mounts.shallowClone()