2015-03-18 22:30:31 +00:00
|
|
|
package vault
|
|
|
|
|
2015-03-18 22:46:07 +00:00
|
|
|
import (
|
2018-01-19 06:44:44 +00:00
|
|
|
"context"
|
2015-03-18 22:46:07 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2015-03-19 16:54:57 +00:00
|
|
|
"strings"
|
2015-03-18 22:46:07 +00:00
|
|
|
|
2021-07-16 00:17:31 +00:00
|
|
|
"github.com/hashicorp/go-secure-stdlib/strutil"
|
2022-04-26 16:13:45 +00:00
|
|
|
"github.com/hashicorp/go-uuid"
|
2018-11-07 01:21:24 +00:00
|
|
|
"github.com/hashicorp/vault/builtin/plugin"
|
2019-04-13 07:44:06 +00:00
|
|
|
"github.com/hashicorp/vault/helper/namespace"
|
2019-04-12 21:54:35 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
|
|
|
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
|
|
|
"github.com/hashicorp/vault/sdk/logical"
|
2015-03-18 22:46:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// coreAuthConfigPath is used to store the auth configuration.
|
|
|
|
// Auth configuration is protected within the Vault itself, which means it
|
|
|
|
// can only be viewed or modified after an unseal.
|
|
|
|
coreAuthConfigPath = "core/auth"
|
|
|
|
|
2017-02-17 04:09:39 +00:00
|
|
|
// coreLocalAuthConfigPath is used to store credential configuration for
|
|
|
|
// local (non-replicated) mounts
|
|
|
|
coreLocalAuthConfigPath = "core/local-auth"
|
|
|
|
|
2015-03-18 22:46:07 +00:00
|
|
|
// credentialBarrierPrefix is the prefix to the UUID used in the
|
|
|
|
// barrier view for the credential backends.
|
|
|
|
credentialBarrierPrefix = "auth/"
|
|
|
|
|
2015-03-19 16:56:39 +00:00
|
|
|
// credentialRoutePrefix is the mount prefix used for the router
|
|
|
|
credentialRoutePrefix = "auth/"
|
2016-05-26 17:38:51 +00:00
|
|
|
|
|
|
|
// credentialTableType is the value we expect to find for the credential
|
|
|
|
// table and corresponding entries
|
|
|
|
credentialTableType = "auth"
|
2015-03-18 22:46:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2015-11-02 16:01:00 +00:00
|
|
|
// errLoadAuthFailed if loadCredentials encounters an error
|
2015-09-09 19:42:29 +00:00
|
|
|
errLoadAuthFailed = errors.New("failed to setup auth table")
|
2017-04-24 19:15:50 +00:00
|
|
|
|
|
|
|
// credentialAliases maps old backend names to new backend names, allowing us
|
|
|
|
// to move/rename backends but maintain backwards compatibility
|
|
|
|
credentialAliases = map[string]string{"aws-ec2": "aws"}
|
2022-02-18 16:04:21 +00:00
|
|
|
|
|
|
|
// protectedAuths marks auth mounts that are protected and cannot be remounted
|
|
|
|
protectedAuths = []string{
|
|
|
|
"auth/token",
|
|
|
|
}
|
2015-03-18 22:46:07 +00:00
|
|
|
)
|
|
|
|
|
2015-03-19 02:36:17 +00:00
|
|
|
// enableCredential is used to enable a new credential backend
|
2018-01-19 06:44:44 +00:00
|
|
|
func (c *Core) enableCredential(ctx context.Context, entry *MountEntry) error {
|
2019-10-27 20:30:38 +00:00
|
|
|
// Enable credential internally
|
|
|
|
if err := c.enableCredentialInternal(ctx, entry, MountTableUpdateStorage); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Re-evaluate filtered paths
|
|
|
|
if err := runFilteredPathsEvaluation(ctx, c); err != nil {
|
|
|
|
c.logger.Error("failed to evaluate filtered paths", "error", err)
|
2019-10-29 16:42:13 +00:00
|
|
|
|
|
|
|
// We failed to evaluate filtered paths so we are undoing the mount operation
|
|
|
|
if disableCredentialErr := c.disableCredentialInternal(ctx, entry.Path, MountTableUpdateStorage); disableCredentialErr != nil {
|
|
|
|
c.logger.Error("failed to disable credential", "error", disableCredentialErr)
|
|
|
|
}
|
2019-10-27 20:30:38 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
2018-09-18 03:03:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// enableCredential is used to enable a new credential backend
|
|
|
|
func (c *Core) enableCredentialInternal(ctx context.Context, entry *MountEntry, updateStorage bool) error {
|
2015-04-03 21:24:00 +00:00
|
|
|
// Ensure we end the path in a slash
|
|
|
|
if !strings.HasSuffix(entry.Path, "/") {
|
|
|
|
entry.Path += "/"
|
|
|
|
}
|
|
|
|
|
2015-03-19 02:36:17 +00:00
|
|
|
// Ensure there is a name
|
2015-04-03 21:24:00 +00:00
|
|
|
if entry.Path == "/" {
|
2015-03-19 16:54:57 +00:00
|
|
|
return fmt.Errorf("backend path must be specified")
|
|
|
|
}
|
2015-03-19 02:36:17 +00:00
|
|
|
|
2015-11-11 16:44:07 +00:00
|
|
|
c.authLock.Lock()
|
|
|
|
defer c.authLock.Unlock()
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
ns, err := namespace.FromContext(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
entry.NamespaceID = ns.ID
|
|
|
|
entry.namespace = ns
|
|
|
|
|
|
|
|
// Populate cache
|
|
|
|
NamespaceByID(ctx, ns.ID, c)
|
|
|
|
|
2019-06-04 17:33:36 +00:00
|
|
|
// Basic check for matching names
|
2015-03-19 02:36:17 +00:00
|
|
|
for _, ent := range c.auth.Entries {
|
2018-09-18 03:03:00 +00:00
|
|
|
if ns.ID == ent.NamespaceID {
|
|
|
|
switch {
|
|
|
|
// Existing is oauth/github/ new is oauth/ or
|
|
|
|
// existing is oauth/ and new is oauth/github/
|
|
|
|
case strings.HasPrefix(ent.Path, entry.Path):
|
|
|
|
fallthrough
|
|
|
|
case strings.HasPrefix(entry.Path, ent.Path):
|
2019-06-04 17:33:36 +00:00
|
|
|
return logical.CodedError(409, fmt.Sprintf("path is already in use at %s", ent.Path))
|
2018-09-18 03:03:00 +00:00
|
|
|
}
|
2015-03-19 02:36:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the token backend is a singleton
|
|
|
|
if entry.Type == "token" {
|
|
|
|
return fmt.Errorf("token credential backend cannot be instantiated")
|
|
|
|
}
|
|
|
|
|
2019-06-04 17:33:36 +00:00
|
|
|
// Check for conflicts according to the router
|
2018-09-18 03:03:00 +00:00
|
|
|
if conflict := c.router.MountConflict(ctx, credentialRoutePrefix+entry.Path); conflict != "" {
|
2017-11-06 20:29:09 +00:00
|
|
|
return logical.CodedError(409, fmt.Sprintf("existing mount at %s", conflict))
|
2017-01-17 18:02:29 +00:00
|
|
|
}
|
|
|
|
|
2015-03-19 02:36:17 +00:00
|
|
|
// Generate a new UUID and view
|
2017-02-17 04:09:39 +00:00
|
|
|
if entry.UUID == "" {
|
|
|
|
entryUUID, err := uuid.GenerateUUID()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
entry.UUID = entryUUID
|
2016-01-13 18:40:08 +00:00
|
|
|
}
|
2018-03-21 19:04:27 +00:00
|
|
|
if entry.BackendAwareUUID == "" {
|
|
|
|
bUUID, err := uuid.GenerateUUID()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
entry.BackendAwareUUID = bUUID
|
|
|
|
}
|
2017-06-26 17:14:36 +00:00
|
|
|
if entry.Accessor == "" {
|
|
|
|
accessor, err := c.generateMountAccessor("auth_" + entry.Type)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
entry.Accessor = accessor
|
|
|
|
}
|
2018-03-02 17:18:39 +00:00
|
|
|
// Sync values to the cache
|
|
|
|
entry.SyncCache()
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
viewPath := entry.ViewPath()
|
2017-02-17 04:09:39 +00:00
|
|
|
view := NewBarrierView(c.barrier, viewPath)
|
2018-09-18 03:03:00 +00:00
|
|
|
|
2019-10-27 20:30:38 +00:00
|
|
|
// Singleton mounts cannot be filtered on a per-secondary basis
|
|
|
|
// from replication
|
|
|
|
if strutil.StrListContains(singletonMounts, entry.Type) {
|
|
|
|
addFilterablePath(c, viewPath)
|
|
|
|
}
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
nilMount, err := preprocessMount(c, entry, view)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
origViewReadOnlyErr := view.getReadOnlyErr()
|
|
|
|
|
2018-02-09 19:04:25 +00:00
|
|
|
// Mark the view as read-only until the mounting is complete and
|
|
|
|
// ensure that it is reset after. This ensures that there will be no
|
|
|
|
// writes during the construction of the backend.
|
|
|
|
view.setReadOnlyErr(logical.ErrSetupReadOnly)
|
2018-09-18 03:03:00 +00:00
|
|
|
defer view.setReadOnlyErr(origViewReadOnlyErr)
|
2018-02-09 19:04:25 +00:00
|
|
|
|
2017-10-23 20:42:56 +00:00
|
|
|
var backend logical.Backend
|
2018-09-18 03:03:00 +00:00
|
|
|
// Create the new backend
|
2017-02-17 04:09:39 +00:00
|
|
|
sysView := c.mountEntrySysView(entry)
|
2018-03-21 19:04:27 +00:00
|
|
|
backend, err = c.newCredentialBackend(ctx, entry, sysView, view)
|
2015-07-01 00:30:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-03-04 21:35:41 +00:00
|
|
|
if backend == nil {
|
|
|
|
return fmt.Errorf("nil backend returned from %q factory", entry.Type)
|
|
|
|
}
|
2015-07-01 00:30:43 +00:00
|
|
|
|
Backend plugin system (#2874)
* Add backend plugin changes
* Fix totp backend plugin tests
* Fix logical/plugin InvalidateKey test
* Fix plugin catalog CRUD test, fix NoopBackend
* Clean up commented code block
* Fix system backend mount test
* Set plugin_name to omitempty, fix handleMountTable config parsing
* Clean up comments, keep shim connections alive until cleanup
* Include pluginClient, disallow LookupPlugin call from within a plugin
* Add wrapper around backendPluginClient for proper cleanup
* Add logger shim tests
* Add logger, storage, and system shim tests
* Use pointer receivers for system view shim
* Use plugin name if no path is provided on mount
* Enable plugins for auth backends
* Add backend type attribute, move builtin/plugin/package
* Fix merge conflict
* Fix missing plugin name in mount config
* Add integration tests on enabling auth backend plugins
* Remove dependency cycle on mock-plugin
* Add passthrough backend plugin, use logical.BackendType to determine lease generation
* Remove vault package dependency on passthrough package
* Add basic impl test for passthrough plugin
* Incorporate feedback; set b.backend after shims creation on backendPluginServer
* Fix totp plugin test
* Add plugin backends docs
* Fix tests
* Fix builtin/plugin tests
* Remove flatten from PluginRunner fields
* Move mock plugin to logical/plugin, remove totp and passthrough plugins
* Move pluginMap into newPluginClient
* Do not create storage RPC connection on HandleRequest and HandleExistenceCheck
* Change shim logger's Fatal to no-op
* Change BackendType to uint32, match UX backend types
* Change framework.Backend Setup signature
* Add Setup func to logical.Backend interface
* Move OptionallyEnableMlock call into plugin.Serve, update docs and comments
* Remove commented var in plugin package
* RegisterLicense on logical.Backend interface (#3017)
* Add RegisterLicense to logical.Backend interface
* Update RegisterLicense to use callback func on framework.Backend
* Refactor framework.Backend.RegisterLicense
* plugin: Prevent plugin.SystemViewClient.ResponseWrapData from getting JWTs
* plugin: Revert BackendType to remove TypePassthrough and related references
* Fix typo in plugin backends docs
2017-07-20 17:28:40 +00:00
|
|
|
// Check for the correct backend type
|
|
|
|
backendType := backend.Type()
|
2018-11-07 01:21:24 +00:00
|
|
|
if backendType != logical.TypeCredential {
|
|
|
|
return fmt.Errorf("cannot mount %q of type %q as an auth backend", entry.Type, backendType)
|
2018-09-18 03:03:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
addPathCheckers(c, entry, backend, viewPath)
|
|
|
|
|
|
|
|
// If the mount is filtered or we are on a DR secondary we don't want to
|
|
|
|
// keep the actual backend running, so we clean it up and set it to nil
|
|
|
|
// so the router does not have a pointer to the object.
|
|
|
|
if nilMount {
|
|
|
|
backend.Cleanup(ctx)
|
|
|
|
backend = nil
|
Backend plugin system (#2874)
* Add backend plugin changes
* Fix totp backend plugin tests
* Fix logical/plugin InvalidateKey test
* Fix plugin catalog CRUD test, fix NoopBackend
* Clean up commented code block
* Fix system backend mount test
* Set plugin_name to omitempty, fix handleMountTable config parsing
* Clean up comments, keep shim connections alive until cleanup
* Include pluginClient, disallow LookupPlugin call from within a plugin
* Add wrapper around backendPluginClient for proper cleanup
* Add logger shim tests
* Add logger, storage, and system shim tests
* Use pointer receivers for system view shim
* Use plugin name if no path is provided on mount
* Enable plugins for auth backends
* Add backend type attribute, move builtin/plugin/package
* Fix merge conflict
* Fix missing plugin name in mount config
* Add integration tests on enabling auth backend plugins
* Remove dependency cycle on mock-plugin
* Add passthrough backend plugin, use logical.BackendType to determine lease generation
* Remove vault package dependency on passthrough package
* Add basic impl test for passthrough plugin
* Incorporate feedback; set b.backend after shims creation on backendPluginServer
* Fix totp plugin test
* Add plugin backends docs
* Fix tests
* Fix builtin/plugin tests
* Remove flatten from PluginRunner fields
* Move mock plugin to logical/plugin, remove totp and passthrough plugins
* Move pluginMap into newPluginClient
* Do not create storage RPC connection on HandleRequest and HandleExistenceCheck
* Change shim logger's Fatal to no-op
* Change BackendType to uint32, match UX backend types
* Change framework.Backend Setup signature
* Add Setup func to logical.Backend interface
* Move OptionallyEnableMlock call into plugin.Serve, update docs and comments
* Remove commented var in plugin package
* RegisterLicense on logical.Backend interface (#3017)
* Add RegisterLicense to logical.Backend interface
* Update RegisterLicense to use callback func on framework.Backend
* Refactor framework.Backend.RegisterLicense
* plugin: Prevent plugin.SystemViewClient.ResponseWrapData from getting JWTs
* plugin: Revert BackendType to remove TypePassthrough and related references
* Fix typo in plugin backends docs
2017-07-20 17:28:40 +00:00
|
|
|
}
|
|
|
|
|
2015-03-19 02:36:17 +00:00
|
|
|
// Update the auth table
|
2016-09-13 15:50:14 +00:00
|
|
|
newTable := c.auth.shallowClone()
|
2015-03-19 02:36:17 +00:00
|
|
|
newTable.Entries = append(newTable.Entries, entry)
|
2018-09-18 03:03:00 +00:00
|
|
|
if updateStorage {
|
|
|
|
if err := c.persistAuth(ctx, newTable, &entry.Local); err != nil {
|
|
|
|
if err == logical.ErrReadOnly && c.perfStandby {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return errors.New("failed to update auth table")
|
|
|
|
}
|
2015-03-19 02:36:17 +00:00
|
|
|
}
|
2015-11-11 16:44:07 +00:00
|
|
|
|
2015-03-19 02:36:17 +00:00
|
|
|
c.auth = newTable
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
if err := c.router.Mount(backend, credentialRoutePrefix+entry.Path, entry, view); err != nil {
|
2017-01-17 20:15:28 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-05 23:55:40 +00:00
|
|
|
if !nilMount {
|
|
|
|
// restore the original readOnlyErr, so we can write to the view in
|
|
|
|
// Initialize() if necessary
|
|
|
|
view.setReadOnlyErr(origViewReadOnlyErr)
|
|
|
|
// initialize, using the core's active context.
|
|
|
|
err := backend.Initialize(c.activeContext, &logical.InitializationRequest{Storage: view})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-19 20:45:17 +00:00
|
|
|
if c.logger.IsInfo() {
|
2018-04-03 00:46:59 +00:00
|
|
|
c.logger.Info("enabled credential backend", "path", entry.Path, "type", entry.Type)
|
2016-08-19 20:45:17 +00:00
|
|
|
}
|
2015-03-19 02:36:17 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-10-27 20:30:38 +00:00
|
|
|
// disableCredential is used to disable an existing credential backend
|
2018-01-19 06:44:44 +00:00
|
|
|
func (c *Core) disableCredential(ctx context.Context, path string) error {
|
2015-04-03 21:24:00 +00:00
|
|
|
// Ensure we end the path in a slash
|
|
|
|
if !strings.HasSuffix(path, "/") {
|
|
|
|
path += "/"
|
|
|
|
}
|
|
|
|
|
2015-03-19 02:36:17 +00:00
|
|
|
// Ensure the token backend is not affected
|
2015-04-03 21:24:00 +00:00
|
|
|
if path == "token/" {
|
2017-07-13 17:57:14 +00:00
|
|
|
return fmt.Errorf("token credential backend cannot be disabled")
|
2015-03-19 02:36:17 +00:00
|
|
|
}
|
|
|
|
|
2019-11-12 17:17:37 +00:00
|
|
|
// Disable credential internally
|
|
|
|
if err := c.disableCredentialInternal(ctx, path, MountTableUpdateStorage); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Re-evaluate filtered paths
|
|
|
|
if err := runFilteredPathsEvaluation(ctx, c); err != nil {
|
|
|
|
// Even we failed to evaluate filtered paths, the unmount operation was still successful
|
|
|
|
c.logger.Error("failed to evaluate filtered paths", "error", err)
|
|
|
|
}
|
|
|
|
return nil
|
2018-09-18 03:03:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Core) disableCredentialInternal(ctx context.Context, path string, updateStorage bool) error {
|
|
|
|
path = credentialRoutePrefix + path
|
|
|
|
|
|
|
|
ns, err := namespace.FromContext(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify exact match of the route
|
|
|
|
match := c.router.MatchingMount(ctx, path)
|
|
|
|
if match == "" || ns.Path+path != match {
|
|
|
|
return fmt.Errorf("no matching mount")
|
|
|
|
}
|
|
|
|
|
2015-04-03 23:07:45 +00:00
|
|
|
// Store the view for this backend
|
2018-09-18 03:03:00 +00:00
|
|
|
view := c.router.MatchingStorageByAPIPath(ctx, path)
|
2015-04-03 23:09:06 +00:00
|
|
|
if view == nil {
|
2018-09-18 03:03:00 +00:00
|
|
|
return fmt.Errorf("no matching backend %q", path)
|
2015-04-03 23:09:06 +00:00
|
|
|
}
|
2015-04-03 23:07:45 +00:00
|
|
|
|
2017-10-23 21:15:56 +00:00
|
|
|
// Get the backend/mount entry for this path, used to remove ignored
|
|
|
|
// replication prefixes
|
2018-09-18 03:03:00 +00:00
|
|
|
backend := c.router.MatchingBackend(ctx, path)
|
|
|
|
entry := c.router.MatchingMountEntry(ctx, path)
|
2017-10-23 21:15:56 +00:00
|
|
|
|
2015-04-03 23:07:45 +00:00
|
|
|
// Mark the entry as tainted
|
2022-02-18 16:04:21 +00:00
|
|
|
if err := c.taintCredEntry(ctx, ns.ID, path, updateStorage); err != nil {
|
2017-07-13 17:57:14 +00:00
|
|
|
return err
|
2015-04-03 23:07:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Taint the router path to prevent routing
|
2018-09-18 03:03:00 +00:00
|
|
|
if err := c.router.Taint(ctx, path); err != nil {
|
2017-07-13 17:57:14 +00:00
|
|
|
return err
|
2015-04-03 23:07:45 +00:00
|
|
|
}
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
if c.expiration != nil && backend != nil {
|
2017-10-23 20:42:56 +00:00
|
|
|
// Revoke credentials from this path
|
2018-09-18 03:03:00 +00:00
|
|
|
ns, err := namespace.FromContext(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
revokeCtx := namespace.ContextWithNamespace(c.activeContext, ns)
|
|
|
|
if err := c.expiration.RevokePrefix(revokeCtx, path, true); err != nil {
|
2017-10-23 20:42:56 +00:00
|
|
|
return err
|
|
|
|
}
|
2018-09-18 03:03:00 +00:00
|
|
|
}
|
2017-10-23 20:42:56 +00:00
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
if backend != nil {
|
2017-10-23 20:42:56 +00:00
|
|
|
// Call cleanup function if it exists
|
2018-01-19 06:44:44 +00:00
|
|
|
backend.Cleanup(ctx)
|
2017-03-04 21:35:41 +00:00
|
|
|
}
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
viewPath := entry.ViewPath()
|
2017-10-23 20:42:56 +00:00
|
|
|
switch {
|
2018-09-18 03:03:00 +00:00
|
|
|
case !updateStorage:
|
2019-06-20 20:02:11 +00:00
|
|
|
// Don't attempt to clear data, replication will handle this
|
|
|
|
case c.IsDRSecondary():
|
|
|
|
// If we are a dr secondary we want to clear the view, but the provided
|
|
|
|
// view is marked as read only. We use the barrier here to get around
|
|
|
|
// it.
|
|
|
|
|
|
|
|
if err := logical.ClearViewWithLogging(ctx, NewBarrierView(c.barrier, viewPath), c.logger.Named("auth.deletion").With("namespace", ns.ID, "path", path)); err != nil {
|
|
|
|
c.logger.Error("failed to clear view for path being unmounted", "error", err, "path", path)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
case entry.Local, !c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary):
|
2017-10-23 20:42:56 +00:00
|
|
|
// Have writable storage, remove the whole thing
|
2019-06-20 20:02:11 +00:00
|
|
|
if err := logical.ClearViewWithLogging(ctx, view, c.logger.Named("auth.deletion").With("namespace", ns.ID, "path", path)); err != nil {
|
2018-04-03 00:46:59 +00:00
|
|
|
c.logger.Error("failed to clear view for path being unmounted", "error", err, "path", path)
|
2017-07-13 17:57:14 +00:00
|
|
|
return err
|
2015-03-19 02:36:17 +00:00
|
|
|
}
|
2017-10-23 20:42:56 +00:00
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
case !entry.Local && c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary):
|
|
|
|
if err := clearIgnoredPaths(ctx, c, backend, viewPath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-19 02:36:17 +00:00
|
|
|
}
|
|
|
|
|
2015-04-03 23:07:45 +00:00
|
|
|
// Remove the mount table entry
|
2018-09-18 03:03:00 +00:00
|
|
|
if err := c.removeCredEntry(ctx, strings.TrimPrefix(path, credentialRoutePrefix), updateStorage); err != nil {
|
2017-07-13 17:57:14 +00:00
|
|
|
return err
|
2015-04-03 23:07:45 +00:00
|
|
|
}
|
2018-09-18 03:03:00 +00:00
|
|
|
|
2019-06-04 17:33:36 +00:00
|
|
|
// Unmount the backend
|
|
|
|
if err := c.router.Unmount(ctx, path); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
removePathCheckers(c, entry, viewPath)
|
|
|
|
|
2021-08-17 22:34:43 +00:00
|
|
|
if !c.IsPerfSecondary() {
|
|
|
|
if c.quotaManager != nil {
|
|
|
|
if err := c.quotaManager.HandleBackendDisabling(ctx, ns.Path, path); err != nil {
|
|
|
|
c.logger.Error("failed to update quotas after disabling auth", "path", path, "error", err)
|
|
|
|
return err
|
|
|
|
}
|
2020-07-02 01:14:33 +00:00
|
|
|
}
|
2020-06-26 21:13:16 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 20:45:17 +00:00
|
|
|
if c.logger.IsInfo() {
|
2018-04-03 00:46:59 +00:00
|
|
|
c.logger.Info("disabled credential backend", "path", path)
|
2016-08-19 20:45:17 +00:00
|
|
|
}
|
2018-09-18 03:03:00 +00:00
|
|
|
|
2017-07-13 17:57:14 +00:00
|
|
|
return nil
|
2015-04-03 23:07:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// removeCredEntry is used to remove an entry in the auth table
|
2018-09-18 03:03:00 +00:00
|
|
|
func (c *Core) removeCredEntry(ctx context.Context, path string, updateStorage bool) error {
|
2017-01-17 18:02:29 +00:00
|
|
|
c.authLock.Lock()
|
|
|
|
defer c.authLock.Unlock()
|
|
|
|
|
2015-04-03 23:07:45 +00:00
|
|
|
// Taint the entry from the auth table
|
2016-09-13 15:50:14 +00:00
|
|
|
newTable := c.auth.shallowClone()
|
2018-09-18 03:03:00 +00:00
|
|
|
entry, err := newTable.remove(ctx, path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-03-02 19:37:59 +00:00
|
|
|
if entry == nil {
|
2018-04-03 00:46:59 +00:00
|
|
|
c.logger.Error("nil entry found removing entry in auth table", "path", path)
|
2017-03-02 19:37:59 +00:00
|
|
|
return logical.CodedError(500, "failed to remove entry in auth table")
|
|
|
|
}
|
2015-04-03 23:07:45 +00:00
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
if updateStorage {
|
|
|
|
// Update the auth table
|
|
|
|
if err := c.persistAuth(ctx, newTable, &entry.Local); err != nil {
|
|
|
|
if err == logical.ErrReadOnly && c.perfStandby {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.New("failed to update auth table")
|
|
|
|
}
|
2015-04-03 23:07:45 +00:00
|
|
|
}
|
2015-11-11 16:44:07 +00:00
|
|
|
|
2015-04-03 23:07:45 +00:00
|
|
|
c.auth = newTable
|
2015-11-11 16:44:07 +00:00
|
|
|
|
2015-04-03 23:07:45 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-02-18 16:04:21 +00:00
|
|
|
func (c *Core) remountCredential(ctx context.Context, src, dst namespace.MountPathDetails, updateStorage bool) error {
|
|
|
|
ns, err := namespace.FromContext(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !strings.HasPrefix(src.MountPath, credentialRoutePrefix) {
|
|
|
|
return fmt.Errorf("cannot remount non-auth mount %q", src.MountPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !strings.HasPrefix(dst.MountPath, credentialRoutePrefix) {
|
|
|
|
return fmt.Errorf("cannot remount auth mount to non-auth mount %q", dst.MountPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, auth := range protectedAuths {
|
|
|
|
if strings.HasPrefix(src.MountPath, auth) {
|
|
|
|
return fmt.Errorf("cannot remount %q", src.MountPath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, auth := range protectedAuths {
|
|
|
|
if strings.HasPrefix(dst.MountPath, auth) {
|
|
|
|
return fmt.Errorf("cannot remount to %q", dst.MountPath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
srcRelativePath := src.GetRelativePath(ns)
|
|
|
|
dstRelativePath := dst.GetRelativePath(ns)
|
|
|
|
|
|
|
|
// Verify exact match of the route
|
|
|
|
srcMatch := c.router.MatchingMountEntry(ctx, srcRelativePath)
|
|
|
|
if srcMatch == nil {
|
|
|
|
return fmt.Errorf("no matching mount at %q", src.Namespace.Path+src.MountPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
if match := c.router.MountConflict(ctx, dstRelativePath); match != "" {
|
|
|
|
return fmt.Errorf("path in use at %q", match)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark the entry as tainted
|
|
|
|
if err := c.taintCredEntry(ctx, src.Namespace.ID, src.MountPath, updateStorage); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Taint the router path to prevent routing
|
|
|
|
if err := c.router.Taint(ctx, srcRelativePath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.expiration != nil {
|
|
|
|
revokeCtx := namespace.ContextWithNamespace(ctx, src.Namespace)
|
|
|
|
// Revoke all the dynamic keys
|
|
|
|
if err := c.expiration.RevokePrefix(revokeCtx, src.MountPath, true); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.authLock.Lock()
|
|
|
|
if match := c.router.MountConflict(ctx, dstRelativePath); match != "" {
|
|
|
|
c.authLock.Unlock()
|
|
|
|
return fmt.Errorf("path in use at %q", match)
|
|
|
|
}
|
|
|
|
|
|
|
|
srcMatch.Tainted = false
|
|
|
|
srcMatch.NamespaceID = dst.Namespace.ID
|
|
|
|
srcMatch.namespace = dst.Namespace
|
|
|
|
srcPath := srcMatch.Path
|
|
|
|
srcMatch.Path = strings.TrimPrefix(dst.MountPath, credentialRoutePrefix)
|
|
|
|
|
|
|
|
// Update the mount table
|
|
|
|
if err := c.persistAuth(ctx, c.auth, &srcMatch.Local); err != nil {
|
|
|
|
srcMatch.Path = srcPath
|
|
|
|
srcMatch.Tainted = true
|
|
|
|
c.authLock.Unlock()
|
|
|
|
if err == logical.ErrReadOnly && c.perfStandby {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Errorf("failed to update auth table with error %+v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remount the backend, setting the existing route entry
|
|
|
|
// against the new path
|
|
|
|
if err := c.router.Remount(ctx, srcRelativePath, dstRelativePath); err != nil {
|
|
|
|
c.authLock.Unlock()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.authLock.Unlock()
|
|
|
|
|
|
|
|
// Un-taint the new path in the router
|
|
|
|
if err := c.router.Untaint(ctx, dstRelativePath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-10-27 20:30:38 +00:00
|
|
|
// remountCredEntryForceInternal takes a copy of the mount entry for the path and fully
|
|
|
|
// unmounts and remounts the backend to pick up any changes, such as filtered
|
|
|
|
// paths. This should be only used internal.
|
|
|
|
func (c *Core) remountCredEntryForceInternal(ctx context.Context, path string, updateStorage bool) error {
|
|
|
|
fullPath := credentialRoutePrefix + path
|
|
|
|
me := c.router.MatchingMountEntry(ctx, fullPath)
|
|
|
|
if me == nil {
|
|
|
|
return fmt.Errorf("cannot find mount for path %q", path)
|
|
|
|
}
|
|
|
|
|
|
|
|
me, err := me.Clone()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := c.disableCredentialInternal(ctx, path, updateStorage); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable credential internally
|
|
|
|
if err := c.enableCredentialInternal(ctx, me, updateStorage); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Re-evaluate filtered paths
|
|
|
|
if err := runFilteredPathsEvaluation(ctx, c); err != nil {
|
|
|
|
c.logger.Error("failed to evaluate filtered paths", "error", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-04-03 23:07:45 +00:00
|
|
|
// taintCredEntry is used to mark an entry in the auth table as tainted
|
2022-02-18 16:04:21 +00:00
|
|
|
func (c *Core) taintCredEntry(ctx context.Context, nsID, path string, updateStorage bool) error {
|
2017-01-17 18:02:29 +00:00
|
|
|
c.authLock.Lock()
|
|
|
|
defer c.authLock.Unlock()
|
|
|
|
|
2015-04-03 23:07:45 +00:00
|
|
|
// Taint the entry from the auth table
|
2015-11-11 16:44:07 +00:00
|
|
|
// We do this on the original since setting the taint operates
|
|
|
|
// on the entries which a shallow clone shares anyways
|
2022-02-18 16:04:21 +00:00
|
|
|
entry, err := c.auth.setTaint(nsID, strings.TrimPrefix(path, credentialRoutePrefix), true, mountStateUnmounting)
|
2018-09-18 03:03:00 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-04-03 23:07:45 +00:00
|
|
|
|
2015-03-19 02:36:17 +00:00
|
|
|
// Ensure there was a match
|
2017-03-02 19:37:59 +00:00
|
|
|
if entry == nil {
|
2022-02-18 16:04:21 +00:00
|
|
|
return fmt.Errorf("no matching backend for path %q namespaceID %q", path, nsID)
|
2015-03-19 02:36:17 +00:00
|
|
|
}
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
if updateStorage {
|
|
|
|
// Update the auth table
|
|
|
|
if err := c.persistAuth(ctx, c.auth, &entry.Local); err != nil {
|
|
|
|
if err == logical.ErrReadOnly && c.perfStandby {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return errors.New("failed to update auth table")
|
|
|
|
}
|
2015-03-19 02:36:17 +00:00
|
|
|
}
|
2015-11-11 16:44:07 +00:00
|
|
|
|
2015-03-19 02:36:17 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-03-18 22:46:07 +00:00
|
|
|
// loadCredentials is invoked as part of postUnseal to load the auth table
|
2018-01-19 06:44:44 +00:00
|
|
|
func (c *Core) loadCredentials(ctx context.Context) error {
|
2015-03-18 22:46:07 +00:00
|
|
|
// Load the existing mount table
|
2018-01-19 06:44:44 +00:00
|
|
|
raw, err := c.barrier.Get(ctx, coreAuthConfigPath)
|
2015-03-18 22:46:07 +00:00
|
|
|
if err != nil {
|
2018-04-03 00:46:59 +00:00
|
|
|
c.logger.Error("failed to read auth table", "error", err)
|
2015-09-09 19:42:29 +00:00
|
|
|
return errLoadAuthFailed
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
2018-01-19 06:44:44 +00:00
|
|
|
rawLocal, err := c.barrier.Get(ctx, coreLocalAuthConfigPath)
|
2017-02-17 04:09:39 +00:00
|
|
|
if err != nil {
|
2018-04-03 00:46:59 +00:00
|
|
|
c.logger.Error("failed to read local auth table", "error", err)
|
2017-02-17 04:09:39 +00:00
|
|
|
return errLoadAuthFailed
|
|
|
|
}
|
2015-11-11 16:44:07 +00:00
|
|
|
|
|
|
|
c.authLock.Lock()
|
|
|
|
defer c.authLock.Unlock()
|
|
|
|
|
2015-03-18 22:46:07 +00:00
|
|
|
if raw != nil {
|
2018-09-18 03:03:00 +00:00
|
|
|
authTable, err := c.decodeMountTable(ctx, raw.Value)
|
|
|
|
if err != nil {
|
|
|
|
c.logger.Error("failed to decompress and/or decode the auth table", "error", err)
|
|
|
|
return err
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
2015-11-11 16:44:07 +00:00
|
|
|
c.auth = authTable
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
2017-11-08 01:30:02 +00:00
|
|
|
|
|
|
|
var needPersist bool
|
|
|
|
if c.auth == nil {
|
|
|
|
c.auth = c.defaultAuthTable()
|
|
|
|
needPersist = true
|
2020-10-27 15:24:43 +00:00
|
|
|
} else {
|
|
|
|
// only record tableMetrics if we have loaded something from storge
|
|
|
|
c.tableMetrics(len(c.auth.Entries), false, true, raw.Value)
|
2017-11-08 01:30:02 +00:00
|
|
|
}
|
2017-02-17 04:09:39 +00:00
|
|
|
if rawLocal != nil {
|
2018-09-18 03:03:00 +00:00
|
|
|
localAuthTable, err := c.decodeMountTable(ctx, rawLocal.Value)
|
|
|
|
if err != nil {
|
|
|
|
c.logger.Error("failed to decompress and/or decode the local mount table", "error", err)
|
|
|
|
return err
|
2017-02-17 04:09:39 +00:00
|
|
|
}
|
2017-11-07 23:04:37 +00:00
|
|
|
if localAuthTable != nil && len(localAuthTable.Entries) > 0 {
|
|
|
|
c.auth.Entries = append(c.auth.Entries, localAuthTable.Entries...)
|
2020-10-27 15:24:43 +00:00
|
|
|
c.tableMetrics(len(localAuthTable.Entries), true, true, rawLocal.Value)
|
2017-11-07 23:04:37 +00:00
|
|
|
}
|
2017-02-17 04:09:39 +00:00
|
|
|
}
|
2015-03-18 22:46:07 +00:00
|
|
|
|
2017-11-08 01:30:02 +00:00
|
|
|
// Upgrade to typed auth table
|
|
|
|
if c.auth.Type == "" {
|
|
|
|
c.auth.Type = credentialTableType
|
|
|
|
needPersist = true
|
|
|
|
}
|
2016-05-26 17:38:51 +00:00
|
|
|
|
2017-11-08 01:30:02 +00:00
|
|
|
// Upgrade to table-scoped entries
|
|
|
|
for _, entry := range c.auth.Entries {
|
|
|
|
if entry.Table == "" {
|
|
|
|
entry.Table = c.auth.Type
|
2016-05-26 17:38:51 +00:00
|
|
|
needPersist = true
|
|
|
|
}
|
2017-11-08 01:30:02 +00:00
|
|
|
if entry.Accessor == "" {
|
|
|
|
accessor, err := c.generateMountAccessor("auth_" + entry.Type)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-06-26 17:14:36 +00:00
|
|
|
}
|
2017-11-08 01:30:02 +00:00
|
|
|
entry.Accessor = accessor
|
|
|
|
needPersist = true
|
2016-05-26 17:38:51 +00:00
|
|
|
}
|
2018-03-21 19:04:27 +00:00
|
|
|
if entry.BackendAwareUUID == "" {
|
|
|
|
bUUID, err := uuid.GenerateUUID()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
entry.BackendAwareUUID = bUUID
|
|
|
|
needPersist = true
|
|
|
|
}
|
2018-03-02 17:18:39 +00:00
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
if entry.NamespaceID == "" {
|
|
|
|
entry.NamespaceID = namespace.RootNamespaceID
|
|
|
|
needPersist = true
|
|
|
|
}
|
|
|
|
ns, err := NamespaceByID(ctx, entry.NamespaceID, c)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if ns == nil {
|
|
|
|
return namespace.ErrNoNamespace
|
|
|
|
}
|
|
|
|
entry.namespace = ns
|
|
|
|
|
2018-03-02 17:18:39 +00:00
|
|
|
// Sync values to the cache
|
|
|
|
entry.SyncCache()
|
2017-11-08 01:30:02 +00:00
|
|
|
}
|
2016-05-26 17:38:51 +00:00
|
|
|
|
2017-11-08 01:30:02 +00:00
|
|
|
if !needPersist {
|
|
|
|
return nil
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
|
|
|
|
2018-04-11 18:32:55 +00:00
|
|
|
if err := c.persistAuth(ctx, c.auth, nil); err != nil {
|
2018-04-03 00:46:59 +00:00
|
|
|
c.logger.Error("failed to persist auth table", "error", err)
|
2015-09-09 19:42:29 +00:00
|
|
|
return errLoadAuthFailed
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
2018-09-18 03:03:00 +00:00
|
|
|
|
2015-03-18 22:46:07 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// persistAuth is used to persist the auth table after modification
|
2018-04-11 18:32:55 +00:00
|
|
|
func (c *Core) persistAuth(ctx context.Context, table *MountTable, local *bool) error {
|
2016-05-26 17:38:51 +00:00
|
|
|
if table.Type != credentialTableType {
|
2018-04-03 00:46:59 +00:00
|
|
|
c.logger.Error("given table to persist has wrong type", "actual_type", table.Type, "expected_type", credentialTableType)
|
2016-05-26 17:38:51 +00:00
|
|
|
return fmt.Errorf("invalid table type given, not persisting")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, entry := range table.Entries {
|
|
|
|
if entry.Table != table.Type {
|
2018-04-03 00:46:59 +00:00
|
|
|
c.logger.Error("given entry to persist in auth table has wrong table value", "path", entry.Path, "entry_table_type", entry.Table, "actual_type", table.Type)
|
2016-05-26 17:38:51 +00:00
|
|
|
return fmt.Errorf("invalid auth entry found, not persisting")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-17 04:09:39 +00:00
|
|
|
nonLocalAuth := &MountTable{
|
|
|
|
Type: credentialTableType,
|
|
|
|
}
|
|
|
|
|
|
|
|
localAuth := &MountTable{
|
|
|
|
Type: credentialTableType,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, entry := range table.Entries {
|
|
|
|
if entry.Local {
|
|
|
|
localAuth.Entries = append(localAuth.Entries, entry)
|
|
|
|
} else {
|
|
|
|
nonLocalAuth.Entries = append(nonLocalAuth.Entries, entry)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-27 15:24:43 +00:00
|
|
|
writeTable := func(mt *MountTable, path string) ([]byte, error) {
|
2018-04-11 18:32:55 +00:00
|
|
|
// Encode the mount table into JSON and compress it (lzw).
|
|
|
|
compressedBytes, err := jsonutil.EncodeJSONAndCompress(mt, nil)
|
2017-03-02 19:37:59 +00:00
|
|
|
if err != nil {
|
2018-04-11 18:32:55 +00:00
|
|
|
c.logger.Error("failed to encode or compress auth mount table", "error", err)
|
2020-10-27 15:24:43 +00:00
|
|
|
return nil, err
|
2017-03-02 19:37:59 +00:00
|
|
|
}
|
2015-03-18 22:46:07 +00:00
|
|
|
|
2017-03-02 19:37:59 +00:00
|
|
|
// Create an entry
|
2019-01-31 14:25:18 +00:00
|
|
|
entry := &logical.StorageEntry{
|
2018-04-11 18:32:55 +00:00
|
|
|
Key: path,
|
2017-03-02 19:37:59 +00:00
|
|
|
Value: compressedBytes,
|
|
|
|
}
|
2015-03-18 22:46:07 +00:00
|
|
|
|
2017-03-02 19:37:59 +00:00
|
|
|
// Write to the physical backend
|
2018-01-19 06:44:44 +00:00
|
|
|
if err := c.barrier.Put(ctx, entry); err != nil {
|
2018-04-11 18:32:55 +00:00
|
|
|
c.logger.Error("failed to persist auth mount table", "error", err)
|
2020-10-27 15:24:43 +00:00
|
|
|
return nil, err
|
2017-03-02 19:37:59 +00:00
|
|
|
}
|
2020-10-27 15:24:43 +00:00
|
|
|
return compressedBytes, nil
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
2017-02-17 04:09:39 +00:00
|
|
|
|
2018-04-11 18:32:55 +00:00
|
|
|
var err error
|
2020-10-27 15:24:43 +00:00
|
|
|
var compressedBytes []byte
|
2018-04-11 18:32:55 +00:00
|
|
|
switch {
|
|
|
|
case local == nil:
|
|
|
|
// Write non-local mounts
|
2020-10-27 15:24:43 +00:00
|
|
|
compressedBytes, err := writeTable(nonLocalAuth, coreAuthConfigPath)
|
2018-04-11 18:32:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-10-27 15:24:43 +00:00
|
|
|
c.tableMetrics(len(nonLocalAuth.Entries), false, true, compressedBytes)
|
2017-02-17 04:09:39 +00:00
|
|
|
|
2018-04-11 18:32:55 +00:00
|
|
|
// Write local mounts
|
2020-10-27 15:24:43 +00:00
|
|
|
compressedBytes, err = writeTable(localAuth, coreLocalAuthConfigPath)
|
2018-04-11 18:32:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-10-27 15:24:43 +00:00
|
|
|
c.tableMetrics(len(localAuth.Entries), true, true, compressedBytes)
|
2018-04-11 18:32:55 +00:00
|
|
|
case *local:
|
2020-10-27 15:24:43 +00:00
|
|
|
compressedBytes, err = writeTable(localAuth, coreLocalAuthConfigPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.tableMetrics(len(localAuth.Entries), true, true, compressedBytes)
|
2018-04-11 18:32:55 +00:00
|
|
|
default:
|
2020-10-27 15:24:43 +00:00
|
|
|
compressedBytes, err = writeTable(nonLocalAuth, coreAuthConfigPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.tableMetrics(len(nonLocalAuth.Entries), false, true, compressedBytes)
|
2017-02-17 04:09:39 +00:00
|
|
|
}
|
|
|
|
|
2018-04-11 18:32:55 +00:00
|
|
|
return err
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// setupCredentials is invoked after we've loaded the auth table to
|
2015-03-18 22:30:31 +00:00
|
|
|
// initialize the credential backends and setup the router
|
2018-01-19 06:44:44 +00:00
|
|
|
func (c *Core) setupCredentials(ctx context.Context) error {
|
2015-11-11 16:44:07 +00:00
|
|
|
c.authLock.Lock()
|
|
|
|
defer c.authLock.Unlock()
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
for _, entry := range c.auth.sortEntriesByPathDepth().Entries {
|
2017-09-01 05:02:03 +00:00
|
|
|
var backend logical.Backend
|
2016-05-25 21:53:45 +00:00
|
|
|
|
2015-07-01 00:30:43 +00:00
|
|
|
// Create a barrier view using the UUID
|
2018-09-18 03:03:00 +00:00
|
|
|
viewPath := entry.ViewPath()
|
|
|
|
|
|
|
|
// Singleton mounts cannot be filtered on a per-secondary basis
|
|
|
|
// from replication
|
|
|
|
if strutil.StrListContains(singletonMounts, entry.Type) {
|
|
|
|
addFilterablePath(c, viewPath)
|
|
|
|
}
|
|
|
|
|
2018-02-09 20:39:27 +00:00
|
|
|
view := NewBarrierView(c.barrier, viewPath)
|
2018-02-09 19:04:25 +00:00
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
// Determining the replicated state of the mount
|
|
|
|
nilMount, err := preprocessMount(c, entry, view)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
origViewReadOnlyErr := view.getReadOnlyErr()
|
|
|
|
|
2018-02-09 19:04:25 +00:00
|
|
|
// Mark the view as read-only until the mounting is complete and
|
|
|
|
// ensure that it is reset after. This ensures that there will be no
|
|
|
|
// writes during the construction of the backend.
|
|
|
|
view.setReadOnlyErr(logical.ErrSetupReadOnly)
|
2018-04-19 17:29:43 +00:00
|
|
|
if strutil.StrListContains(singletonMounts, entry.Type) {
|
2018-09-18 03:03:00 +00:00
|
|
|
defer view.setReadOnlyErr(origViewReadOnlyErr)
|
2018-04-19 17:29:43 +00:00
|
|
|
} else {
|
|
|
|
c.postUnsealFuncs = append(c.postUnsealFuncs, func() {
|
2018-09-18 03:03:00 +00:00
|
|
|
view.setReadOnlyErr(origViewReadOnlyErr)
|
2018-04-19 17:29:43 +00:00
|
|
|
})
|
|
|
|
}
|
2018-02-09 19:04:25 +00:00
|
|
|
|
2017-10-23 20:49:46 +00:00
|
|
|
// Initialize the backend
|
2017-02-17 04:09:39 +00:00
|
|
|
sysView := c.mountEntrySysView(entry)
|
2015-07-01 00:30:43 +00:00
|
|
|
|
2018-03-21 19:04:27 +00:00
|
|
|
backend, err = c.newCredentialBackend(ctx, entry, sysView, view)
|
2015-03-18 22:46:07 +00:00
|
|
|
if err != nil {
|
2018-04-03 00:46:59 +00:00
|
|
|
c.logger.Error("failed to create credential entry", "path", entry.Path, "error", err)
|
2022-08-25 20:31:42 +00:00
|
|
|
if plug, plugerr := c.pluginCatalog.Get(ctx, entry.Type, consts.PluginTypeCredential, ""); plugerr == nil && !plug.Builtin {
|
2017-12-15 18:31:57 +00:00
|
|
|
// If we encounter an error instantiating the backend due to an error,
|
|
|
|
// skip backend initialization but register the entry to the mount table
|
|
|
|
// to preserve storage and path.
|
2018-04-03 00:46:59 +00:00
|
|
|
c.logger.Warn("skipping plugin-based credential entry", "path", entry.Path)
|
2017-09-01 05:02:03 +00:00
|
|
|
goto ROUTER_MOUNT
|
|
|
|
}
|
2015-09-09 19:42:29 +00:00
|
|
|
return errLoadAuthFailed
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
2017-03-04 21:35:41 +00:00
|
|
|
if backend == nil {
|
|
|
|
return fmt.Errorf("nil backend returned from %q factory", entry.Type)
|
|
|
|
}
|
2015-03-18 22:46:07 +00:00
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
{
|
|
|
|
// Check for the correct backend type
|
|
|
|
backendType := backend.Type()
|
2018-11-07 01:21:24 +00:00
|
|
|
if backendType != logical.TypeCredential {
|
|
|
|
return fmt.Errorf("cannot mount %q of type %q as an auth backend", entry.Type, backendType)
|
2018-09-18 03:03:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
addPathCheckers(c, entry, backend, viewPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the mount is filtered or we are on a DR secondary we don't want to
|
|
|
|
// keep the actual backend running, so we clean it up and set it to nil
|
|
|
|
// so the router does not have a pointer to the object.
|
|
|
|
if nilMount {
|
|
|
|
backend.Cleanup(ctx)
|
|
|
|
backend = nil
|
Backend plugin system (#2874)
* Add backend plugin changes
* Fix totp backend plugin tests
* Fix logical/plugin InvalidateKey test
* Fix plugin catalog CRUD test, fix NoopBackend
* Clean up commented code block
* Fix system backend mount test
* Set plugin_name to omitempty, fix handleMountTable config parsing
* Clean up comments, keep shim connections alive until cleanup
* Include pluginClient, disallow LookupPlugin call from within a plugin
* Add wrapper around backendPluginClient for proper cleanup
* Add logger shim tests
* Add logger, storage, and system shim tests
* Use pointer receivers for system view shim
* Use plugin name if no path is provided on mount
* Enable plugins for auth backends
* Add backend type attribute, move builtin/plugin/package
* Fix merge conflict
* Fix missing plugin name in mount config
* Add integration tests on enabling auth backend plugins
* Remove dependency cycle on mock-plugin
* Add passthrough backend plugin, use logical.BackendType to determine lease generation
* Remove vault package dependency on passthrough package
* Add basic impl test for passthrough plugin
* Incorporate feedback; set b.backend after shims creation on backendPluginServer
* Fix totp plugin test
* Add plugin backends docs
* Fix tests
* Fix builtin/plugin tests
* Remove flatten from PluginRunner fields
* Move mock plugin to logical/plugin, remove totp and passthrough plugins
* Move pluginMap into newPluginClient
* Do not create storage RPC connection on HandleRequest and HandleExistenceCheck
* Change shim logger's Fatal to no-op
* Change BackendType to uint32, match UX backend types
* Change framework.Backend Setup signature
* Add Setup func to logical.Backend interface
* Move OptionallyEnableMlock call into plugin.Serve, update docs and comments
* Remove commented var in plugin package
* RegisterLicense on logical.Backend interface (#3017)
* Add RegisterLicense to logical.Backend interface
* Update RegisterLicense to use callback func on framework.Backend
* Refactor framework.Backend.RegisterLicense
* plugin: Prevent plugin.SystemViewClient.ResponseWrapData from getting JWTs
* plugin: Revert BackendType to remove TypePassthrough and related references
* Fix typo in plugin backends docs
2017-07-20 17:28:40 +00:00
|
|
|
}
|
|
|
|
|
2017-09-01 05:02:03 +00:00
|
|
|
ROUTER_MOUNT:
|
2015-03-18 22:46:07 +00:00
|
|
|
// Mount the backend
|
2015-04-03 21:24:00 +00:00
|
|
|
path := credentialRoutePrefix + entry.Path
|
2015-09-04 20:58:12 +00:00
|
|
|
err = c.router.Mount(backend, path, entry, view)
|
2015-03-18 22:46:07 +00:00
|
|
|
if err != nil {
|
2022-04-26 16:13:45 +00:00
|
|
|
c.logger.Error("failed to mount auth entry", "path", entry.Path, "namespace", entry.Namespace(), "error", err)
|
2015-09-09 19:42:29 +00:00
|
|
|
return errLoadAuthFailed
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
2015-03-23 20:41:05 +00:00
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
if c.logger.IsInfo() {
|
2022-04-26 16:13:45 +00:00
|
|
|
c.logger.Info("successfully enabled credential backend", "type", entry.Type, "path", entry.Path, "namespace", entry.Namespace())
|
2018-09-18 03:03:00 +00:00
|
|
|
}
|
|
|
|
|
2015-04-03 23:07:45 +00:00
|
|
|
// Ensure the path is tainted if set in the mount table
|
|
|
|
if entry.Tainted {
|
2022-04-26 16:13:45 +00:00
|
|
|
// Calculate any namespace prefixes here, because when Taint() is called, there won't be
|
|
|
|
// a namespace to pull from the context. This is similar to what we do above in c.router.Mount().
|
|
|
|
path = entry.Namespace().Path + path
|
2018-09-18 03:03:00 +00:00
|
|
|
c.router.Taint(ctx, path)
|
2015-04-03 23:07:45 +00:00
|
|
|
}
|
|
|
|
|
2015-03-23 20:41:05 +00:00
|
|
|
// Check if this is the token store
|
|
|
|
if entry.Type == "token" {
|
|
|
|
c.tokenStore = backend.(*TokenStore)
|
2015-09-15 16:27:22 +00:00
|
|
|
|
2018-10-15 16:56:24 +00:00
|
|
|
// At some point when this isn't beta we may persist this but for
|
|
|
|
// now always set it on mount
|
|
|
|
entry.Config.TokenType = logical.TokenTypeDefaultService
|
|
|
|
|
2015-09-15 16:27:22 +00:00
|
|
|
// this is loaded *after* the normal mounts, including cubbyhole
|
2017-07-18 16:02:03 +00:00
|
|
|
c.router.tokenStoreSaltFunc = c.tokenStore.Salt
|
2018-09-18 03:03:00 +00:00
|
|
|
if !c.IsDRSecondary() {
|
|
|
|
c.tokenStore.cubbyholeBackend = c.router.MatchingBackend(ctx, cubbyholeMountPath).(*CubbyholeBackend)
|
|
|
|
}
|
2015-03-23 20:41:05 +00:00
|
|
|
}
|
2018-09-18 03:03:00 +00:00
|
|
|
|
|
|
|
// Populate cache
|
|
|
|
NamespaceByID(ctx, entry.NamespaceID, c)
|
2019-07-05 23:55:40 +00:00
|
|
|
|
|
|
|
// Initialize
|
|
|
|
if !nilMount {
|
2019-07-06 01:37:10 +00:00
|
|
|
// Bind locally
|
|
|
|
localEntry := entry
|
2019-07-05 23:55:40 +00:00
|
|
|
c.postUnsealFuncs = append(c.postUnsealFuncs, func() {
|
|
|
|
if backend == nil {
|
2019-07-06 01:37:10 +00:00
|
|
|
c.logger.Error("skipping initialization on nil backend", "path", localEntry.Path)
|
2019-07-05 23:55:40 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err := backend.Initialize(ctx, &logical.InitializationRequest{Storage: view})
|
|
|
|
if err != nil {
|
2019-07-06 01:37:10 +00:00
|
|
|
c.logger.Error("failed to initialize auth entry", "path", localEntry.Path, "error", err)
|
2019-07-05 23:55:40 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
2016-05-25 21:53:45 +00:00
|
|
|
|
2015-03-18 22:30:31 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// teardownCredentials is used before we seal the vault to reset the credential
|
|
|
|
// backends to their unloaded state. This is reversed by loadCredentials.
|
2018-01-19 06:44:44 +00:00
|
|
|
func (c *Core) teardownCredentials(ctx context.Context) error {
|
2015-11-11 16:44:07 +00:00
|
|
|
c.authLock.Lock()
|
|
|
|
defer c.authLock.Unlock()
|
|
|
|
|
2016-10-28 19:32:32 +00:00
|
|
|
if c.auth != nil {
|
|
|
|
authTable := c.auth.shallowClone()
|
|
|
|
for _, e := range authTable.Entries {
|
2018-09-18 03:03:00 +00:00
|
|
|
backend := c.router.MatchingBackend(namespace.ContextWithNamespace(ctx, e.namespace), credentialRoutePrefix+e.Path)
|
2017-03-04 21:35:41 +00:00
|
|
|
if backend != nil {
|
2018-01-19 06:44:44 +00:00
|
|
|
backend.Cleanup(ctx)
|
2016-10-28 19:32:32 +00:00
|
|
|
}
|
2018-09-18 03:03:00 +00:00
|
|
|
|
|
|
|
viewPath := e.ViewPath()
|
|
|
|
removePathCheckers(c, e, viewPath)
|
2016-10-28 19:32:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-18 22:46:07 +00:00
|
|
|
c.auth = nil
|
2015-03-23 20:41:05 +00:00
|
|
|
c.tokenStore = nil
|
2015-03-18 22:30:31 +00:00
|
|
|
return nil
|
|
|
|
}
|
2015-03-18 22:46:07 +00:00
|
|
|
|
|
|
|
// newCredentialBackend is used to create and configure a new credential backend by name
|
2018-03-21 19:04:27 +00:00
|
|
|
func (c *Core) newCredentialBackend(ctx context.Context, entry *MountEntry, sysView logical.SystemView, view logical.Storage) (logical.Backend, error) {
|
|
|
|
t := entry.Type
|
2017-04-24 19:15:50 +00:00
|
|
|
if alias, ok := credentialAliases[t]; ok {
|
|
|
|
t = alias
|
|
|
|
}
|
2018-09-18 03:03:00 +00:00
|
|
|
|
2015-03-18 22:46:07 +00:00
|
|
|
f, ok := c.credentialBackends[t]
|
|
|
|
if !ok {
|
2022-08-25 20:31:42 +00:00
|
|
|
plug, err := c.pluginCatalog.Get(ctx, t, consts.PluginTypeCredential, "")
|
2022-04-14 20:54:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if plug == nil {
|
2022-08-11 01:02:05 +00:00
|
|
|
return nil, fmt.Errorf("%w: %s", ErrPluginNotFound, t)
|
2022-04-14 20:54:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
f = plugin.Factory
|
|
|
|
if !plug.Builtin {
|
|
|
|
f = wrapFactoryCheckPerms(c, plugin.Factory)
|
|
|
|
}
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
2015-03-31 01:07:05 +00:00
|
|
|
|
2018-03-21 19:04:27 +00:00
|
|
|
// Set up conf to pass in plugin_name
|
2022-01-14 23:35:27 +00:00
|
|
|
conf := make(map[string]string)
|
2018-03-21 19:04:27 +00:00
|
|
|
for k, v := range entry.Options {
|
|
|
|
conf[k] = v
|
|
|
|
}
|
2018-11-07 01:21:24 +00:00
|
|
|
|
|
|
|
switch {
|
|
|
|
case entry.Type == "plugin":
|
2018-11-19 23:23:48 +00:00
|
|
|
conf["plugin_name"] = entry.Config.PluginName
|
2018-11-07 01:21:24 +00:00
|
|
|
default:
|
|
|
|
conf["plugin_name"] = t
|
2018-03-21 19:04:27 +00:00
|
|
|
}
|
|
|
|
|
2018-11-07 01:21:24 +00:00
|
|
|
conf["plugin_type"] = consts.PluginTypeCredential.String()
|
|
|
|
|
2018-09-05 19:52:54 +00:00
|
|
|
authLogger := c.baseLogger.Named(fmt.Sprintf("auth.%s.%s", t, entry.Accessor))
|
|
|
|
c.AddLogger(authLogger)
|
2015-07-01 00:30:43 +00:00
|
|
|
config := &logical.BackendConfig{
|
2015-09-09 19:42:29 +00:00
|
|
|
StorageView: view,
|
2018-09-05 19:52:54 +00:00
|
|
|
Logger: authLogger,
|
2015-09-09 19:42:29 +00:00
|
|
|
Config: conf,
|
2015-09-10 01:58:09 +00:00
|
|
|
System: sysView,
|
2018-03-21 19:04:27 +00:00
|
|
|
BackendUUID: entry.BackendAwareUUID,
|
2015-07-01 00:30:43 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 06:44:44 +00:00
|
|
|
b, err := f(ctx, config)
|
2015-07-01 00:30:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-09-04 20:58:12 +00:00
|
|
|
|
2015-07-01 00:30:43 +00:00
|
|
|
return b, nil
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// defaultAuthTable creates a default auth table
|
2017-06-26 17:14:36 +00:00
|
|
|
func (c *Core) defaultAuthTable() *MountTable {
|
2016-05-26 17:38:51 +00:00
|
|
|
table := &MountTable{
|
|
|
|
Type: credentialTableType,
|
|
|
|
}
|
2016-01-13 18:40:08 +00:00
|
|
|
tokenUUID, err := uuid.GenerateUUID()
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("could not generate UUID for default auth table token entry: %v", err))
|
|
|
|
}
|
2017-06-26 17:14:36 +00:00
|
|
|
tokenAccessor, err := c.generateMountAccessor("auth_token")
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("could not generate accessor for default auth table token entry: %v", err))
|
|
|
|
}
|
2018-03-21 19:04:27 +00:00
|
|
|
tokenBackendUUID, err := uuid.GenerateUUID()
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("could not create identity backend UUID: %v", err))
|
|
|
|
}
|
2015-03-19 16:54:57 +00:00
|
|
|
tokenAuth := &MountEntry{
|
2018-03-21 19:04:27 +00:00
|
|
|
Table: credentialTableType,
|
|
|
|
Path: "token/",
|
|
|
|
Type: "token",
|
|
|
|
Description: "token based credentials",
|
|
|
|
UUID: tokenUUID,
|
|
|
|
Accessor: tokenAccessor,
|
|
|
|
BackendAwareUUID: tokenBackendUUID,
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
|
|
|
table.Entries = append(table.Entries, tokenAuth)
|
|
|
|
return table
|
|
|
|
}
|