2015-03-15 23:39:49 +00:00
|
|
|
package framework
|
2015-03-14 04:11:19 +00:00
|
|
|
|
|
|
|
import (
|
2018-01-08 18:31:38 +00:00
|
|
|
"context"
|
2019-10-17 17:33:00 +00:00
|
|
|
"crypto/rand"
|
2021-10-13 19:24:31 +00:00
|
|
|
"encoding/json"
|
2015-03-19 18:41:41 +00:00
|
|
|
"fmt"
|
2019-10-17 17:33:00 +00:00
|
|
|
"io"
|
2015-04-04 18:39:58 +00:00
|
|
|
"io/ioutil"
|
2018-08-13 18:02:44 +00:00
|
|
|
"net/http"
|
2015-03-14 06:17:25 +00:00
|
|
|
"regexp"
|
2015-04-04 04:00:23 +00:00
|
|
|
"sort"
|
2015-04-04 04:10:54 +00:00
|
|
|
"strings"
|
2015-03-14 06:17:25 +00:00
|
|
|
"sync"
|
2015-03-18 00:58:05 +00:00
|
|
|
"time"
|
2015-03-14 06:17:25 +00:00
|
|
|
|
2022-09-19 14:23:40 +00:00
|
|
|
"github.com/hashicorp/go-kms-wrapping/entropy/v2"
|
|
|
|
|
2021-10-14 16:47:32 +00:00
|
|
|
jsonpatch "github.com/evanphx/json-patch/v5"
|
2018-04-05 15:49:21 +00:00
|
|
|
"github.com/hashicorp/errwrap"
|
2018-04-03 00:46:59 +00:00
|
|
|
log "github.com/hashicorp/go-hclog"
|
2020-01-07 22:04:08 +00:00
|
|
|
"github.com/hashicorp/go-multierror"
|
2021-07-16 00:17:31 +00:00
|
|
|
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
2020-01-07 22:04:08 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
2019-04-12 21:54:35 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/helper/errutil"
|
|
|
|
"github.com/hashicorp/vault/sdk/helper/license"
|
|
|
|
"github.com/hashicorp/vault/sdk/helper/logging"
|
|
|
|
"github.com/hashicorp/vault/sdk/logical"
|
2015-03-14 04:11:19 +00:00
|
|
|
)
|
|
|
|
|
2015-03-15 21:57:19 +00:00
|
|
|
// Backend is an implementation of logical.Backend that allows
|
2015-03-14 04:11:19 +00:00
|
|
|
// the implementer to code a backend using a much more programmer-friendly
|
|
|
|
// framework that handles a lot of the routing and validation for you.
|
|
|
|
//
|
2015-03-15 21:57:19 +00:00
|
|
|
// This is recommended over implementing logical.Backend directly.
|
2015-03-14 04:11:19 +00:00
|
|
|
type Backend struct {
|
2015-04-04 03:36:47 +00:00
|
|
|
// Help is the help text that is shown when a help request is made
|
|
|
|
// on the root of this resource. The root help is special since we
|
|
|
|
// show all the paths that can be requested.
|
|
|
|
Help string
|
|
|
|
|
2015-03-14 06:17:25 +00:00
|
|
|
// Paths are the various routes that the backend responds to.
|
|
|
|
// This cannot be modified after construction (i.e. dynamically changing
|
|
|
|
// paths, including adding or removing, is not allowed once the
|
|
|
|
// backend is in use).
|
2015-03-19 13:39:25 +00:00
|
|
|
//
|
2021-10-13 16:51:20 +00:00
|
|
|
// PathsSpecial is the list of path patterns that denote the paths above
|
|
|
|
// that require special privileges.
|
2015-03-31 00:46:18 +00:00
|
|
|
Paths []*Path
|
|
|
|
PathsSpecial *logical.Paths
|
2015-03-16 00:35:59 +00:00
|
|
|
|
2015-03-19 14:07:45 +00:00
|
|
|
// Secrets is the list of secret types that this backend can
|
|
|
|
// return. It is used to automatically generate proper responses,
|
|
|
|
// and ease specifying callbacks for revocation, renewal, etc.
|
|
|
|
Secrets []*Secret
|
|
|
|
|
2019-07-05 23:55:40 +00:00
|
|
|
// InitializeFunc is the callback, which if set, will be invoked via
|
|
|
|
// Initialize() just after a plugin has been mounted.
|
|
|
|
InitializeFunc InitializeFunc
|
|
|
|
|
2016-04-19 18:21:27 +00:00
|
|
|
// PeriodicFunc is the callback, which if set, will be invoked when the
|
2016-04-19 01:06:26 +00:00
|
|
|
// periodic timer of RollbackManager ticks. This can be used by
|
2016-04-19 18:21:27 +00:00
|
|
|
// backends to do anything it wishes to do periodically.
|
2016-04-19 01:06:26 +00:00
|
|
|
//
|
2021-06-28 22:51:47 +00:00
|
|
|
// PeriodicFunc can be invoked to, say periodically delete stale
|
2016-04-19 18:21:27 +00:00
|
|
|
// entries in backend's storage, while the backend is still being used.
|
2021-06-28 22:51:47 +00:00
|
|
|
// (Note the difference between this action and `Clean`, which is
|
2016-04-19 18:21:27 +00:00
|
|
|
// invoked just before the backend is unmounted).
|
|
|
|
PeriodicFunc periodicFunc
|
2016-04-19 01:06:26 +00:00
|
|
|
|
|
|
|
// WALRollback is called when a WAL entry (see wal.go) has to be rolled
|
2015-03-21 10:08:13 +00:00
|
|
|
// back. It is called with the data from the entry.
|
2015-03-21 10:03:59 +00:00
|
|
|
//
|
2016-04-19 01:06:26 +00:00
|
|
|
// WALRollbackMinAge is the minimum age of a WAL entry before it is attempted
|
2015-03-21 10:03:59 +00:00
|
|
|
// to be rolled back. This should be longer than the maximum time it takes
|
|
|
|
// to successfully create a secret.
|
2016-04-19 01:06:26 +00:00
|
|
|
WALRollback WALRollbackFunc
|
|
|
|
WALRollbackMinAge time.Duration
|
2015-03-18 00:15:23 +00:00
|
|
|
|
2015-09-10 14:11:37 +00:00
|
|
|
// Clean is called on unload to clean up e.g any existing connections
|
|
|
|
// to the backend, if required.
|
2015-09-10 01:58:09 +00:00
|
|
|
Clean CleanupFunc
|
2015-09-10 14:11:37 +00:00
|
|
|
|
2021-06-28 22:51:47 +00:00
|
|
|
// Invalidate is called when a key is modified, if required.
|
2017-01-07 23:18:22 +00:00
|
|
|
Invalidate InvalidateFunc
|
|
|
|
|
2015-04-11 21:46:09 +00:00
|
|
|
// AuthRenew is the callback to call when a RenewRequest for an
|
|
|
|
// authentication comes in. By default, renewal won't be allowed.
|
|
|
|
// See the built-in AuthRenew helpers in lease.go for common callbacks.
|
|
|
|
AuthRenew OperationFunc
|
|
|
|
|
2021-06-28 22:51:47 +00:00
|
|
|
// BackendType is the logical.BackendType for the backend implementation
|
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
|
|
|
BackendType logical.BackendType
|
|
|
|
|
Add plugin version to GRPC interface (#17088)
Add plugin version to GRPC interface
Added a version interface in the sdk/logical so that it can be shared between all plugin types, and then wired it up to RunningVersion in the mounts, auth list, and database systems.
I've tested that this works with auth, database, and secrets plugin types, with the following logic to populate RunningVersion:
If a plugin has a PluginVersion() method implemented, then that is used
If not, and the plugin is built into the Vault binary, then the go.mod version is used
Otherwise, the it will be the empty string.
My apologies for the length of this PR.
* Placeholder backend should be external
We use a placeholder backend (previously a framework.Backend) before a
GRPC plugin is lazy-loaded. This makes us later think the plugin is a
builtin plugin.
So we added a `placeholderBackend` type that overrides the
`IsExternal()` method so that later we know that the plugin is external,
and don't give it a default builtin version.
2022-09-15 23:37:59 +00:00
|
|
|
// RunningVersion is the optional version that will be self-reported
|
|
|
|
RunningVersion string
|
|
|
|
|
2016-08-19 20:45:17 +00:00
|
|
|
logger log.Logger
|
2015-09-02 19:56:58 +00:00
|
|
|
system logical.SystemView
|
2015-08-27 18:25:07 +00:00
|
|
|
once sync.Once
|
|
|
|
pathsRe []*regexp.Regexp
|
2015-03-14 04:11:19 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 18:21:27 +00:00
|
|
|
// periodicFunc is the callback called when the RollbackManager's timer ticks.
|
|
|
|
// This can be utilized by the backends to do anything it wants.
|
2018-01-19 06:44:44 +00:00
|
|
|
type periodicFunc func(context.Context, *logical.Request) error
|
2016-04-19 01:06:26 +00:00
|
|
|
|
2015-03-14 07:19:25 +00:00
|
|
|
// OperationFunc is the callback called for an operation on a path.
|
2018-01-08 18:31:38 +00:00
|
|
|
type OperationFunc func(context.Context, *logical.Request, *FieldData) (*logical.Response, error)
|
|
|
|
|
2018-03-20 18:54:10 +00:00
|
|
|
// ExistenceFunc is the callback called for an existence check on a path.
|
2018-01-08 18:31:38 +00:00
|
|
|
type ExistenceFunc func(context.Context, *logical.Request, *FieldData) (bool, error)
|
2015-03-14 07:19:25 +00:00
|
|
|
|
2016-04-19 01:06:26 +00:00
|
|
|
// WALRollbackFunc is the callback for rollbacks.
|
2018-01-19 06:44:44 +00:00
|
|
|
type WALRollbackFunc func(context.Context, *logical.Request, string, interface{}) error
|
2015-03-21 10:03:59 +00:00
|
|
|
|
2015-09-10 14:11:37 +00:00
|
|
|
// CleanupFunc is the callback for backend unload.
|
2018-01-19 06:44:44 +00:00
|
|
|
type CleanupFunc func(context.Context)
|
2015-09-10 14:11:37 +00:00
|
|
|
|
2017-01-07 23:18:22 +00:00
|
|
|
// InvalidateFunc is the callback for backend key invalidation.
|
2018-01-19 06:44:44 +00:00
|
|
|
type InvalidateFunc func(context.Context, string)
|
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
|
|
|
|
2019-07-05 23:55:40 +00:00
|
|
|
// InitializeFunc is the callback, which if set, will be invoked via
|
|
|
|
// Initialize() just after a plugin has been mounted.
|
|
|
|
type InitializeFunc func(context.Context, *logical.InitializationRequest) error
|
|
|
|
|
2021-10-13 19:24:31 +00:00
|
|
|
// PatchPreprocessorFunc is used by HandlePatchOperation in order to shape
|
|
|
|
// the input as defined by request handler prior to JSON marshaling
|
|
|
|
type PatchPreprocessorFunc func(map[string]interface{}) (map[string]interface{}, error)
|
|
|
|
|
2019-07-05 23:55:40 +00:00
|
|
|
// Initialize is the logical.Backend implementation.
|
|
|
|
func (b *Backend) Initialize(ctx context.Context, req *logical.InitializationRequest) error {
|
|
|
|
if b.InitializeFunc != nil {
|
|
|
|
return b.InitializeFunc(ctx, req)
|
|
|
|
}
|
|
|
|
return 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
|
|
|
// HandleExistenceCheck is the logical.Backend implementation.
|
2018-01-08 18:31:38 +00:00
|
|
|
func (b *Backend) HandleExistenceCheck(ctx context.Context, req *logical.Request) (checkFound bool, exists bool, err error) {
|
2016-01-07 20:10:05 +00:00
|
|
|
b.once.Do(b.init)
|
|
|
|
|
|
|
|
// Ensure we are only doing this when one of the correct operations is in play
|
|
|
|
switch req.Operation {
|
|
|
|
case logical.CreateOperation:
|
|
|
|
case logical.UpdateOperation:
|
|
|
|
default:
|
2016-01-12 20:09:16 +00:00
|
|
|
return false, false, fmt.Errorf("incorrect operation type %v for an existence check", req.Operation)
|
2016-01-07 20:10:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find the matching route
|
|
|
|
path, captures := b.route(req.Path)
|
|
|
|
if path == nil {
|
2016-01-12 20:09:16 +00:00
|
|
|
return false, false, logical.ErrUnsupportedPath
|
2016-01-07 20:10:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if path.ExistenceCheck == nil {
|
2016-01-12 20:09:16 +00:00
|
|
|
return false, false, nil
|
2016-01-07 20:10:05 +00:00
|
|
|
}
|
|
|
|
|
2016-01-12 20:09:16 +00:00
|
|
|
checkFound = true
|
|
|
|
|
2016-01-07 20:10:05 +00:00
|
|
|
// Build up the data for the route, with the URL taking priority
|
|
|
|
// for the fields over the PUT data.
|
|
|
|
raw := make(map[string]interface{}, len(path.Fields))
|
|
|
|
for k, v := range req.Data {
|
|
|
|
raw[k] = v
|
|
|
|
}
|
|
|
|
for k, v := range captures {
|
|
|
|
raw[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
fd := FieldData{
|
|
|
|
Raw: raw,
|
2021-04-08 16:43:39 +00:00
|
|
|
Schema: path.Fields,
|
|
|
|
}
|
2016-01-07 20:10:05 +00:00
|
|
|
|
2016-01-12 20:09:16 +00:00
|
|
|
err = fd.Validate()
|
2016-01-07 20:10:05 +00:00
|
|
|
if err != nil {
|
2016-07-28 19:19:27 +00:00
|
|
|
return false, false, errutil.UserError{Err: err.Error()}
|
2016-01-07 20:10:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Call the callback with the request and the data
|
2018-01-08 18:31:38 +00:00
|
|
|
exists, err = path.ExistenceCheck(ctx, req, &fd)
|
2016-01-12 20:09:16 +00:00
|
|
|
return
|
2016-01-07 20:10:05 +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
|
|
|
// HandleRequest is the logical.Backend implementation.
|
2018-01-08 18:31:38 +00:00
|
|
|
func (b *Backend) HandleRequest(ctx context.Context, req *logical.Request) (*logical.Response, error) {
|
2015-04-04 03:36:47 +00:00
|
|
|
b.once.Do(b.init)
|
|
|
|
|
2015-03-19 18:41:41 +00:00
|
|
|
// Check for special cased global operations. These don't route
|
|
|
|
// to a specific Path.
|
|
|
|
switch req.Operation {
|
2015-03-19 19:20:25 +00:00
|
|
|
case logical.RenewOperation:
|
|
|
|
fallthrough
|
2015-03-19 18:41:41 +00:00
|
|
|
case logical.RevokeOperation:
|
2018-01-08 18:31:38 +00:00
|
|
|
return b.handleRevokeRenew(ctx, req)
|
2015-03-19 18:41:41 +00:00
|
|
|
case logical.RollbackOperation:
|
2018-01-19 06:44:44 +00:00
|
|
|
return b.handleRollback(ctx, req)
|
2015-03-18 00:15:23 +00:00
|
|
|
}
|
|
|
|
|
2015-04-04 03:36:47 +00:00
|
|
|
// If the path is empty and it is a help operation, handle that.
|
|
|
|
if req.Path == "" && req.Operation == logical.HelpOperation {
|
2022-03-12 00:00:26 +00:00
|
|
|
return b.handleRootHelp(req)
|
2015-04-04 03:36:47 +00:00
|
|
|
}
|
|
|
|
|
2015-03-14 06:58:20 +00:00
|
|
|
// Find the matching route
|
|
|
|
path, captures := b.route(req.Path)
|
|
|
|
if path == nil {
|
2015-03-15 21:57:19 +00:00
|
|
|
return nil, logical.ErrUnsupportedPath
|
2015-03-14 06:58:20 +00:00
|
|
|
}
|
|
|
|
|
2018-09-18 03:03:00 +00:00
|
|
|
// Check if a feature is required and if the license has that feature
|
|
|
|
if path.FeatureRequired != license.FeatureNone {
|
|
|
|
hasFeature := b.system.HasFeature(path.FeatureRequired)
|
|
|
|
if !hasFeature {
|
|
|
|
return nil, logical.CodedError(401, "Feature Not Enabled")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-14 06:58:20 +00:00
|
|
|
// Build up the data for the route, with the URL taking priority
|
|
|
|
// for the fields over the PUT data.
|
|
|
|
raw := make(map[string]interface{}, len(path.Fields))
|
2022-04-11 13:57:12 +00:00
|
|
|
var ignored []string
|
2015-03-14 06:58:20 +00:00
|
|
|
for k, v := range req.Data {
|
|
|
|
raw[k] = v
|
2022-08-23 12:51:23 +00:00
|
|
|
if !path.TakesArbitraryInput && path.Fields[k] == nil {
|
2022-04-11 13:57:12 +00:00
|
|
|
ignored = append(ignored, k)
|
|
|
|
}
|
2015-03-14 06:58:20 +00:00
|
|
|
}
|
2022-04-11 13:57:12 +00:00
|
|
|
|
|
|
|
var replaced []string
|
2015-03-14 06:58:20 +00:00
|
|
|
for k, v := range captures {
|
2022-04-11 13:57:12 +00:00
|
|
|
if raw[k] != nil {
|
|
|
|
replaced = append(replaced, k)
|
|
|
|
}
|
2015-03-14 06:58:20 +00:00
|
|
|
raw[k] = v
|
|
|
|
}
|
|
|
|
|
2018-11-05 20:24:39 +00:00
|
|
|
// Look up the callback for this operation, preferring the
|
|
|
|
// path.Operations definition if present.
|
2015-03-14 17:12:50 +00:00
|
|
|
var callback OperationFunc
|
2018-11-05 20:24:39 +00:00
|
|
|
|
|
|
|
if path.Operations != nil {
|
|
|
|
if op, ok := path.Operations[req.Operation]; ok {
|
2020-01-07 22:04:08 +00:00
|
|
|
|
|
|
|
// Check whether this operation should be forwarded
|
2020-01-08 13:59:44 +00:00
|
|
|
if sysView := b.System(); sysView != nil {
|
|
|
|
replState := sysView.ReplicationState()
|
|
|
|
props := op.Properties()
|
2020-01-07 22:04:08 +00:00
|
|
|
|
2020-01-08 13:59:44 +00:00
|
|
|
if props.ForwardPerformanceStandby && replState.HasState(consts.ReplicationPerformanceStandby) {
|
|
|
|
return nil, logical.ErrReadOnly
|
|
|
|
}
|
2020-01-07 22:04:08 +00:00
|
|
|
|
2020-01-08 13:59:44 +00:00
|
|
|
if props.ForwardPerformanceSecondary && !sysView.LocalMount() && replState.HasState(consts.ReplicationPerformanceSecondary) {
|
|
|
|
return nil, logical.ErrReadOnly
|
|
|
|
}
|
2020-01-07 22:04:08 +00:00
|
|
|
}
|
|
|
|
|
2018-11-05 20:24:39 +00:00
|
|
|
callback = op.Handler()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
callback = path.Callbacks[req.Operation]
|
2015-03-14 17:12:50 +00:00
|
|
|
}
|
2018-11-05 20:24:39 +00:00
|
|
|
ok := callback != nil
|
|
|
|
|
2015-03-14 17:12:50 +00:00
|
|
|
if !ok {
|
2015-05-07 22:39:43 +00:00
|
|
|
if req.Operation == logical.HelpOperation {
|
2018-11-05 20:24:39 +00:00
|
|
|
callback = path.helpCallback(b)
|
2015-03-14 17:12:50 +00:00
|
|
|
ok = true
|
|
|
|
}
|
2015-03-14 07:19:25 +00:00
|
|
|
}
|
|
|
|
if !ok {
|
2015-03-15 21:57:19 +00:00
|
|
|
return nil, logical.ErrUnsupportedOperation
|
2015-03-14 07:19:25 +00:00
|
|
|
}
|
2015-08-27 16:41:03 +00:00
|
|
|
|
2015-08-11 16:34:14 +00:00
|
|
|
fd := FieldData{
|
|
|
|
Raw: raw,
|
2021-04-08 16:43:39 +00:00
|
|
|
Schema: path.Fields,
|
|
|
|
}
|
2015-08-27 16:41:03 +00:00
|
|
|
|
2015-08-11 16:34:14 +00:00
|
|
|
if req.Operation != logical.HelpOperation {
|
2015-08-27 16:41:03 +00:00
|
|
|
err := fd.Validate()
|
|
|
|
if err != nil {
|
2021-07-12 17:39:28 +00:00
|
|
|
return logical.ErrorResponse(fmt.Sprintf("Field validation failed: %s", err.Error())), nil
|
2015-08-27 16:41:03 +00:00
|
|
|
}
|
2015-08-11 16:34:14 +00:00
|
|
|
}
|
2015-03-14 07:19:25 +00:00
|
|
|
|
2022-04-11 13:57:12 +00:00
|
|
|
resp, err := callback(ctx, req, &fd)
|
|
|
|
if err != nil {
|
|
|
|
return resp, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch resp {
|
|
|
|
case nil:
|
|
|
|
default:
|
|
|
|
// If fields supplied in the request are not present in the field schema
|
|
|
|
// of the path, add a warning to the response indicating that those
|
|
|
|
// parameters will be ignored.
|
2022-04-18 19:45:08 +00:00
|
|
|
sort.Strings(ignored)
|
|
|
|
|
2022-04-11 13:57:12 +00:00
|
|
|
if len(ignored) != 0 {
|
|
|
|
resp.AddWarning(fmt.Sprintf("Endpoint ignored these unrecognized parameters: %v", ignored))
|
|
|
|
}
|
|
|
|
// If fields supplied in the request is being overwritten by the values
|
|
|
|
// supplied in the API request path, add a warning to the response
|
|
|
|
// indicating that those parameters will be replaced.
|
|
|
|
if len(replaced) != 0 {
|
|
|
|
resp.AddWarning(fmt.Sprintf("Endpoint replaced the value of these parameters with the values captured from the endpoint's path: %v", replaced))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp, nil
|
2015-03-14 06:58:20 +00:00
|
|
|
}
|
|
|
|
|
2021-10-13 19:24:31 +00:00
|
|
|
// HandlePatchOperation acts as an abstraction for performing JSON merge patch
|
|
|
|
// operations (see https://datatracker.ietf.org/doc/html/rfc7396) for HTTP
|
|
|
|
// PATCH requests. It is responsible for properly processing and marshalling
|
|
|
|
// the input and existing resource prior to performing the JSON merge operation
|
|
|
|
// using the MergePatch function from the json-patch library. The preprocessor
|
|
|
|
// is an arbitrary func that can be provided to further process the input. The
|
2022-01-12 14:32:59 +00:00
|
|
|
// MergePatch function accepts and returns byte arrays. Null values will unset
|
|
|
|
// fields defined within the input's FieldData (as if they were never specified)
|
|
|
|
// and remove user-specified keys that exist within a map field.
|
2021-10-13 19:24:31 +00:00
|
|
|
func HandlePatchOperation(input *FieldData, resource map[string]interface{}, preprocessor PatchPreprocessorFunc) ([]byte, error) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if resource == nil {
|
|
|
|
return nil, fmt.Errorf("resource does not exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
inputMap := map[string]interface{}{}
|
|
|
|
|
|
|
|
for key := range input.Raw {
|
2022-01-12 14:32:59 +00:00
|
|
|
if _, ok := input.Schema[key]; !ok {
|
|
|
|
// Only accept fields in the schema
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure data types are handled properly according to the FieldSchema
|
|
|
|
val, ok, err := input.GetOkErr(key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-10-13 19:24:31 +00:00
|
|
|
|
|
|
|
if ok {
|
|
|
|
inputMap[key] = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if preprocessor != nil {
|
|
|
|
inputMap, err = preprocessor(inputMap)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
marshaledResource, err := json.Marshal(resource)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
marshaledInput, err := json.Marshal(inputMap)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
modified, err := jsonpatch.MergePatch(marshaledResource, marshaledInput)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return modified, 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
|
|
|
// SpecialPaths is the logical.Backend implementation.
|
2015-03-31 00:46:18 +00:00
|
|
|
func (b *Backend) SpecialPaths() *logical.Paths {
|
|
|
|
return b.PathsSpecial
|
2015-03-14 06:58:20 +00:00
|
|
|
}
|
|
|
|
|
2017-01-07 23:18:22 +00:00
|
|
|
// Cleanup is used to release resources and prepare to stop the backend
|
2018-01-19 06:44:44 +00:00
|
|
|
func (b *Backend) Cleanup(ctx context.Context) {
|
2015-09-10 14:11:37 +00:00
|
|
|
if b.Clean != nil {
|
2018-01-19 06:44:44 +00:00
|
|
|
b.Clean(ctx)
|
2015-09-10 14:11:37 +00:00
|
|
|
}
|
|
|
|
}
|
2015-09-10 01:58:09 +00:00
|
|
|
|
2017-01-07 23:18:22 +00:00
|
|
|
// InvalidateKey is used to clear caches and reset internal state on key changes
|
2018-01-19 06:44:44 +00:00
|
|
|
func (b *Backend) InvalidateKey(ctx context.Context, key string) {
|
2017-01-07 23:18:22 +00:00
|
|
|
if b.Invalidate != nil {
|
2018-01-19 06:44:44 +00:00
|
|
|
b.Invalidate(ctx, key)
|
2017-01-07 23:18:22 +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
|
|
|
// Setup is used to initialize the backend with the initial backend configuration
|
2018-01-19 06:44:44 +00:00
|
|
|
func (b *Backend) Setup(ctx context.Context, config *logical.BackendConfig) error {
|
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
|
|
|
b.logger = config.Logger
|
|
|
|
b.system = config.System
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-10-17 17:33:00 +00:00
|
|
|
// GetRandomReader returns an io.Reader to use for generating key material in
|
|
|
|
// backends. If the backend has access to an external entropy source it will
|
|
|
|
// return that, otherwise it returns crypto/rand.Reader.
|
|
|
|
func (b *Backend) GetRandomReader() io.Reader {
|
|
|
|
if sourcer, ok := b.System().(entropy.Sourcer); ok {
|
|
|
|
return entropy.NewReader(sourcer)
|
|
|
|
}
|
|
|
|
|
|
|
|
return rand.Reader
|
|
|
|
}
|
|
|
|
|
2015-04-04 18:39:58 +00:00
|
|
|
// Logger can be used to get the logger. If no logger has been set,
|
|
|
|
// the logs will be discarded.
|
2016-08-19 20:45:17 +00:00
|
|
|
func (b *Backend) Logger() log.Logger {
|
2015-04-04 18:39:58 +00:00
|
|
|
if b.logger != nil {
|
|
|
|
return b.logger
|
|
|
|
}
|
|
|
|
|
2018-04-03 00:46:59 +00:00
|
|
|
return logging.NewVaultLoggerWithWriter(ioutil.Discard, log.NoLevel)
|
2015-04-04 18:39:58 +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
|
|
|
// System returns the backend's system view.
|
2015-09-02 19:56:58 +00:00
|
|
|
func (b *Backend) System() logical.SystemView {
|
|
|
|
return b.system
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// Type returns the backend type
|
|
|
|
func (b *Backend) Type() logical.BackendType {
|
|
|
|
return b.BackendType
|
|
|
|
}
|
|
|
|
|
Add plugin version to GRPC interface (#17088)
Add plugin version to GRPC interface
Added a version interface in the sdk/logical so that it can be shared between all plugin types, and then wired it up to RunningVersion in the mounts, auth list, and database systems.
I've tested that this works with auth, database, and secrets plugin types, with the following logic to populate RunningVersion:
If a plugin has a PluginVersion() method implemented, then that is used
If not, and the plugin is built into the Vault binary, then the go.mod version is used
Otherwise, the it will be the empty string.
My apologies for the length of this PR.
* Placeholder backend should be external
We use a placeholder backend (previously a framework.Backend) before a
GRPC plugin is lazy-loaded. This makes us later think the plugin is a
builtin plugin.
So we added a `placeholderBackend` type that overrides the
`IsExternal()` method so that later we know that the plugin is external,
and don't give it a default builtin version.
2022-09-15 23:37:59 +00:00
|
|
|
// Version returns the plugin version information
|
|
|
|
func (b *Backend) PluginVersion() logical.PluginVersion {
|
|
|
|
return logical.PluginVersion{
|
|
|
|
Version: b.RunningVersion,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-14 06:48:49 +00:00
|
|
|
// Route looks up the path that would be used for a given path string.
|
2015-03-14 06:17:25 +00:00
|
|
|
func (b *Backend) Route(path string) *Path {
|
2015-03-14 06:48:49 +00:00
|
|
|
result, _ := b.route(path)
|
|
|
|
return result
|
2015-03-14 06:17:25 +00:00
|
|
|
}
|
|
|
|
|
2015-03-19 13:59:01 +00:00
|
|
|
// Secret is used to look up the secret with the given type.
|
|
|
|
func (b *Backend) Secret(k string) *Secret {
|
|
|
|
for _, s := range b.Secrets {
|
|
|
|
if s.Type == k {
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-03-14 06:25:17 +00:00
|
|
|
func (b *Backend) init() {
|
|
|
|
b.pathsRe = make([]*regexp.Regexp, len(b.Paths))
|
|
|
|
for i, p := range b.Paths {
|
2015-04-02 00:56:03 +00:00
|
|
|
if len(p.Pattern) == 0 {
|
|
|
|
panic(fmt.Sprintf("Routing pattern cannot be blank"))
|
|
|
|
}
|
2015-04-02 00:53:02 +00:00
|
|
|
// Automatically anchor the pattern
|
2015-04-02 00:56:03 +00:00
|
|
|
if p.Pattern[0] != '^' {
|
2015-04-02 00:53:02 +00:00
|
|
|
p.Pattern = "^" + p.Pattern
|
|
|
|
}
|
|
|
|
if p.Pattern[len(p.Pattern)-1] != '$' {
|
|
|
|
p.Pattern = p.Pattern + "$"
|
|
|
|
}
|
2015-03-14 06:25:17 +00:00
|
|
|
b.pathsRe[i] = regexp.MustCompile(p.Pattern)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-14 06:48:49 +00:00
|
|
|
func (b *Backend) route(path string) (*Path, map[string]string) {
|
|
|
|
b.once.Do(b.init)
|
|
|
|
|
|
|
|
for i, re := range b.pathsRe {
|
|
|
|
matches := re.FindStringSubmatch(path)
|
|
|
|
if matches == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have a match, determine the mapping of the captures and
|
|
|
|
// store that for returning.
|
|
|
|
var captures map[string]string
|
|
|
|
path := b.Paths[i]
|
|
|
|
if captureNames := re.SubexpNames(); len(captureNames) > 1 {
|
|
|
|
captures = make(map[string]string, len(captureNames))
|
|
|
|
for i, name := range captureNames {
|
|
|
|
if name != "" {
|
|
|
|
captures[name] = matches[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return path, captures
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2022-03-12 00:00:26 +00:00
|
|
|
func (b *Backend) handleRootHelp(req *logical.Request) (*logical.Response, error) {
|
2015-04-04 04:00:23 +00:00
|
|
|
// Build a mapping of the paths and get the paths alphabetized to
|
|
|
|
// make the output prettier.
|
|
|
|
pathsMap := make(map[string]*Path)
|
2015-04-04 03:36:47 +00:00
|
|
|
paths := make([]string, 0, len(b.Paths))
|
2015-04-04 04:00:23 +00:00
|
|
|
for i, p := range b.pathsRe {
|
2015-04-04 03:36:47 +00:00
|
|
|
paths = append(paths, p.String())
|
2015-04-04 04:00:23 +00:00
|
|
|
pathsMap[p.String()] = b.Paths[i]
|
|
|
|
}
|
|
|
|
sort.Strings(paths)
|
|
|
|
|
|
|
|
// Build the path data
|
|
|
|
pathData := make([]rootHelpTemplatePath, 0, len(paths))
|
|
|
|
for _, route := range paths {
|
|
|
|
p := pathsMap[route]
|
|
|
|
pathData = append(pathData, rootHelpTemplatePath{
|
|
|
|
Path: route,
|
2015-04-04 04:10:54 +00:00
|
|
|
Help: strings.TrimSpace(p.HelpSynopsis),
|
2015-04-04 04:00:23 +00:00
|
|
|
})
|
2015-04-04 03:36:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
help, err := executeTemplate(rootHelpTemplate, &rootHelpTemplateData{
|
2015-04-04 04:10:54 +00:00
|
|
|
Help: strings.TrimSpace(b.Help),
|
2015-04-04 04:00:23 +00:00
|
|
|
Paths: pathData,
|
2015-04-04 03:36:47 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-03-12 00:00:26 +00:00
|
|
|
// Plugins currently don't have a direct knowledge of their own "type"
|
|
|
|
// (e.g. "kv", "cubbyhole"). It defaults to the name of the executable but
|
|
|
|
// can be overridden when the plugin is mounted. Since we need this type to
|
|
|
|
// form the request & response full names, we are passing it as an optional
|
|
|
|
// request parameter to the plugin's root help endpoint. If specified in
|
|
|
|
// the request, the type will be used as part of the request/response body
|
|
|
|
// names in the OAS document.
|
|
|
|
requestResponsePrefix := req.GetString("requestResponsePrefix")
|
|
|
|
|
2022-11-10 23:39:53 +00:00
|
|
|
// Generic mount paths will primarily be used for code generation purposes.
|
|
|
|
// This will result in dynamic mount paths being placed instead of
|
|
|
|
// hardcoded default paths. For example /auth/approle/login would be replaced
|
|
|
|
// with /auth/{mountPath}/login. This will be replaced for all secrets
|
|
|
|
// engines and auth methods that are enabled.
|
|
|
|
genericMountPaths, _ := req.Get("genericMountPaths").(bool)
|
|
|
|
|
2018-11-05 20:24:39 +00:00
|
|
|
// Build OpenAPI response for the entire backend
|
2022-12-07 18:29:51 +00:00
|
|
|
vaultVersion := "unknown"
|
|
|
|
if b.System() != nil {
|
|
|
|
env, err := b.System().PluginEnv(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
vaultVersion = env.VaultVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
doc := NewOASDocument(vaultVersion)
|
2022-11-10 23:39:53 +00:00
|
|
|
if err := documentPaths(b, requestResponsePrefix, genericMountPaths, doc); err != nil {
|
2018-11-05 20:24:39 +00:00
|
|
|
b.Logger().Warn("error generating OpenAPI", "error", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return logical.HelpResponse(help, nil, doc), nil
|
2015-04-04 03:36:47 +00:00
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
func (b *Backend) handleRevokeRenew(ctx context.Context, req *logical.Request) (*logical.Response, error) {
|
2015-04-09 21:21:06 +00:00
|
|
|
// Special case renewal of authentication for credential backends
|
|
|
|
if req.Operation == logical.RenewOperation && req.Auth != nil {
|
2018-01-08 18:31:38 +00:00
|
|
|
return b.handleAuthRenew(ctx, req)
|
2015-04-09 21:21:06 +00:00
|
|
|
}
|
|
|
|
|
2015-03-19 22:11:42 +00:00
|
|
|
if req.Secret == nil {
|
|
|
|
return nil, fmt.Errorf("request has no secret")
|
2015-03-19 18:41:41 +00:00
|
|
|
}
|
|
|
|
|
2015-03-19 22:11:42 +00:00
|
|
|
rawSecretType, ok := req.Secret.InternalData["secret_type"]
|
2015-03-19 18:41:41 +00:00
|
|
|
if !ok {
|
2015-03-19 22:11:42 +00:00
|
|
|
return nil, fmt.Errorf("secret is unsupported by this backend")
|
2015-03-19 18:41:41 +00:00
|
|
|
}
|
2015-03-19 22:11:42 +00:00
|
|
|
secretType, ok := rawSecretType.(string)
|
|
|
|
if !ok {
|
2015-03-19 18:41:41 +00:00
|
|
|
return nil, fmt.Errorf("secret is unsupported by this backend")
|
|
|
|
}
|
|
|
|
|
|
|
|
secret := b.Secret(secretType)
|
|
|
|
if secret == nil {
|
|
|
|
return nil, fmt.Errorf("secret is unsupported by this backend")
|
|
|
|
}
|
|
|
|
|
2015-03-19 19:20:25 +00:00
|
|
|
switch req.Operation {
|
|
|
|
case logical.RenewOperation:
|
2018-01-08 18:31:38 +00:00
|
|
|
return secret.HandleRenew(ctx, req)
|
2015-03-19 19:20:25 +00:00
|
|
|
case logical.RevokeOperation:
|
2018-01-08 18:31:38 +00:00
|
|
|
return secret.HandleRevoke(ctx, req)
|
2015-03-19 19:20:25 +00:00
|
|
|
default:
|
2018-04-05 15:49:21 +00:00
|
|
|
return nil, fmt.Errorf("invalid operation for revoke/renew: %q", req.Operation)
|
2015-03-19 19:20:25 +00:00
|
|
|
}
|
2015-03-19 18:41:41 +00:00
|
|
|
}
|
|
|
|
|
2019-05-10 19:11:42 +00:00
|
|
|
// handleRollback invokes the PeriodicFunc set on the backend. It also does a
|
|
|
|
// WAL rollback operation.
|
2018-01-19 06:44:44 +00:00
|
|
|
func (b *Backend) handleRollback(ctx context.Context, req *logical.Request) (*logical.Response, error) {
|
2016-04-19 18:21:27 +00:00
|
|
|
// Response is not expected from the periodic operation.
|
2019-05-13 15:04:06 +00:00
|
|
|
var resp *logical.Response
|
|
|
|
|
|
|
|
merr := new(multierror.Error)
|
2016-04-19 18:21:27 +00:00
|
|
|
if b.PeriodicFunc != nil {
|
2018-01-19 06:44:44 +00:00
|
|
|
if err := b.PeriodicFunc(ctx, req); err != nil {
|
2019-05-10 19:11:42 +00:00
|
|
|
merr = multierror.Append(merr, err)
|
2016-04-19 01:06:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-13 15:04:06 +00:00
|
|
|
if b.WALRollback != nil {
|
|
|
|
var err error
|
|
|
|
resp, err = b.handleWALRollback(ctx, req)
|
|
|
|
if err != nil {
|
|
|
|
merr = multierror.Append(merr, err)
|
|
|
|
}
|
2019-05-10 19:11:42 +00:00
|
|
|
}
|
2019-05-13 15:04:06 +00:00
|
|
|
return resp, merr.ErrorOrNil()
|
2016-04-19 01:06:26 +00:00
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
func (b *Backend) handleAuthRenew(ctx context.Context, req *logical.Request) (*logical.Response, error) {
|
2015-04-11 21:46:09 +00:00
|
|
|
if b.AuthRenew == nil {
|
|
|
|
return logical.ErrorResponse("this auth type doesn't support renew"), nil
|
|
|
|
}
|
2015-04-11 04:21:06 +00:00
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
return b.AuthRenew(ctx, req, nil)
|
2015-04-09 21:21:06 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 06:44:44 +00:00
|
|
|
func (b *Backend) handleWALRollback(ctx context.Context, req *logical.Request) (*logical.Response, error) {
|
2016-04-19 01:06:26 +00:00
|
|
|
if b.WALRollback == nil {
|
2015-03-18 00:15:23 +00:00
|
|
|
return nil, logical.ErrUnsupportedOperation
|
|
|
|
}
|
|
|
|
|
|
|
|
var merr error
|
2018-01-19 06:44:44 +00:00
|
|
|
keys, err := ListWAL(ctx, req.Storage)
|
2015-03-18 00:15:23 +00:00
|
|
|
if err != nil {
|
2015-03-18 00:58:05 +00:00
|
|
|
return logical.ErrorResponse(err.Error()), nil
|
|
|
|
}
|
|
|
|
if len(keys) == 0 {
|
|
|
|
return nil, nil
|
2015-03-18 00:15:23 +00:00
|
|
|
}
|
|
|
|
|
2015-03-18 00:58:05 +00:00
|
|
|
// Calculate the minimum time that the WAL entries could be
|
|
|
|
// created in order to be rolled back.
|
2016-04-19 01:06:26 +00:00
|
|
|
age := b.WALRollbackMinAge
|
2015-03-18 00:58:05 +00:00
|
|
|
if age == 0 {
|
|
|
|
age = 10 * time.Minute
|
|
|
|
}
|
2016-07-07 21:44:14 +00:00
|
|
|
minAge := time.Now().Add(-1 * age)
|
2015-03-21 10:18:33 +00:00
|
|
|
if _, ok := req.Data["immediate"]; ok {
|
2016-07-07 21:44:14 +00:00
|
|
|
minAge = time.Now().Add(1000 * time.Hour)
|
2015-03-21 10:18:33 +00:00
|
|
|
}
|
2015-03-18 00:58:05 +00:00
|
|
|
|
2015-03-18 00:15:23 +00:00
|
|
|
for _, k := range keys {
|
2018-01-19 06:44:44 +00:00
|
|
|
entry, err := GetWAL(ctx, req.Storage, k)
|
2015-03-18 00:15:23 +00:00
|
|
|
if err != nil {
|
|
|
|
merr = multierror.Append(merr, err)
|
|
|
|
continue
|
|
|
|
}
|
2015-03-18 00:58:05 +00:00
|
|
|
if entry == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the entry isn't old enough, then don't roll it back
|
2016-07-12 20:28:27 +00:00
|
|
|
if !time.Unix(entry.CreatedAt, 0).Before(minAge) {
|
2015-03-18 00:58:05 +00:00
|
|
|
continue
|
|
|
|
}
|
2015-03-18 00:15:23 +00:00
|
|
|
|
2016-04-19 01:06:26 +00:00
|
|
|
// Attempt a WAL rollback
|
2018-01-19 06:44:44 +00:00
|
|
|
err = b.WALRollback(ctx, req, entry.Kind, entry.Data)
|
2015-03-21 10:08:13 +00:00
|
|
|
if err != nil {
|
2018-04-05 15:49:21 +00:00
|
|
|
err = errwrap.Wrapf(fmt.Sprintf("error rolling back %q entry: {{err}}", entry.Kind), err)
|
2015-03-21 10:08:13 +00:00
|
|
|
}
|
|
|
|
if err == nil {
|
2018-01-19 06:44:44 +00:00
|
|
|
err = DeleteWAL(ctx, req.Storage, k)
|
2015-03-21 10:08:13 +00:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
merr = multierror.Append(merr, err)
|
2015-03-18 00:15:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if merr == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return logical.ErrorResponse(merr.Error()), nil
|
|
|
|
}
|
|
|
|
|
2015-03-14 04:11:19 +00:00
|
|
|
// FieldSchema is a basic schema to describe the format of a path field.
|
|
|
|
type FieldSchema struct {
|
2015-03-14 17:12:50 +00:00
|
|
|
Type FieldType
|
|
|
|
Default interface{}
|
|
|
|
Description string
|
2022-02-03 16:44:01 +00:00
|
|
|
|
|
|
|
// The Required and Deprecated members are only used by openapi, and are not actually
|
|
|
|
// used by the framework.
|
|
|
|
Required bool
|
|
|
|
Deprecated bool
|
2019-01-09 00:50:24 +00:00
|
|
|
|
2019-03-28 21:40:56 +00:00
|
|
|
// Query indicates this field will be sent as a query parameter:
|
|
|
|
//
|
|
|
|
// /v1/foo/bar?some_param=some_value
|
|
|
|
//
|
|
|
|
// It doesn't affect handling of the value, but may be used for documentation.
|
|
|
|
Query bool
|
|
|
|
|
2019-01-09 00:50:24 +00:00
|
|
|
// AllowedValues is an optional list of permitted values for this field.
|
|
|
|
// This constraint is not (yet) enforced by the framework, but the list is
|
|
|
|
// output as part of OpenAPI generation and may effect documentation and
|
|
|
|
// dynamic UI generation.
|
|
|
|
AllowedValues []interface{}
|
2019-01-29 23:35:37 +00:00
|
|
|
|
2019-06-19 23:48:58 +00:00
|
|
|
// DisplayAttrs provides hints for UI and documentation generators. They
|
|
|
|
// will be included in OpenAPI output if set.
|
|
|
|
DisplayAttrs *DisplayAttributes
|
2015-03-14 04:15:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DefaultOrZero returns the default value if it is set, or otherwise
|
|
|
|
// the zero value of the type.
|
|
|
|
func (s *FieldSchema) DefaultOrZero() interface{} {
|
|
|
|
if s.Default != nil {
|
2017-02-17 22:25:53 +00:00
|
|
|
switch s.Type {
|
2019-06-26 17:15:36 +00:00
|
|
|
case TypeDurationSecond, TypeSignedDurationSecond:
|
2019-06-20 18:28:32 +00:00
|
|
|
resultDur, err := parseutil.ParseDurationSecond(s.Default)
|
|
|
|
if err != nil {
|
2017-02-17 22:25:53 +00:00
|
|
|
return s.Type.Zero()
|
|
|
|
}
|
2019-06-20 18:28:32 +00:00
|
|
|
return int(resultDur.Seconds())
|
2017-02-17 22:25:53 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
return s.Default
|
|
|
|
}
|
2015-03-14 04:15:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return s.Type.Zero()
|
2015-03-14 04:11:19 +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
|
|
|
// Zero returns the correct zero-value for a specific FieldType
|
2015-03-14 04:11:19 +00:00
|
|
|
func (t FieldType) Zero() interface{} {
|
|
|
|
switch t {
|
2018-06-02 01:30:59 +00:00
|
|
|
case TypeString, TypeNameString, TypeLowerCaseString:
|
2015-03-14 04:11:19 +00:00
|
|
|
return ""
|
|
|
|
case TypeInt:
|
|
|
|
return 0
|
|
|
|
case TypeBool:
|
|
|
|
return false
|
2015-03-31 23:43:37 +00:00
|
|
|
case TypeMap:
|
|
|
|
return map[string]interface{}{}
|
2017-11-07 16:11:49 +00:00
|
|
|
case TypeKVPairs:
|
|
|
|
return map[string]string{}
|
2019-06-26 17:15:36 +00:00
|
|
|
case TypeDurationSecond, TypeSignedDurationSecond:
|
2015-06-17 22:56:26 +00:00
|
|
|
return 0
|
2017-04-18 20:02:31 +00:00
|
|
|
case TypeSlice:
|
|
|
|
return []interface{}{}
|
|
|
|
case TypeStringSlice, TypeCommaStringSlice:
|
|
|
|
return []string{}
|
2018-03-02 23:01:13 +00:00
|
|
|
case TypeCommaIntSlice:
|
|
|
|
return []int{}
|
2018-08-13 18:02:44 +00:00
|
|
|
case TypeHeader:
|
|
|
|
return http.Header{}
|
2020-05-04 22:22:28 +00:00
|
|
|
case TypeFloat:
|
|
|
|
return 0.0
|
2020-09-09 20:53:51 +00:00
|
|
|
case TypeTime:
|
|
|
|
return time.Time{}
|
2015-03-14 04:11:19 +00:00
|
|
|
default:
|
|
|
|
panic("unknown type: " + t.String())
|
|
|
|
}
|
|
|
|
}
|
2015-04-04 03:36:47 +00:00
|
|
|
|
|
|
|
type rootHelpTemplateData struct {
|
|
|
|
Help string
|
2015-04-04 04:00:23 +00:00
|
|
|
Paths []rootHelpTemplatePath
|
|
|
|
}
|
|
|
|
|
|
|
|
type rootHelpTemplatePath struct {
|
|
|
|
Path string
|
|
|
|
Help string
|
2015-04-04 03:36:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const rootHelpTemplate = `
|
|
|
|
## DESCRIPTION
|
|
|
|
|
|
|
|
{{.Help}}
|
|
|
|
|
|
|
|
## PATHS
|
|
|
|
|
|
|
|
The following paths are supported by this backend. To view help for
|
|
|
|
any of the paths below, use the help command with any route matching
|
2015-04-04 04:00:23 +00:00
|
|
|
the path pattern. Note that depending on the policy of your auth token,
|
|
|
|
you may or may not be able to access certain paths.
|
|
|
|
|
|
|
|
{{range .Paths}}{{indent 4 .Path}}
|
|
|
|
{{indent 8 .Help}}
|
2015-04-04 03:36:47 +00:00
|
|
|
|
|
|
|
{{end}}
|
|
|
|
|
|
|
|
`
|