2023-03-15 16:00:52 +00:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
2015-03-11 22:19:41 +00:00
package vault
import (
2018-01-19 06:44:44 +00:00
"context"
2022-09-21 19:25:04 +00:00
"encoding/hex"
2015-03-11 22:19:41 +00:00
"errors"
2015-03-12 01:19:40 +00:00
"fmt"
2018-05-22 21:31:36 +00:00
"os"
2017-02-18 18:51:05 +00:00
"sort"
2015-03-12 01:19:40 +00:00
"strings"
2018-03-02 17:18:39 +00:00
"sync"
2015-08-28 21:25:09 +00:00
"time"
2015-03-15 21:53:41 +00:00
2020-10-27 15:24:43 +00:00
"github.com/armon/go-metrics"
2021-07-16 00:17:31 +00:00
"github.com/hashicorp/go-secure-stdlib/strutil"
2022-08-31 18:23:05 +00:00
"github.com/hashicorp/go-uuid"
2018-11-07 01:21:24 +00:00
"github.com/hashicorp/vault/builtin/plugin"
2023-03-20 11:14:14 +00:00
"github.com/hashicorp/vault/helper/experiments"
2020-10-27 15:24:43 +00:00
"github.com/hashicorp/vault/helper/metricsutil"
2019-04-13 07:44:06 +00:00
"github.com/hashicorp/vault/helper/namespace"
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
"github.com/hashicorp/vault/helper/versions"
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"
2017-10-23 21:15:56 +00:00
"github.com/mitchellh/copystructure"
2015-03-11 22:19:41 +00:00
)
const (
// coreMountConfigPath is used to store the mount configuration.
// Mounts are protected within the Vault itself, which means they
// can only be viewed or modified after an unseal.
coreMountConfigPath = "core/mounts"
2015-03-11 22:50:27 +00:00
2017-02-17 01:13:19 +00:00
// coreLocalMountConfigPath is used to store mount configuration for local
// (non-replicated) mounts
coreLocalMountConfigPath = "core/local-mounts"
2015-03-11 22:50:27 +00:00
// backendBarrierPrefix is the prefix to the UUID used in the
// barrier view for the backends.
backendBarrierPrefix = "logical/"
2015-03-12 19:41:12 +00:00
2015-08-10 17:27:25 +00:00
// systemBarrierPrefix is the prefix used for the
2015-03-12 19:41:12 +00:00
// system logical backend.
systemBarrierPrefix = "sys/"
2016-05-26 16:55:00 +00:00
2016-05-26 17:38:51 +00:00
// mountTableType is the value we expect to find for the mount table and
// corresponding entries
2016-05-26 16:55:00 +00:00
mountTableType = "mounts"
2015-03-11 22:50:27 +00:00
)
2018-05-25 17:39:35 +00:00
// ListingVisibilityType represents the types for listing visibility
type ListingVisibilityType string
2018-03-20 03:16:33 +00:00
const (
2018-07-02 16:13:25 +00:00
// ListingVisibilityDefault is the default value for listing visibility
ListingVisibilityDefault ListingVisibilityType = ""
2018-03-20 03:16:33 +00:00
// ListingVisibilityHidden is the hidden type for listing visibility
2018-07-02 16:13:25 +00:00
ListingVisibilityHidden ListingVisibilityType = "hidden"
2018-03-20 03:16:33 +00:00
// ListingVisibilityUnauth is the unauth type for listing visibility
2018-05-25 17:39:35 +00:00
ListingVisibilityUnauth ListingVisibilityType = "unauth"
2018-09-18 03:03:00 +00:00
2023-10-16 11:38:11 +00:00
mountPathSystem = "sys/"
mountPathIdentity = "identity/"
mountPathCubbyhole = "cubbyhole/"
mountTypeSystem = "system"
mountTypeNSSystem = "ns_system"
mountTypeIdentity = "identity"
mountTypeCubbyhole = "cubbyhole"
mountTypePlugin = "plugin"
mountTypeKV = "kv"
2021-08-30 19:31:11 +00:00
mountTypeNSCubbyhole = "ns_cubbyhole"
2023-10-16 11:38:11 +00:00
mountTypeToken = "token"
mountTypeNSToken = "ns_token"
2018-09-18 03:03:00 +00:00
MountTableUpdateStorage = true
MountTableNoUpdateStorage = false
2018-03-20 03:16:33 +00:00
)
2022-12-14 18:06:33 +00:00
// DeprecationStatus errors
var (
errMountDeprecated = errors . New ( "mount entry associated with deprecated builtin" )
errMountPendingRemoval = errors . New ( "mount entry associated with pending removal builtin" )
errMountRemoved = errors . New ( "mount entry associated with removed builtin" )
)
2015-03-11 22:50:27 +00:00
var (
// loadMountsFailed if loadMounts encounters an error
2015-08-28 21:25:09 +00:00
errLoadMountsFailed = errors . New ( "failed to setup mount table" )
2015-03-12 19:09:30 +00:00
// protectedMounts cannot be remounted
protectedMounts = [ ] string {
2015-03-27 21:00:57 +00:00
"audit/" ,
2015-03-18 22:16:52 +00:00
"auth/" ,
2023-10-16 11:38:11 +00:00
mountPathSystem ,
mountPathCubbyhole ,
mountPathIdentity ,
2015-09-10 01:58:09 +00:00
}
2015-09-19 15:50:50 +00:00
untunableMounts = [ ] string {
2023-10-16 11:38:11 +00:00
mountPathCubbyhole ,
mountPathSystem ,
2015-09-19 15:50:50 +00:00
"audit/" ,
2023-10-16 11:38:11 +00:00
mountPathIdentity ,
2015-09-19 15:50:50 +00:00
}
2015-09-10 01:58:09 +00:00
// singletonMounts can only exist in one location and are
// loaded by default. These are types, not paths.
singletonMounts = [ ] string {
2023-10-16 11:38:11 +00:00
mountTypeCubbyhole ,
mountTypeSystem ,
mountTypeToken ,
mountTypeIdentity ,
2015-03-12 19:09:30 +00:00
}
2017-09-15 13:02:29 +00:00
// mountAliases maps old backend names to new backend names, allowing us
// to move/rename backends but maintain backwards compatibility
mountAliases = map [ string ] string { "generic" : "kv" }
2015-03-11 22:19:41 +00:00
)
2017-06-26 17:14:36 +00:00
func ( c * Core ) generateMountAccessor ( entryType string ) ( string , error ) {
var accessor string
for {
randBytes , err := uuid . GenerateRandomBytes ( 4 )
if err != nil {
return "" , err
}
accessor = fmt . Sprintf ( "%s_%s" , entryType , fmt . Sprintf ( "%08x" , randBytes [ 0 : 4 ] ) )
if entry := c . router . MatchingMountByAccessor ( accessor ) ; entry == nil {
break
}
}
return accessor , nil
}
2015-03-11 22:19:41 +00:00
// MountTable is used to represent the internal mount table
type MountTable struct {
2016-05-26 16:55:00 +00:00
Type string ` json:"type" `
2015-03-11 22:19:41 +00:00
Entries [ ] * MountEntry ` json:"entries" `
}
2022-02-17 20:17:59 +00:00
type MountMigrationStatus int
const (
MigrationInProgressStatus MountMigrationStatus = iota
MigrationSuccessStatus
MigrationFailureStatus
)
func ( m MountMigrationStatus ) String ( ) string {
switch m {
case MigrationInProgressStatus :
return "in-progress"
case MigrationSuccessStatus :
return "success"
case MigrationFailureStatus :
return "failure"
}
return "unknown"
}
type MountMigrationInfo struct {
SourceMount string ` json:"source_mount" `
TargetMount string ` json:"target_mount" `
MigrationStatus string ` json:"status" `
}
2020-10-27 15:24:43 +00:00
// tableMetrics is responsible for setting gauge metrics for
// mount table storage sizes (in bytes) and mount table num
// entries. It does this via setGaugeWithLabels. It then
// saves these metrics in a cache for regular reporting in
// a loop, via AddGaugeLoopMetric.
// Note that the reported storage sizes are pre-encryption
// sizes. Currently barrier uses aes-gcm for encryption, which
// preserves plaintext size, adding a constant of 30 bytes of
// padding, which is negligable and subject to change, and thus
// not accounted for.
func ( c * Core ) tableMetrics ( entryCount int , isLocal bool , isAuth bool , compressedTable [ ] byte ) {
if c . metricsHelper == nil {
// do nothing if metrics are not initialized
return
}
typeAuthLabelMap := map [ bool ] metrics . Label {
2021-04-08 16:43:39 +00:00
true : { Name : "type" , Value : "auth" } ,
false : { Name : "type" , Value : "logical" } ,
2020-10-27 15:24:43 +00:00
}
typeLocalLabelMap := map [ bool ] metrics . Label {
2021-04-08 16:43:39 +00:00
true : { Name : "local" , Value : "true" } ,
false : { Name : "local" , Value : "false" } ,
2020-10-27 15:24:43 +00:00
}
c . metricSink . SetGaugeWithLabels ( metricsutil . LogicalTableSizeName ,
2021-04-08 16:43:39 +00:00
float32 ( entryCount ) , [ ] metrics . Label {
typeAuthLabelMap [ isAuth ] ,
typeLocalLabelMap [ isLocal ] ,
} )
2020-10-27 15:24:43 +00:00
c . metricsHelper . AddGaugeLoopMetric ( metricsutil . LogicalTableSizeName ,
2021-04-08 16:43:39 +00:00
float32 ( entryCount ) , [ ] metrics . Label {
typeAuthLabelMap [ isAuth ] ,
typeLocalLabelMap [ isLocal ] ,
} )
2020-10-27 15:24:43 +00:00
c . metricSink . SetGaugeWithLabels ( metricsutil . PhysicalTableSizeName ,
2021-04-08 16:43:39 +00:00
float32 ( len ( compressedTable ) ) , [ ] metrics . Label {
typeAuthLabelMap [ isAuth ] ,
typeLocalLabelMap [ isLocal ] ,
} )
2020-10-27 15:24:43 +00:00
c . metricsHelper . AddGaugeLoopMetric ( metricsutil . PhysicalTableSizeName ,
2021-04-08 16:43:39 +00:00
float32 ( len ( compressedTable ) ) , [ ] metrics . Label {
typeAuthLabelMap [ isAuth ] ,
typeLocalLabelMap [ isLocal ] ,
} )
2020-10-27 15:24:43 +00:00
}
2016-09-13 15:50:14 +00:00
// shallowClone returns a copy of the mount table that
2015-09-10 02:17:49 +00:00
// keeps the MountEntry locations, so as not to invalidate
// other locations holding pointers. Care needs to be taken
// if modifying entries rather than modifying the table itself
2016-09-13 15:50:14 +00:00
func ( t * MountTable ) shallowClone ( ) * MountTable {
2015-03-12 01:19:40 +00:00
mt := & MountTable {
2016-05-26 16:55:00 +00:00
Type : t . Type ,
2015-03-12 01:19:40 +00:00
Entries : make ( [ ] * MountEntry , len ( t . Entries ) ) ,
}
2020-10-27 15:24:43 +00:00
2015-03-12 01:19:40 +00:00
for i , e := range t . Entries {
2015-09-10 02:17:49 +00:00
mt . Entries [ i ] = e
2015-03-12 01:19:40 +00:00
}
return mt
}
2018-09-18 03:03:00 +00:00
// setTaint is used to set the taint on given entry Accepts either the mount
// entry's path or namespace + path, i.e. <ns-path>/secret/ or <ns-path>/token/
2022-02-17 20:17:59 +00:00
func ( t * MountTable ) setTaint ( nsID , path string , tainted bool , mountState string ) ( * MountEntry , error ) {
2015-04-03 22:59:30 +00:00
n := len ( t . Entries )
for i := 0 ; i < n ; i ++ {
2022-02-17 20:17:59 +00:00
if entry := t . Entries [ i ] ; entry . Path == path && entry . Namespace ( ) . ID == nsID {
2020-10-29 23:47:34 +00:00
t . Entries [ i ] . Tainted = tainted
t . Entries [ i ] . MountState = mountState
2018-09-18 03:03:00 +00:00
return t . Entries [ i ] , nil
2015-04-03 22:59:30 +00:00
}
}
2018-09-18 03:03:00 +00:00
return nil , nil
2015-04-03 22:59:30 +00:00
}
2016-09-30 19:04:50 +00:00
// remove is used to remove a given path entry; returns the entry that was
// removed
2018-09-18 03:03:00 +00:00
func ( t * MountTable ) remove ( ctx context . Context , path string ) ( * MountEntry , error ) {
2015-04-03 22:59:30 +00:00
n := len ( t . Entries )
2018-09-18 03:03:00 +00:00
ns , err := namespace . FromContext ( ctx )
if err != nil {
return nil , err
}
2015-04-03 22:59:30 +00:00
for i := 0 ; i < n ; i ++ {
2018-09-18 03:03:00 +00:00
if entry := t . Entries [ i ] ; entry . Path == path && entry . Namespace ( ) . ID == ns . ID {
2015-04-03 22:59:30 +00:00
t . Entries [ i ] , t . Entries [ n - 1 ] = t . Entries [ n - 1 ] , nil
t . Entries = t . Entries [ : n - 1 ]
2018-09-18 03:03:00 +00:00
return entry , nil
2015-04-03 22:59:30 +00:00
}
}
2018-09-18 03:03:00 +00:00
return nil , nil
2015-04-03 22:59:30 +00:00
}
2019-04-11 15:12:37 +00:00
func ( t * MountTable ) find ( ctx context . Context , path string ) ( * MountEntry , error ) {
n := len ( t . Entries )
ns , err := namespace . FromContext ( ctx )
if err != nil {
return nil , err
}
for i := 0 ; i < n ; i ++ {
if entry := t . Entries [ i ] ; entry . Path == path && entry . Namespace ( ) . ID == ns . ID {
return entry , nil
}
}
return nil , nil
}
2022-04-29 20:15:29 +00:00
func ( t * MountTable ) findByBackendUUID ( ctx context . Context , backendUUID string ) ( * MountEntry , error ) {
n := len ( t . Entries )
ns , err := namespace . FromContext ( ctx )
if err != nil {
return nil , err
}
for i := 0 ; i < n ; i ++ {
if entry := t . Entries [ i ] ; entry . BackendAwareUUID == backendUUID && entry . Namespace ( ) . ID == ns . ID {
return entry , nil
}
}
return nil , nil
}
2017-02-18 18:51:05 +00:00
// sortEntriesByPath sorts the entries in the table by path and returns the
// table; this is useful for tests
func ( t * MountTable ) sortEntriesByPath ( ) * MountTable {
sort . Slice ( t . Entries , func ( i , j int ) bool {
return t . Entries [ i ] . Path < t . Entries [ j ] . Path
} )
return t
}
2018-09-18 03:03:00 +00:00
// sortEntriesByPath sorts the entries in the table by path and returns the
// table; this is useful for tests
func ( t * MountTable ) sortEntriesByPathDepth ( ) * MountTable {
sort . Slice ( t . Entries , func ( i , j int ) bool {
return len ( strings . Split ( t . Entries [ i ] . Namespace ( ) . Path + t . Entries [ i ] . Path , "/" ) ) < len ( strings . Split ( t . Entries [ j ] . Namespace ( ) . Path + t . Entries [ j ] . Path , "/" ) )
} )
return t
}
2020-10-29 23:47:34 +00:00
const mountStateUnmounting = "unmounting"
2015-03-11 22:19:41 +00:00
// MountEntry is used to represent a mount table entry
type MountEntry struct {
2021-04-08 16:43:39 +00:00
Table string ` json:"table" ` // The table it belongs to
Path string ` json:"path" ` // Mount Path
2022-09-09 16:32:28 +00:00
Type string ` json:"type" ` // Logical backend Type. NB: This is the plugin name, e.g. my-vault-plugin, NOT plugin type (e.g. auth).
2021-04-08 16:43:39 +00:00
Description string ` json:"description" ` // User-provided description
UUID string ` json:"uuid" ` // Barrier view UUID
BackendAwareUUID string ` json:"backend_aware_uuid" ` // UUID that can be used by the backend as a helper when a consistent value is needed outside of storage.
Accessor string ` json:"accessor" ` // Unique but more human-friendly ID. Does not change, not used for any sensitive things (like as a salt, which the UUID sometimes is).
Config MountConfig ` json:"config" ` // Configuration related to this mount (but not backend-derived)
Options map [ string ] string ` json:"options" ` // Backend options
Local bool ` json:"local" ` // Local mounts are not replicated or affected by replication
SealWrap bool ` json:"seal_wrap" ` // Whether to wrap CSPs
2020-10-26 19:34:55 +00:00
ExternalEntropyAccess bool ` json:"external_entropy_access,omitempty" ` // Whether to allow external entropy source access
2021-04-08 16:43:39 +00:00
Tainted bool ` json:"tainted,omitempty" ` // Set as a Write-Ahead flag for unmount/remount
MountState string ` json:"mount_state,omitempty" ` // The current mount state. The only non-empty mount state right now is "unmounting"
2019-10-17 17:33:00 +00:00
NamespaceID string ` json:"namespace_id" `
2018-09-18 03:03:00 +00:00
// namespace contains the populated namespace
namespace * namespace . Namespace
2018-03-02 17:18:39 +00:00
2018-03-21 23:56:47 +00:00
// synthesizedConfigCache is used to cache configuration values. These
// particular values are cached since we want to get them at a point-in-time
// without separately managing their locks individually. See SyncCache() for
// the specific values that are being cached.
2018-03-02 17:18:39 +00:00
synthesizedConfigCache sync . Map
2022-08-31 18:23:05 +00:00
// version info
2022-09-20 11:35:50 +00:00
Version string ` json:"plugin_version,omitempty" ` // The semantic version of the mounted plugin, e.g. v1.2.3.
RunningVersion string ` json:"running_plugin_version,omitempty" ` // The semantic version of the mounted plugin as reported by the plugin.
RunningSha256 string ` json:"running_sha256,omitempty" `
2015-03-11 22:19:41 +00:00
}
2015-08-29 13:02:46 +00:00
// MountConfig is used to hold settable options
type MountConfig struct {
2020-10-26 19:34:55 +00:00
DefaultLeaseTTL time . Duration ` json:"default_lease_ttl,omitempty" structs:"default_lease_ttl" mapstructure:"default_lease_ttl" ` // Override for global default
MaxLeaseTTL time . Duration ` json:"max_lease_ttl,omitempty" structs:"max_lease_ttl" mapstructure:"max_lease_ttl" ` // Override for global default
ForceNoCache bool ` json:"force_no_cache,omitempty" structs:"force_no_cache" mapstructure:"force_no_cache" ` // Override for global default
2018-05-25 17:39:35 +00:00
AuditNonHMACRequestKeys [ ] string ` json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys" `
AuditNonHMACResponseKeys [ ] string ` json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys" `
ListingVisibility ListingVisibilityType ` json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility" `
PassthroughRequestHeaders [ ] string ` json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers" `
2019-02-05 21:02:15 +00:00
AllowedResponseHeaders [ ] string ` json:"allowed_response_headers,omitempty" structs:"allowed_response_headers" mapstructure:"allowed_response_headers" `
2020-10-26 19:34:55 +00:00
TokenType logical . TokenType ` json:"token_type,omitempty" structs:"token_type" mapstructure:"token_type" `
2021-11-22 01:08:38 +00:00
AllowedManagedKeys [ ] string ` json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys" `
2022-11-01 18:02:07 +00:00
UserLockoutConfig * UserLockoutConfig ` json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config" `
2018-11-19 23:23:48 +00:00
// PluginName is the name of the plugin registered in the catalog.
//
// Deprecated: MountEntry.Type should be used instead for Vault 1.0.0 and beyond.
PluginName string ` json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name" `
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
}
2022-11-01 18:02:07 +00:00
type UserLockoutConfig struct {
LockoutThreshold uint64 ` json:"lockout_threshold,omitempty" structs:"lockout_threshold" mapstructure:"lockout_threshold" `
LockoutDuration time . Duration ` json:"lockout_duration,omitempty" structs:"lockout_duration" mapstructure:"lockout_duration" `
LockoutCounterReset time . Duration ` json:"lockout_counter_reset,omitempty" structs:"lockout_counter_reset" mapstructure:"lockout_counter_reset" `
DisableLockout bool ` json:"disable_lockout,omitempty" structs:"disable_lockout" mapstructure:"disable_lockout" `
}
type APIUserLockoutConfig struct {
LockoutThreshold string ` json:"lockout_threshold,omitempty" structs:"lockout_threshold" mapstructure:"lockout_threshold" `
LockoutDuration string ` json:"lockout_duration,omitempty" structs:"lockout_duration" mapstructure:"lockout_duration" `
LockoutCounterResetDuration string ` json:"lockout_counter_reset_duration,omitempty" structs:"lockout_counter_reset_duration" mapstructure:"lockout_counter_reset_duration" `
DisableLockout * bool ` json:"lockout_disable,omitempty" structs:"lockout_disable" mapstructure:"lockout_disable" `
}
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
// APIMountConfig is an embedded struct of api.MountConfigInput
type APIMountConfig struct {
2018-05-25 17:39:35 +00:00
DefaultLeaseTTL string ` json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl" `
MaxLeaseTTL string ` json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl" `
ForceNoCache bool ` json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache" `
AuditNonHMACRequestKeys [ ] string ` json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys" `
AuditNonHMACResponseKeys [ ] string ` json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys" `
ListingVisibility ListingVisibilityType ` json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility" `
PassthroughRequestHeaders [ ] string ` json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers" `
2019-02-05 21:02:15 +00:00
AllowedResponseHeaders [ ] string ` json:"allowed_response_headers,omitempty" structs:"allowed_response_headers" mapstructure:"allowed_response_headers" `
2018-10-15 16:56:24 +00:00
TokenType string ` json:"token_type" structs:"token_type" mapstructure:"token_type" `
2021-11-22 01:08:38 +00:00
AllowedManagedKeys [ ] string ` json:"allowed_managed_keys,omitempty" mapstructure:"allowed_managed_keys" `
2022-11-01 18:02:07 +00:00
UserLockoutConfig * UserLockoutConfig ` json:"user_lockout_config,omitempty" mapstructure:"user_lockout_config" `
2022-09-22 19:55:46 +00:00
PluginVersion string ` json:"plugin_version,omitempty" mapstructure:"plugin_version" `
2018-11-19 23:23:48 +00:00
// PluginName is the name of the plugin registered in the catalog.
//
// Deprecated: MountEntry.Type should be used instead for Vault 1.0.0 and beyond.
PluginName string ` json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name" `
2017-10-23 19:35:28 +00:00
}
2022-11-15 23:07:52 +00:00
type FailedLoginUser struct {
aliasName string
mountAccessor string
}
type FailedLoginInfo struct {
count uint
lastFailedLoginTime int
}
2017-10-23 19:35:28 +00:00
// Clone returns a deep copy of the mount entry
func ( e * MountEntry ) Clone ( ) ( * MountEntry , error ) {
cp , err := copystructure . Copy ( e )
if err != nil {
return nil , err
}
return cp . ( * MountEntry ) , nil
2015-08-28 21:25:09 +00:00
}
2023-04-06 07:41:07 +00:00
// IsExternalPlugin returns whether the plugin is running externally
// if the RunningSha256 is non-empty, the builtin is external. Otherwise, it's builtin
func ( e * MountEntry ) IsExternalPlugin ( ) bool {
return e . RunningSha256 != ""
}
// MountClass returns the mount class based on Accessor and Path
func ( e * MountEntry ) MountClass ( ) string {
2023-10-16 11:38:11 +00:00
if e . Accessor == "" || strings . HasPrefix ( e . Path , fmt . Sprintf ( "%s/" , mountPathSystem ) ) {
2023-04-06 07:41:07 +00:00
return ""
}
if e . Table == credentialTableType {
return consts . PluginTypeCredential . String ( )
}
return consts . PluginTypeSecrets . String ( )
}
2018-09-18 03:03:00 +00:00
// Namespace returns the namespace for the mount entry
func ( e * MountEntry ) Namespace ( ) * namespace . Namespace {
return e . namespace
}
// APIPath returns the full API Path for the given mount entry
func ( e * MountEntry ) APIPath ( ) string {
path := e . Path
if e . Table == credentialTableType {
path = credentialRoutePrefix + path
}
return e . namespace . Path + path
}
2022-06-17 12:52:43 +00:00
// APIPathNoNamespace returns the API Path without the namespace for the given mount entry
func ( e * MountEntry ) APIPathNoNamespace ( ) string {
path := e . Path
if e . Table == credentialTableType {
path = credentialRoutePrefix + path
}
return path
}
2018-03-21 23:56:47 +00:00
// SyncCache syncs tunable configuration values to the cache. In the case of
// cached values, they should be retrieved via synthesizedConfigCache.Load()
// instead of accessing them directly through MountConfig.
2018-03-02 17:18:39 +00:00
func ( e * MountEntry ) SyncCache ( ) {
if len ( e . Config . AuditNonHMACRequestKeys ) == 0 {
e . synthesizedConfigCache . Delete ( "audit_non_hmac_request_keys" )
} else {
e . synthesizedConfigCache . Store ( "audit_non_hmac_request_keys" , e . Config . AuditNonHMACRequestKeys )
}
if len ( e . Config . AuditNonHMACResponseKeys ) == 0 {
e . synthesizedConfigCache . Delete ( "audit_non_hmac_response_keys" )
} else {
e . synthesizedConfigCache . Store ( "audit_non_hmac_response_keys" , e . Config . AuditNonHMACResponseKeys )
}
2018-03-21 23:56:47 +00:00
if len ( e . Config . PassthroughRequestHeaders ) == 0 {
e . synthesizedConfigCache . Delete ( "passthrough_request_headers" )
} else {
e . synthesizedConfigCache . Store ( "passthrough_request_headers" , e . Config . PassthroughRequestHeaders )
}
2019-02-05 21:02:15 +00:00
if len ( e . Config . AllowedResponseHeaders ) == 0 {
e . synthesizedConfigCache . Delete ( "allowed_response_headers" )
} else {
e . synthesizedConfigCache . Store ( "allowed_response_headers" , e . Config . AllowedResponseHeaders )
}
2021-11-22 01:08:38 +00:00
if len ( e . Config . AllowedManagedKeys ) == 0 {
e . synthesizedConfigCache . Delete ( "allowed_managed_keys" )
} else {
e . synthesizedConfigCache . Store ( "allowed_managed_keys" , e . Config . AllowedManagedKeys )
}
2018-03-02 17:18:39 +00:00
}
2022-11-04 16:39:09 +00:00
func ( entry * MountEntry ) Deserialize ( ) map [ string ] interface { } {
return map [ string ] interface { } {
"mount_path" : entry . Path ,
"mount_namespace" : entry . Namespace ( ) . Path ,
"uuid" : entry . UUID ,
"accessor" : entry . Accessor ,
"mount_type" : entry . Type ,
}
}
2023-07-19 18:32:53 +00:00
// DecodeMountTable is used for testing
func ( c * Core ) DecodeMountTable ( ctx context . Context , raw [ ] byte ) ( * MountTable , error ) {
return c . decodeMountTable ( ctx , raw )
}
2018-09-18 03:03:00 +00:00
func ( c * Core ) decodeMountTable ( ctx context . Context , raw [ ] byte ) ( * MountTable , error ) {
// Decode into mount table
mountTable := new ( MountTable )
if err := jsonutil . DecodeJSON ( raw , mountTable ) ; err != nil {
return nil , err
}
// Populate the namespace in memory
var mountEntries [ ] * MountEntry
for _ , entry := range mountTable . Entries {
if entry . NamespaceID == "" {
entry . NamespaceID = namespace . RootNamespaceID
}
ns , err := NamespaceByID ( ctx , entry . NamespaceID , c )
if err != nil {
return nil , err
}
if ns == nil {
c . logger . Error ( "namespace on mount entry not found" , "namespace_id" , entry . NamespaceID , "mount_path" , entry . Path , "mount_description" , entry . Description )
continue
}
entry . namespace = ns
mountEntries = append ( mountEntries , entry )
}
return & MountTable {
Type : mountTable . Type ,
Entries : mountEntries ,
} , nil
}
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
// Mount is used to mount a new backend to the mount table.
2018-01-19 06:44:44 +00:00
func ( c * Core ) mount ( ctx context . Context , entry * MountEntry ) error {
2015-03-12 01:19:40 +00:00
// Ensure we end the path in a slash
2017-02-17 01:13:19 +00:00
if ! strings . HasSuffix ( entry . Path , "/" ) {
entry . Path += "/"
2015-03-12 01:19:40 +00:00
}
2015-08-10 17:27:25 +00:00
// Prevent protected paths from being mounted
2015-03-18 22:16:52 +00:00
for _ , p := range protectedMounts {
2018-09-18 03:03:00 +00:00
if strings . HasPrefix ( entry . Path , p ) && entry . namespace == nil {
return logical . CodedError ( 403 , fmt . Sprintf ( "cannot mount %q" , entry . Path ) )
2015-03-18 22:16:52 +00:00
}
}
2015-09-10 01:58:09 +00:00
// Do not allow more than one instance of a singleton mount
for _ , p := range singletonMounts {
2017-02-17 01:13:19 +00:00
if entry . Type == p {
2018-09-18 03:03:00 +00:00
return logical . CodedError ( 403 , fmt . Sprintf ( "mount type of %q is not mountable" , entry . Type ) )
2015-09-10 01:58:09 +00:00
}
}
2019-10-27 20:30:38 +00:00
// Mount internally
if err := c . mountInternal ( ctx , entry , MountTableUpdateStorage ) ; err != nil {
return err
}
return nil
2017-10-23 19:35:28 +00:00
}
2015-09-10 01:58:09 +00:00
2018-09-18 03:03:00 +00:00
func ( c * Core ) mountInternal ( ctx context . Context , entry * MountEntry , updateStorage bool ) error {
2017-01-17 18:02:29 +00:00
c . mountsLock . Lock ( )
2023-06-15 20:41:45 +00:00
c . authLock . Lock ( )
2023-07-13 22:26:01 +00:00
locked := true
2023-06-15 20:41:45 +00:00
unlock := func ( ) {
2023-07-13 22:26:01 +00:00
if locked {
c . authLock . Unlock ( )
c . mountsLock . Unlock ( )
locked = false
}
2023-06-15 20:41:45 +00:00
}
defer unlock ( )
2017-01-17 18:02:29 +00:00
2018-09-18 03:03:00 +00:00
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
if err := verifyNamespace ( c , ns , entry ) ; err != nil {
return err
}
entry . NamespaceID = ns . ID
entry . namespace = ns
// Ensure the cache is populated, don't need the result
NamespaceByID ( ctx , ns . ID , c )
2019-06-04 17:33:36 +00:00
// Basic check for matching names
for _ , ent := range c . mounts . Entries {
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 ) :
return logical . CodedError ( 409 , fmt . Sprintf ( "path is already in use at %s" , ent . Path ) )
}
}
}
// Verify there are no conflicting mounts in the router
2018-09-18 03:03:00 +00:00
if match := c . router . MountConflict ( ctx , entry . Path ) ; match != "" {
2015-08-10 17:27:25 +00:00
return logical . CodedError ( 409 , fmt . Sprintf ( "existing mount at %s" , match ) )
2015-03-12 01:19:40 +00:00
}
// Generate a new UUID and view
2017-02-17 01:13:19 +00:00
if entry . UUID == "" {
entryUUID , err := uuid . GenerateUUID ( )
if err != nil {
return err
}
entry . UUID = entryUUID
}
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 ( 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 ( )
Add path based primary write forwarding (PBPWF) - OSS (#18735)
* Add WriteForwardedStorage to sdk's plugin, logical in OSS
This should allow backends to specify paths to forward write
(storage.Put(...) and storage.Delete(...)) operations for.
Notably, these semantics are subject to change and shouldn't yet be
relied on.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Collect paths for write forwarding in OSS
This adds a path manager to Core, allowing tracking across all Vault
versions of paths which could use write forwarding if available. In
particular, even on OSS offerings, we'll need to template {{clusterId}}
into the paths, in the event of later upgrading to Enterprise. If we
didn't, we'd end up writing paths which will no longer be accessible
post-migration, due to write forwarding now replacing the sentinel with
the actual cluster identifier.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add forwarded writer implementation to OSS
Here, for paths given to us, we determine if we need to do cluster
translation and perform local writing. This is the OSS variant.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Wire up mount-specific request forwarding in OSS
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clarify that state lock needs to be held to call HAState in OSS
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Move cluster sentinel constant to sdk/logical
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Expose ClusterID to Plugins via SystemView
This will let plugins learn what the Cluster's ID is, without having to
resort to hacks like writing a random string to its cluster-prefixed
namespace and then reading it once it has replicated.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add GRPC ClusterID implementation
For any external plugins which wish to use it.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2023-01-20 21:36:18 +00:00
// Resolution to absolute storage paths (versus uuid-relative) needs
// to happen prior to calling into the forwarded writer. Thus we
// intercept writes just before they hit barrier storage.
forwarded , err := c . NewForwardedWriter ( ctx , c . barrier , entry . Local )
if err != nil {
return fmt . Errorf ( "error creating forwarded writer: %v" , err )
}
2018-09-18 03:03:00 +00:00
viewPath := entry . ViewPath ( )
Add path based primary write forwarding (PBPWF) - OSS (#18735)
* Add WriteForwardedStorage to sdk's plugin, logical in OSS
This should allow backends to specify paths to forward write
(storage.Put(...) and storage.Delete(...)) operations for.
Notably, these semantics are subject to change and shouldn't yet be
relied on.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Collect paths for write forwarding in OSS
This adds a path manager to Core, allowing tracking across all Vault
versions of paths which could use write forwarding if available. In
particular, even on OSS offerings, we'll need to template {{clusterId}}
into the paths, in the event of later upgrading to Enterprise. If we
didn't, we'd end up writing paths which will no longer be accessible
post-migration, due to write forwarding now replacing the sentinel with
the actual cluster identifier.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add forwarded writer implementation to OSS
Here, for paths given to us, we determine if we need to do cluster
translation and perform local writing. This is the OSS variant.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Wire up mount-specific request forwarding in OSS
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clarify that state lock needs to be held to call HAState in OSS
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Move cluster sentinel constant to sdk/logical
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Expose ClusterID to Plugins via SystemView
This will let plugins learn what the Cluster's ID is, without having to
resort to hacks like writing a random string to its cluster-prefixed
namespace and then reading it once it has replicated.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add GRPC ClusterID implementation
For any external plugins which wish to use it.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2023-01-20 21:36:18 +00:00
view := NewBarrierView ( forwarded , viewPath )
2018-02-09 19:04:25 +00:00
2019-10-27 20:30:38 +00:00
// Singleton mounts cannot be filtered manually on a per-secondary basis
// from replication.
2018-09-18 03:03:00 +00:00
if strutil . StrListContains ( singletonMounts , entry . Type ) {
addFilterablePath ( c , viewPath )
}
2023-06-15 17:05:27 +00:00
addKnownPath ( c , viewPath )
2018-09-18 03:03:00 +00:00
nilMount , err := preprocessMount ( c , entry , view )
if err != nil {
return err
}
2023-06-15 20:41:45 +00:00
2018-09-18 03:03:00 +00:00
origReadOnlyErr := 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
// We defer this because we're already up and running so we don't need to
// time it for after postUnseal
2018-09-18 03:03:00 +00:00
defer view . setReadOnlyErr ( origReadOnlyErr )
2018-02-09 19:04:25 +00:00
2017-10-23 19:35:28 +00:00
var backend logical . Backend
2017-02-17 01:13:19 +00:00
sysView := c . mountEntrySysView ( entry )
2022-09-21 20:32:00 +00:00
backend , entry . RunningSha256 , err = c . newLogicalBackend ( ctx , entry , sysView , view )
2016-01-13 18:40:08 +00:00
if err != nil {
return err
}
2017-03-04 21:35:41 +00:00
if backend == nil {
return fmt . Errorf ( "nil backend of type %q returned from creation function" , entry . Type )
}
2015-03-12 01:19:40 +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 . TypeLogical {
2023-10-16 11:38:11 +00:00
if entry . Type != mountTypeKV && entry . Type != mountTypeSystem && entry . Type != mountTypeCubbyhole {
2018-11-07 01:21:24 +00:00
return fmt . Errorf ( ` unknown backend type: "%s" ` , entry . Type )
}
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
}
2022-09-23 10:19:38 +00:00
// update the entry running version with the configured version, which was verified during registration.
entry . RunningVersion = entry . Version
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
if entry . RunningVersion == "" {
// don't set the running version to a builtin if it is running as an external plugin
2023-04-12 17:34:35 +00:00
if entry . RunningSha256 == "" {
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
entry . RunningVersion = versions . GetBuiltinVersion ( consts . PluginTypeSecrets , entry . Type )
}
}
2018-09-18 03:03:00 +00:00
addPathCheckers ( c , entry , backend , viewPath )
2017-10-11 17:21:20 +00:00
c . setCoreBackend ( entry , backend , view )
2018-09-18 03:03:00 +00:00
// 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
}
2016-09-13 15:50:14 +00:00
newTable := c . mounts . shallowClone ( )
2017-02-17 01:13:19 +00:00
newTable . Entries = append ( newTable . Entries , entry )
2018-09-18 03:03:00 +00:00
if updateStorage {
if err := c . persistMounts ( ctx , newTable , & entry . Local ) ; err != nil {
c . logger . Error ( "failed to update mount table" , "error" , err )
if err == logical . ErrReadOnly && c . perfStandby {
return err
}
return logical . CodedError ( 500 , "failed to update mount table" )
}
2015-03-12 01:19:40 +00:00
}
c . mounts = newTable
2017-02-17 01:13:19 +00:00
if err := c . router . Mount ( backend , entry . Path , entry , view ) ; err != nil {
2017-01-17 20:15:28 +00:00
return err
}
2023-07-10 19:52:42 +00:00
if err = c . entBuiltinPluginMetrics ( ctx , entry , 1 ) ; err != nil {
c . logger . Error ( "failed to emit enabled ent builtin plugin metrics" , "error" , err )
return err
}
2017-01-17 20:15:28 +00:00
2023-06-15 20:41:45 +00:00
// Re-evaluate filtered paths
if err := runFilteredPathsEvaluation ( ctx , c , false ) ; err != nil {
c . logger . Error ( "failed to evaluate filtered paths" , "error" , err )
unlock ( )
// We failed to evaluate filtered paths so we are undoing the mount operation
if unmountInternalErr := c . unmountInternal ( ctx , entry . Path , MountTableUpdateStorage ) ; unmountInternalErr != nil {
c . logger . Error ( "failed to unmount" , "error" , unmountInternalErr )
}
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 ( origReadOnlyErr )
// 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 ( ) {
2022-10-06 11:54:27 +00:00
c . logger . Info ( "successful mount" , "namespace" , entry . Namespace ( ) . Path , "path" , entry . Path , "type" , entry . Type , "version" , entry . Version )
2016-08-19 20:45:17 +00:00
}
2015-03-12 01:19:40 +00:00
return nil
}
2022-08-31 20:11:14 +00:00
// builtinTypeFromMountEntry attempts to find a builtin PluginType associated
// with the specified MountEntry. Returns consts.PluginTypeUnknown if not found.
func ( c * Core ) builtinTypeFromMountEntry ( ctx context . Context , entry * MountEntry ) consts . PluginType {
if c . builtinRegistry == nil || entry == nil {
return consts . PluginTypeUnknown
}
2022-11-23 18:36:25 +00:00
if ! versions . IsBuiltinVersion ( entry . RunningVersion ) {
2022-11-11 19:51:37 +00:00
return consts . PluginTypeUnknown
}
2022-08-31 20:11:14 +00:00
builtinPluginType := func ( name string , pluginType consts . PluginType ) ( consts . PluginType , bool ) {
2022-11-11 19:51:37 +00:00
plugin , err := c . pluginCatalog . Get ( ctx , name , pluginType , entry . RunningVersion )
2022-08-31 20:11:14 +00:00
if err == nil && plugin != nil && plugin . Builtin {
return plugin . Type , true
}
return consts . PluginTypeUnknown , false
}
// auth plugins have their own dedicated mount table
if pluginType , err := consts . ParsePluginType ( entry . Table ) ; err == nil {
if builtinType , ok := builtinPluginType ( entry . Type , pluginType ) ; ok {
return builtinType
}
}
// Check for possible matches
var builtinTypes [ ] consts . PluginType
for _ , pluginType := range [ ... ] consts . PluginType { consts . PluginTypeSecrets , consts . PluginTypeDatabase } {
if builtinType , ok := builtinPluginType ( entry . Type , pluginType ) ; ok {
builtinTypes = append ( builtinTypes , builtinType )
}
}
if len ( builtinTypes ) == 1 {
return builtinTypes [ 0 ]
}
return consts . PluginTypeUnknown
}
2016-09-19 17:02:25 +00:00
// Unmount is used to unmount a path. The boolean indicates whether the mount
// was found.
2018-01-19 06:44:44 +00:00
func ( c * Core ) unmount ( ctx context . Context , path string ) error {
2015-03-12 01:19:40 +00:00
// Ensure we end the path in a slash
if ! strings . HasSuffix ( path , "/" ) {
path += "/"
}
2015-03-18 22:16:52 +00:00
// Prevent protected paths from being unmounted
for _ , p := range protectedMounts {
if strings . HasPrefix ( path , p ) {
2018-04-05 15:49:21 +00:00
return fmt . Errorf ( "cannot unmount %q" , path )
2015-03-18 22:16:52 +00:00
}
}
2019-11-12 17:17:37 +00:00
// Unmount mount internally
if err := c . unmountInternal ( ctx , path , MountTableUpdateStorage ) ; err != nil {
return err
}
// Re-evaluate filtered paths
2023-06-15 20:41:45 +00:00
if err := runFilteredPathsEvaluation ( ctx , c , true ) ; err != nil {
2019-11-12 17:17:37 +00:00
// 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
2017-10-23 19:35:28 +00:00
}
2015-03-18 22:16:52 +00:00
2018-09-18 03:03:00 +00:00
func ( c * Core ) unmountInternal ( ctx context . Context , path string , updateStorage bool ) error {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
2015-03-12 01:19:40 +00:00
// Verify exact match of the route
2018-09-18 03:03:00 +00:00
match := c . router . MatchingMount ( ctx , path )
if match == "" || ns . Path + path != match {
2017-07-13 17:57:14 +00:00
return fmt . Errorf ( "no matching mount" )
2015-03-12 01:19:40 +00:00
}
2016-09-13 15:50:14 +00:00
// Get the view for this backend
2018-09-18 03:03:00 +00:00
view := c . router . MatchingStorageByAPIPath ( ctx , path )
2017-10-23 19:35:28 +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 )
2015-04-02 18:17:55 +00:00
2015-04-02 01:04:36 +00:00
// Mark the entry as tainted
2022-02-17 20:17:59 +00:00
if err := c . taintMountEntry ( ctx , ns . ID , path , updateStorage , true ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to taint mount entry for path being unmounted" , "error" , err , "path" , path )
2017-07-13 17:57:14 +00:00
return err
2015-04-02 01:04:36 +00:00
}
2016-09-13 15:50:14 +00:00
// Taint the router path to prevent routing. Note that in-flight requests
// are uncertain, right now.
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-02 01:04:36 +00:00
}
2018-09-18 03:03:00 +00:00
rCtx := namespace . ContextWithNamespace ( c . activeContext , ns )
if backend != nil && c . rollback != nil {
2023-08-16 17:34:37 +00:00
// Invoke the rollback manager a final time. This is not fatal as
// various periodic funcs (e.g., PKI) can legitimately error; the
// periodic rollback manager logs these errors rather than failing
// replication like returning this error would do.
2018-09-18 03:03:00 +00:00
if err := c . rollback . Rollback ( rCtx , path ) ; err != nil {
2023-08-16 17:34:37 +00:00
c . logger . Error ( "ignoring rollback error during unmount" , "error" , err , "path" , path )
err = nil
2017-10-23 19:35:28 +00:00
}
2018-09-18 03:03:00 +00:00
}
if backend != nil && c . expiration != nil && updateStorage {
2017-10-23 19:35:28 +00:00
// Revoke all the dynamic keys
2018-09-18 03:03:00 +00:00
if err := c . expiration . RevokePrefix ( rCtx , path , true ) ; err != nil {
2017-10-23 19:35:28 +00:00
return err
}
2018-09-18 03:03:00 +00:00
}
2015-04-02 05:13:08 +00:00
2018-09-18 03:03:00 +00:00
if backend != nil {
2017-10-23 19:35:28 +00:00
// Call cleanup function if it exists
2018-01-19 06:44:44 +00:00
backend . Cleanup ( ctx )
2017-02-01 19:05:25 +00:00
}
2018-09-18 03:03:00 +00:00
viewPath := entry . ViewPath ( )
2017-10-23 19:35:28 +00:00
switch {
2018-09-18 03:03:00 +00:00
case ! updateStorage :
// Don't attempt to clear data, replication will handle this
2019-06-20 20:02:11 +00:00
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 ( "secrets.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
}
2022-09-19 12:15:27 +00:00
case entry . Local , ! c . IsPerfSecondary ( ) :
2017-10-23 19:35:28 +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 ( "secrets.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-10-23 19:35:28 +00:00
return err
}
2015-04-02 05:13:08 +00:00
2022-09-19 12:15:27 +00:00
case ! entry . Local && c . IsPerfSecondary ( ) :
2018-09-18 03:03:00 +00:00
if err := clearIgnoredPaths ( ctx , c , backend , viewPath ) ; err != nil {
return err
}
}
2019-06-04 17:33:36 +00:00
2015-04-02 05:13:08 +00:00
// Remove the mount table entry
2018-09-18 03:03:00 +00:00
if err := c . removeMountEntry ( ctx , path , updateStorage ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to remove mount entry for path being unmounted" , "error" , err , "path" , path )
2017-07-13 17:57:14 +00:00
return err
2015-04-02 05:13:08 +00:00
}
2017-10-23 19:35:28 +00:00
2019-06-04 17:33:36 +00:00
// Unmount the backend entirely
if err := c . router . Unmount ( ctx , path ) ; err != nil {
return err
}
2023-07-10 19:52:42 +00:00
if err = c . entBuiltinPluginMetrics ( ctx , entry , - 1 ) ; err != nil {
c . logger . Error ( "failed to emit disabled ent builtin plugin metrics" , "error" , err )
return err
}
2019-06-04 17:33:36 +00:00
2018-09-18 03:03:00 +00:00
removePathCheckers ( c , entry , viewPath )
2020-07-02 01:14:33 +00:00
if c . quotaManager != nil {
if err := c . quotaManager . HandleBackendDisabling ( ctx , ns . Path , path ) ; err != nil {
c . logger . Error ( "failed to update quotas after disabling mount" , "path" , path , "error" , err )
return err
}
2020-06-26 21:13:16 +00:00
}
2016-08-19 20:45:17 +00:00
if c . logger . IsInfo ( ) {
2018-09-18 03:03:00 +00:00
c . logger . Info ( "successfully unmounted" , "path" , path , "namespace" , ns . 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-02 01:04:36 +00:00
}
// removeMountEntry is used to remove an entry from the mount table
2018-09-18 03:03:00 +00:00
func ( c * Core ) removeMountEntry ( ctx context . Context , path string , updateStorage bool ) error {
2016-09-13 15:50:14 +00:00
c . mountsLock . Lock ( )
defer c . mountsLock . Unlock ( )
2015-03-12 01:19:40 +00:00
// Remove the entry from the mount table
2016-09-13 15:50:14 +00:00
newTable := c . mounts . 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 mounts table" , "path" , path )
2017-03-02 19:37:59 +00:00
return logical . CodedError ( 500 , "failed to remove entry in mounts table" )
}
2015-03-12 01:19:40 +00:00
2017-02-17 01:13:19 +00:00
// When unmounting all entries the JSON code will load back up from storage
// as a nil slice, which kills tests...just set it nil explicitly
if len ( newTable . Entries ) == 0 {
newTable . Entries = nil
}
2018-09-18 03:03:00 +00:00
if updateStorage {
// Update the mount table
if err := c . persistMounts ( ctx , newTable , & entry . Local ) ; err != nil {
c . logger . Error ( "failed to remove entry from mounts table" , "error" , err )
return logical . CodedError ( 500 , "failed to remove entry from mounts table" )
}
2015-03-12 01:19:40 +00:00
}
2015-11-11 16:44:07 +00:00
2015-03-12 01:19:40 +00:00
c . mounts = newTable
2015-04-02 01:04:36 +00:00
return nil
}
2015-03-12 01:19:40 +00:00
2015-04-02 01:04:36 +00:00
// taintMountEntry is used to mark an entry in the mount table as tainted
2022-02-17 20:17:59 +00:00
func ( c * Core ) taintMountEntry ( ctx context . Context , nsID , mountPath string , updateStorage , unmounting bool ) error {
2016-09-13 15:50:14 +00:00
c . mountsLock . Lock ( )
defer c . mountsLock . Unlock ( )
2020-10-29 23:47:34 +00:00
mountState := ""
if unmounting {
mountState = mountStateUnmounting
}
2015-11-11 16:44:07 +00:00
// As modifying the taint of an entry affects shallow clones,
// we simply use the original
2022-02-17 20:17:59 +00:00
entry , err := c . mounts . setTaint ( nsID , mountPath , true , mountState )
2018-09-18 03:03:00 +00:00
if err != nil {
return err
}
2017-03-02 19:37:59 +00:00
if entry == nil {
2022-02-17 20:17:59 +00:00
c . logger . Error ( "nil entry found tainting entry in mounts table" , "path" , mountPath )
2017-03-02 19:37:59 +00:00
return logical . CodedError ( 500 , "failed to taint entry in mounts table" )
}
2015-03-12 01:19:40 +00:00
2018-09-18 03:03:00 +00:00
if updateStorage {
// Update the mount table
if err := c . persistMounts ( ctx , c . mounts , & entry . Local ) ; err != nil {
if err == logical . ErrReadOnly && c . perfStandby {
return err
}
c . logger . Error ( "failed to taint entry in mounts table" , "error" , err )
return logical . CodedError ( 500 , "failed to taint entry in mounts table" )
}
2015-04-02 01:04:36 +00:00
}
2015-11-11 16:44:07 +00:00
2015-03-12 01:19:40 +00:00
return nil
}
2022-09-06 19:49:35 +00:00
// handleDeprecatedMountEntry handles the Deprecation Status of the specified
2022-12-14 18:06:33 +00:00
// mount entry's builtin engine. Warnings are appended to the returned response
// and logged. Errors are returned with a nil response to be processed by the
// caller.
2022-09-08 13:15:10 +00:00
func ( c * Core ) handleDeprecatedMountEntry ( ctx context . Context , entry * MountEntry , pluginType consts . PluginType ) ( * logical . Response , error ) {
2022-12-14 18:06:33 +00:00
resp := & logical . Response { }
2022-09-06 19:49:35 +00:00
if c . builtinRegistry == nil || entry == nil {
2022-09-08 13:15:10 +00:00
return nil , nil
2022-09-06 19:49:35 +00:00
}
// Allow type to be determined from mount entry when not otherwise specified
if pluginType == consts . PluginTypeUnknown {
pluginType = c . builtinTypeFromMountEntry ( ctx , entry )
}
// Handle aliases
t := entry . Type
if alias , ok := mountAliases [ t ] ; ok {
t = alias
}
status , ok := c . builtinRegistry . DeprecationStatus ( t , pluginType )
if ok {
switch status {
case consts . Deprecated :
2022-12-14 18:06:33 +00:00
c . logger . Warn ( "mounting deprecated builtin" , "name" , t , "type" , pluginType , "path" , entry . Path )
resp . AddWarning ( errMountDeprecated . Error ( ) )
2022-09-08 13:15:10 +00:00
return resp , nil
2022-09-06 19:49:35 +00:00
case consts . PendingRemoval :
2022-12-14 18:06:33 +00:00
if c . pendingRemovalMountsAllowed {
c . Logger ( ) . Info ( "mount allowed by environment variable" , "env" , consts . EnvVaultAllowPendingRemovalMounts )
resp . AddWarning ( errMountPendingRemoval . Error ( ) )
return resp , nil
2022-09-06 19:49:35 +00:00
}
2022-12-14 18:06:33 +00:00
return nil , errMountPendingRemoval
2022-09-06 19:49:35 +00:00
case consts . Removed :
2022-12-14 18:06:33 +00:00
return nil , errMountRemoved
2022-09-06 19:49:35 +00:00
}
}
2022-09-08 13:15:10 +00:00
return nil , nil
2022-09-06 19:49:35 +00:00
}
2019-10-27 20:30:38 +00:00
// remountForceInternal 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.
// Should be only used for internal usage.
func ( c * Core ) remountForceInternal ( ctx context . Context , path string , updateStorage bool ) error {
me := c . router . MatchingMountEntry ( ctx , path )
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 . unmountInternal ( ctx , path , updateStorage ) ; err != nil {
return err
}
// Mount internally
if err := c . mountInternal ( ctx , me , updateStorage ) ; err != nil {
return err
}
return nil
}
2022-02-17 20:17:59 +00:00
func ( c * Core ) remountSecretsEngineCurrentNamespace ( ctx context . Context , src , dst string , updateStorage bool ) error {
2018-09-18 03:03:00 +00:00
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
2022-02-17 20:17:59 +00:00
srcPathDetails := c . splitNamespaceAndMountFromPath ( ns . Path , src )
dstPathDetails := c . splitNamespaceAndMountFromPath ( ns . Path , dst )
return c . remountSecretsEngine ( ctx , srcPathDetails , dstPathDetails , updateStorage )
}
// remountSecretsEngine is used to remount a path at a new mount point.
func ( c * Core ) remountSecretsEngine ( ctx context . Context , src , dst namespace . MountPathDetails , updateStorage bool ) error {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
2015-03-12 19:09:30 +00:00
}
2022-02-17 20:17:59 +00:00
// Prevent protected paths from being remounted, or target mounts being in protected paths
2015-03-12 19:09:30 +00:00
for _ , p := range protectedMounts {
2022-02-17 20:17:59 +00:00
if strings . HasPrefix ( src . MountPath , p ) {
return fmt . Errorf ( "cannot remount %q" , src . MountPath )
}
if strings . HasPrefix ( dst . MountPath , p ) {
return fmt . Errorf ( "cannot remount to destination %+v" , dst )
2015-03-12 19:09:30 +00:00
}
}
2022-02-17 20:17:59 +00:00
srcRelativePath := src . GetRelativePath ( ns )
dstRelativePath := dst . GetRelativePath ( ns )
2015-03-12 19:09:30 +00:00
// Verify exact match of the route
2022-02-17 20:17:59 +00:00
srcMatch := c . router . MatchingMountEntry ( ctx , srcRelativePath )
2018-11-07 19:56:24 +00:00
if srcMatch == nil {
2022-02-18 16:04:21 +00:00
return fmt . Errorf ( "no matching mount at %q" , src . Namespace . Path + src . MountPath )
2018-11-07 19:56:24 +00:00
}
2022-02-18 16:04:21 +00:00
if match := c . router . MountConflict ( ctx , dstRelativePath ) ; match != "" {
return fmt . Errorf ( "path in use at %q" , match )
2015-03-12 19:09:30 +00:00
}
2015-04-02 19:03:00 +00:00
// Mark the entry as tainted
2022-02-17 20:17:59 +00:00
if err := c . taintMountEntry ( ctx , src . Namespace . ID , src . MountPath , updateStorage , false ) ; err != nil {
2015-04-02 19:03:00 +00:00
return err
}
// Taint the router path to prevent routing
2022-02-17 20:17:59 +00:00
if err := c . router . Taint ( ctx , srcRelativePath ) ; err != nil {
2015-04-02 19:03:00 +00:00
return err
}
2018-09-18 03:03:00 +00:00
if ! c . IsDRSecondary ( ) {
2023-08-16 17:34:37 +00:00
// Invoke the rollback manager a final time. This is not fatal as
// various periodic funcs (e.g., PKI) can legitimately error; the
// periodic rollback manager logs these errors rather than failing
// replication like returning this error would do.
2018-09-18 03:03:00 +00:00
rCtx := namespace . ContextWithNamespace ( c . activeContext , ns )
2022-02-17 20:17:59 +00:00
if c . rollback != nil && c . router . MatchingBackend ( ctx , srcRelativePath ) != nil {
if err := c . rollback . Rollback ( rCtx , srcRelativePath ) ; err != nil {
2023-08-16 17:34:37 +00:00
c . logger . Error ( "ignoring rollback error during remount" , "error" , err , "path" , src . Namespace . Path + src . MountPath )
err = nil
2019-10-28 15:38:14 +00:00
}
2018-09-18 03:03:00 +00:00
}
2015-04-02 19:03:00 +00:00
2022-02-17 20:17:59 +00:00
revokeCtx := namespace . ContextWithNamespace ( ctx , src . Namespace )
2018-09-18 03:03:00 +00:00
// Revoke all the dynamic keys
2022-02-17 20:17:59 +00:00
if err := c . expiration . RevokePrefix ( revokeCtx , src . MountPath , true ) ; err != nil {
2018-09-18 03:03:00 +00:00
return err
}
2015-04-02 19:03:00 +00:00
}
2016-09-13 15:50:14 +00:00
c . mountsLock . Lock ( )
2022-02-18 16:04:21 +00:00
if match := c . router . MountConflict ( ctx , dstRelativePath ) ; match != "" {
2020-10-29 18:39:41 +00:00
c . mountsLock . Unlock ( )
2022-02-18 16:04:21 +00:00
return fmt . Errorf ( "path in use at %q" , match )
2021-04-08 16:43:39 +00:00
}
2015-03-12 19:09:30 +00:00
2022-02-17 20:17:59 +00:00
srcMatch . Tainted = false
srcMatch . NamespaceID = dst . Namespace . ID
srcMatch . namespace = dst . Namespace
srcPath := srcMatch . Path
srcMatch . Path = dst . MountPath
2017-03-02 19:37:59 +00:00
2015-03-12 19:09:30 +00:00
// Update the mount table
2022-02-17 20:17:59 +00:00
if err := c . persistMounts ( ctx , c . mounts , & srcMatch . Local ) ; err != nil {
srcMatch . Path = srcPath
srcMatch . Tainted = true
2016-09-13 15:50:14 +00:00
c . mountsLock . Unlock ( )
2018-09-18 03:03:00 +00:00
if err == logical . ErrReadOnly && c . perfStandby {
return err
}
2022-02-18 16:04:21 +00:00
return fmt . Errorf ( "failed to update mount table with error %+v" , err )
2015-03-12 19:09:30 +00:00
}
// Remount the backend
2022-02-17 20:17:59 +00:00
if err := c . router . Remount ( ctx , srcRelativePath , dstRelativePath ) ; err != nil {
2021-04-24 16:05:41 +00:00
c . mountsLock . Unlock ( )
2015-03-12 19:09:30 +00:00
return err
}
2021-04-24 16:05:41 +00:00
c . mountsLock . Unlock ( )
2015-04-02 19:03:00 +00:00
// Un-taint the path
2022-02-17 20:17:59 +00:00
if err := c . router . Untaint ( ctx , dstRelativePath ) ; err != nil {
2015-04-02 19:03:00 +00:00
return err
}
2022-02-17 16:10:56 +00:00
return nil
2022-02-16 19:21:42 +00:00
}
2023-01-05 02:52:42 +00:00
// From an input path that has a relative namespace hierarchy followed by a mount point, return the full
2022-02-17 20:17:59 +00:00
// namespace of the mount point, along with the mount point without the namespace related prefix.
2023-01-05 02:52:42 +00:00
// For example, in a hierarchy ns1/ns2/ns3/secret-mount, when currNs is ns1 and path is ns2/ns3/secret-mount,
2022-02-17 20:17:59 +00:00
// this returns the namespace object for ns1/ns2/ns3/, and the string "secret-mount"
func ( c * Core ) splitNamespaceAndMountFromPath ( currNs , path string ) namespace . MountPathDetails {
fullPath := currNs + path
fullNs := c . namespaceByPath ( fullPath )
mountPath := strings . TrimPrefix ( fullPath , fullNs . Path )
return namespace . MountPathDetails {
Namespace : fullNs ,
MountPath : sanitizePath ( mountPath ) ,
}
}
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
// loadMounts is invoked as part of postUnseal to load the mount table
2018-01-19 06:44:44 +00:00
func ( c * Core ) loadMounts ( ctx context . Context ) error {
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
// Load the existing mount table
2018-01-19 06:44:44 +00:00
raw , err := c . barrier . Get ( ctx , coreMountConfigPath )
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to read mount table" , "error" , err )
2015-08-28 21:25:09 +00:00
return errLoadMountsFailed
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
}
2018-01-19 06:44:44 +00:00
rawLocal , err := c . barrier . Get ( ctx , coreLocalMountConfigPath )
2017-02-17 01:13:19 +00:00
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to read local mount table" , "error" , err )
2017-02-17 01:13:19 +00:00
return errLoadMountsFailed
}
2015-11-11 16:44:07 +00:00
c . mountsLock . Lock ( )
defer c . mountsLock . Unlock ( )
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
if raw != nil {
2016-08-04 21:20:37 +00:00
// Check if the persisted value has canary in the beginning. If
// yes, decompress the table and then JSON decode it. If not,
// simply JSON decode it.
2018-09-18 03:03:00 +00:00
mountTable , err := c . decodeMountTable ( ctx , raw . Value )
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to decompress and/or decode the mount table" , "error" , err )
2016-08-04 21:20:37 +00:00
return err
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
}
2020-10-27 15:24:43 +00:00
c . tableMetrics ( len ( mountTable . Entries ) , false , false , raw . Value )
2015-11-11 16:44:07 +00:00
c . mounts = mountTable
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
}
2017-11-08 01:30:02 +00:00
var needPersist bool
if c . mounts == nil {
2018-04-19 17:29:43 +00:00
c . logger . Info ( "no mounts; adding default mount table" )
2017-11-08 01:30:02 +00:00
c . mounts = c . defaultMountTable ( )
needPersist = true
}
2017-02-17 01:13:19 +00:00
if rawLocal != nil {
2018-09-18 03:03:00 +00:00
localMountTable , err := c . decodeMountTable ( ctx , rawLocal . Value )
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to decompress and/or decode the local mount table" , "error" , err )
2017-02-17 01:13:19 +00:00
return err
}
2017-11-07 23:04:37 +00:00
if localMountTable != nil && len ( localMountTable . Entries ) > 0 {
2022-03-30 17:06:49 +00:00
c . tableMetrics ( len ( localMountTable . Entries ) , true , false , rawLocal . Value )
2017-11-07 23:04:37 +00:00
c . mounts . Entries = append ( c . mounts . Entries , localMountTable . Entries ... )
}
2017-02-17 01:13:19 +00:00
}
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
2020-10-29 23:47:34 +00:00
// If this node is a performance standby we do not want to attempt to
// upgrade the mount table, this will be the active node's responsibility.
if ! c . perfStandby {
err := c . runMountUpdates ( ctx , needPersist )
if err != nil {
c . logger . Error ( "failed to run mount table upgrades" , "error" , err )
return err
}
}
for _ , entry := range c . mounts . Entries {
if entry . NamespaceID == "" {
entry . NamespaceID = namespace . RootNamespaceID
}
ns , err := NamespaceByID ( ctx , entry . NamespaceID , c )
if err != nil {
return err
}
if ns == nil {
return namespace . ErrNoNamespace
}
entry . namespace = ns
// Sync values to the cache
entry . SyncCache ( )
}
return nil
}
2016-05-26 16:55:00 +00:00
2020-10-29 23:47:34 +00:00
// Note that this is only designed to work with singletons, as it checks by
// type only.
func ( c * Core ) runMountUpdates ( ctx context . Context , needPersist bool ) error {
2017-11-08 01:30:02 +00:00
// Upgrade to typed mount table
if c . mounts . Type == "" {
c . mounts . Type = mountTableType
needPersist = true
}
2016-05-26 16:55:00 +00:00
2017-11-08 01:30:02 +00:00
for _ , requiredMount := range c . requiredMountTable ( ) . Entries {
foundRequired := false
for _ , coreMount := range c . mounts . Entries {
if coreMount . Type == requiredMount . Type {
foundRequired = true
2019-02-14 20:46:59 +00:00
coreMount . Config = requiredMount . Config
2017-11-08 01:30:02 +00:00
break
2015-09-21 21:54:36 +00:00
}
2017-11-08 01:30:02 +00:00
}
2017-10-23 19:35:28 +00:00
2017-11-08 01:30:02 +00:00
// In a replication scenario we will let sync invalidation take
// care of creating a new required mount that doesn't exist yet.
// This should only happen in the upgrade case where a new one is
// introduced on the primary; otherwise initial bootstrapping will
// ensure this comes over. If we upgrade first, we simply don't
// create the mount, so we won't conflict when we sync. If this is
// local (e.g. cubbyhole) we do still add it.
2022-09-19 12:15:27 +00:00
if ! foundRequired && ( ! c . IsPerfSecondary ( ) || requiredMount . Local ) {
2017-11-08 01:30:02 +00:00
c . mounts . Entries = append ( c . mounts . Entries , requiredMount )
needPersist = true
2015-09-21 21:54:36 +00:00
}
2017-11-08 01:30:02 +00:00
}
2015-09-21 21:54:36 +00:00
2017-11-08 01:30:02 +00:00
// Upgrade to table-scoped entries
for _ , entry := range c . mounts . Entries {
2020-10-29 23:47:34 +00:00
if ! c . PR1103disabled && entry . Type == mountTypeNSCubbyhole && ! entry . Local && ! c . ReplicationState ( ) . HasState ( consts . ReplicationPerformanceSecondary | consts . ReplicationDRSecondary ) {
entry . Local = true
needPersist = true
}
2023-10-16 11:38:11 +00:00
if entry . Type == mountTypeCubbyhole && ! entry . Local {
2017-11-08 01:30:02 +00:00
entry . Local = true
needPersist = true
}
if entry . Table == "" {
entry . Table = c . mounts . Type
needPersist = true
}
if entry . Accessor == "" {
accessor , err := c . generateMountAccessor ( 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 16:55:00 +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
}
2016-05-26 16:55:00 +00:00
2022-11-23 18:36:25 +00:00
// Don't store built-in version in the mount table, to make upgrades smoother.
if versions . IsBuiltinVersion ( entry . Version ) {
entry . Version = ""
needPersist = true
}
2022-11-01 18:02:07 +00:00
}
2017-11-08 01:30:02 +00:00
// Done if we have restored the mount table and we don't need
// to persist
if ! needPersist {
return nil
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
}
2018-09-18 03:03:00 +00:00
// Persist both mount tables
2018-04-11 18:32:55 +00:00
if err := c . persistMounts ( ctx , c . mounts , nil ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to persist mount table" , "error" , err )
2015-08-28 21:25:09 +00:00
return errLoadMountsFailed
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
}
return nil
}
// persistMounts is used to persist the mount table after modification
2018-04-11 18:32:55 +00:00
func ( c * Core ) persistMounts ( ctx context . Context , table * MountTable , local * bool ) error {
2016-05-26 16:55:00 +00:00
if table . Type != mountTableType {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "given table to persist has wrong type" , "actual_type" , table . Type , "expected_type" , mountTableType )
2016-05-26 16:55:00 +00:00
return fmt . Errorf ( "invalid table type given, not persisting" )
}
2017-02-17 01:13:19 +00:00
nonLocalMounts := & MountTable {
Type : mountTableType ,
}
localMounts := & MountTable {
Type : mountTableType ,
}
for _ , entry := range table . Entries {
2022-10-05 19:56:12 +00:00
if entry . Table != table . Type {
c . logger . Error ( "given entry to persist in mount table has wrong table value" , "path" , entry . Path , "entry_table_type" , entry . Table , "actual_type" , table . Type )
return fmt . Errorf ( "invalid mount entry found, not persisting" )
}
2017-02-17 01:13:19 +00:00
if entry . Local {
localMounts . Entries = append ( localMounts . Entries , entry )
} else {
nonLocalMounts . Entries = append ( nonLocalMounts . Entries , entry )
}
}
2020-10-27 15:24:43 +00:00
writeTable := func ( mt * MountTable , path string ) ( [ ] byte , error ) {
2017-03-02 19:37:59 +00:00
// Encode the mount table into JSON and compress it (lzw).
2018-04-11 18:32:55 +00:00
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 mount table" , "error" , err )
2020-10-27 15:24:43 +00:00
return nil , err
2017-03-02 19:37:59 +00:00
}
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +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 ,
}
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +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-03 00:46:59 +00:00
c . logger . Error ( "failed to persist 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
2017-02-17 01:13:19 +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 ( nonLocalMounts , coreMountConfigPath )
2018-04-11 18:32:55 +00:00
if err != nil {
return err
}
2020-10-27 15:24:43 +00:00
c . tableMetrics ( len ( nonLocalMounts . Entries ) , false , false , compressedBytes )
2017-02-17 01:13:19 +00:00
2018-04-11 18:32:55 +00:00
// Write local mounts
2020-10-27 15:24:43 +00:00
compressedBytes , err = writeTable ( localMounts , coreLocalMountConfigPath )
2018-04-11 18:32:55 +00:00
if err != nil {
return err
}
2020-10-27 15:24:43 +00:00
c . tableMetrics ( len ( localMounts . Entries ) , true , false , compressedBytes )
2018-04-11 18:32:55 +00:00
case * local :
// Write local mounts
2020-10-27 15:24:43 +00:00
compressedBytes , err = writeTable ( localMounts , coreLocalMountConfigPath )
if err != nil {
return err
}
c . tableMetrics ( len ( localMounts . Entries ) , true , false , compressedBytes )
2018-04-11 18:32:55 +00:00
default :
// Write non-local mounts
2020-10-27 15:24:43 +00:00
compressedBytes , err = writeTable ( nonLocalMounts , coreMountConfigPath )
if err != nil {
return err
}
c . tableMetrics ( len ( nonLocalMounts . Entries ) , false , false , compressedBytes )
2017-02-17 01:13:19 +00:00
}
2020-10-27 15:24:43 +00:00
return nil
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
}
// setupMounts is invoked after we've loaded the mount table to
// initialize the logical backends and setup the router
2018-01-19 06:44:44 +00:00
func ( c * Core ) setupMounts ( ctx context . Context ) error {
2015-11-11 16:44:07 +00:00
c . mountsLock . Lock ( )
defer c . mountsLock . Unlock ( )
2018-09-18 03:03:00 +00:00
for _ , entry := range c . mounts . sortEntriesByPathDepth ( ) . Entries {
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
// Initialize the backend, special casing for system
2018-09-18 03:03:00 +00:00
barrierPath := entry . ViewPath ( )
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
Add path based primary write forwarding (PBPWF) - OSS (#18735)
* Add WriteForwardedStorage to sdk's plugin, logical in OSS
This should allow backends to specify paths to forward write
(storage.Put(...) and storage.Delete(...)) operations for.
Notably, these semantics are subject to change and shouldn't yet be
relied on.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Collect paths for write forwarding in OSS
This adds a path manager to Core, allowing tracking across all Vault
versions of paths which could use write forwarding if available. In
particular, even on OSS offerings, we'll need to template {{clusterId}}
into the paths, in the event of later upgrading to Enterprise. If we
didn't, we'd end up writing paths which will no longer be accessible
post-migration, due to write forwarding now replacing the sentinel with
the actual cluster identifier.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add forwarded writer implementation to OSS
Here, for paths given to us, we determine if we need to do cluster
translation and perform local writing. This is the OSS variant.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Wire up mount-specific request forwarding in OSS
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Clarify that state lock needs to be held to call HAState in OSS
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Move cluster sentinel constant to sdk/logical
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Expose ClusterID to Plugins via SystemView
This will let plugins learn what the Cluster's ID is, without having to
resort to hacks like writing a random string to its cluster-prefixed
namespace and then reading it once it has replicated.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add GRPC ClusterID implementation
For any external plugins which wish to use it.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2023-01-20 21:36:18 +00:00
// Resolution to absolute storage paths (versus uuid-relative) needs
// to happen prior to calling into the forwarded writer. Thus we
// intercept writes just before they hit barrier storage.
forwarded , err := c . NewForwardedWriter ( ctx , c . barrier , entry . Local )
if err != nil {
return fmt . Errorf ( "error creating forwarded writer: %v" , err )
}
// Create a barrier storage view using the UUID
view := NewBarrierView ( forwarded , barrierPath )
2017-10-23 19:35:28 +00:00
2019-10-27 20:30:38 +00:00
// Singleton mounts cannot be filtered manually on a per-secondary basis
2018-09-18 03:03:00 +00:00
// from replication
if strutil . StrListContains ( singletonMounts , entry . Type ) {
addFilterablePath ( c , barrierPath )
}
2023-06-15 17:05:27 +00:00
addKnownPath ( c , barrierPath )
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
}
origReadOnlyErr := 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 ( origReadOnlyErr )
2018-04-19 17:29:43 +00:00
}
2018-02-09 19:04:25 +00:00
2017-10-23 19:35:28 +00:00
var backend logical . Backend
2015-08-28 21:25:09 +00:00
// Create the new backend
2018-09-18 03:03:00 +00:00
sysView := c . mountEntrySysView ( entry )
2022-09-21 20:32:00 +00:00
backend , entry . RunningSha256 , err = c . newLogicalBackend ( ctx , entry , sysView , view )
2015-03-15 23:25:38 +00:00
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to create mount entry" , "path" , entry . Path , "error" , err )
2022-12-02 18:16:31 +00:00
if c . isMountable ( ctx , entry , consts . PluginTypeSecrets ) {
2018-04-03 00:46:59 +00:00
c . logger . Warn ( "skipping plugin-based mount entry" , "path" , entry . Path )
2017-09-01 05:02:03 +00:00
goto ROUTER_MOUNT
}
2015-08-28 21:25:09 +00:00
return errLoadMountsFailed
2015-03-15 23:25:38 +00:00
}
2017-03-04 21:35:41 +00:00
if backend == nil {
return fmt . Errorf ( "created mount entry of type %q is nil" , entry . Type )
}
2015-03-15 23:25:38 +00:00
2022-09-23 10:19:38 +00:00
// update the entry running version with the configured version, which was verified during registration.
entry . RunningVersion = entry . Version
if entry . RunningVersion == "" {
// don't set the running version to a builtin if it is running as an external plugin
2023-04-12 17:34:35 +00:00
if entry . RunningSha256 == "" {
2022-09-23 10:19:38 +00:00
entry . RunningVersion = versions . GetBuiltinVersion ( consts . PluginTypeSecrets , entry . Type )
}
}
2022-12-14 18:06:33 +00:00
// Do not start up deprecated builtin plugins. If this is a major
// upgrade, stop unsealing and shutdown. If we've already mounted this
// plugin, proceed with unsealing and skip backend initialization.
if versions . IsBuiltinVersion ( entry . RunningVersion ) {
_ , err := c . handleDeprecatedMountEntry ( ctx , entry , consts . PluginTypeSecrets )
if c . isMajorVersionFirstMount ( ctx ) && err != nil {
go c . ShutdownCoreError ( fmt . Errorf ( "could not mount %q: %w" , entry . Type , err ) )
return errLoadMountsFailed
} else if err != nil {
c . logger . Error ( "skipping deprecated mount entry" , "name" , entry . Type , "path" , entry . Path , "error" , err )
backend . Cleanup ( ctx )
backend = nil
goto ROUTER_MOUNT
}
}
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 . TypeLogical {
2023-10-16 11:38:11 +00:00
if entry . Type != mountTypeKV && entry . Type != mountTypeSystem && entry . Type != mountTypeCubbyhole {
2018-11-07 01:21:24 +00:00
return fmt . Errorf ( ` unknown backend type: "%s" ` , entry . Type )
}
2018-09-18 03:03:00 +00:00
}
addPathCheckers ( c , entry , backend , barrierPath )
c . setCoreBackend ( entry , backend , view )
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
}
2018-09-18 03:03:00 +00:00
// 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
}
2017-10-11 17:21:20 +00:00
2017-09-01 05:02:03 +00:00
ROUTER_MOUNT :
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
// Mount the backend
2015-09-04 20:58:12 +00:00
err = c . router . Mount ( backend , entry . Path , entry , view )
2015-03-15 23:25:38 +00:00
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to mount entry" , "path" , entry . Path , "error" , err )
2015-08-28 21:25:09 +00:00
return errLoadMountsFailed
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-10-23 19:35:28 +00:00
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 ( ) {
2022-12-14 18:06:33 +00:00
postUnsealLogger := c . logger . With ( "type" , localEntry . Type , "version" , localEntry . RunningVersion , "path" , localEntry . Path )
2019-07-05 23:55:40 +00:00
if backend == nil {
2022-12-14 18:06:33 +00:00
postUnsealLogger . Error ( "skipping initialization for nil backend" , "path" , localEntry . Path )
2019-07-05 23:55:40 +00:00
return
}
2023-08-16 14:48:12 +00:00
if ! strutil . StrListContains ( singletonMounts , localEntry . Type ) {
view . setReadOnlyErr ( origReadOnlyErr )
}
2019-07-05 23:55:40 +00:00
err := backend . Initialize ( ctx , & logical . InitializationRequest { Storage : view } )
if err != nil {
2022-12-14 18:06:33 +00:00
postUnsealLogger . Error ( "failed to initialize mount backend" , "error" , err )
2019-07-05 23:55:40 +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
if c . logger . IsInfo ( ) {
2022-12-14 18:06:33 +00:00
c . logger . Info ( "successfully mounted" , "type" , entry . Type , "version" , entry . RunningVersion , "path" , entry . Path , "namespace" , entry . Namespace ( ) )
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
}
2015-04-02 18:17:55 +00:00
// Ensure the path is tainted if set in the mount table
if entry . Tainted {
2023-06-27 19:54:28 +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 + entry . Path
c . logger . Debug ( "tainting a mount due to it being marked as tainted in mount table" , "entry.path" , entry . Path , "entry.namespace.path" , entry . Namespace ( ) . Path , "full_path" , path )
c . router . Taint ( ctx , path )
2015-04-02 18:17:55 +00:00
}
2018-09-18 03:03:00 +00:00
// Ensure the cache is populated, don't need the result
NamespaceByID ( ctx , entry . NamespaceID , c )
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
}
return nil
}
// unloadMounts is used before we seal the vault to reset the mounts to
2015-09-10 14:11:37 +00:00
// their unloaded state, calling Cleanup if defined. This is reversed by load and setup mounts.
2018-01-19 06:44:44 +00:00
func ( c * Core ) unloadMounts ( ctx context . Context ) error {
2015-11-11 16:44:07 +00:00
c . mountsLock . Lock ( )
defer c . mountsLock . Unlock ( )
2015-09-10 14:11:37 +00:00
if c . mounts != nil {
2016-09-13 15:50:14 +00:00
mountTable := c . mounts . shallowClone ( )
2015-11-11 16:44:07 +00:00
for _ , e := range mountTable . Entries {
2018-09-18 03:03:00 +00:00
backend := c . router . MatchingBackend ( namespace . ContextWithNamespace ( ctx , e . namespace ) , e . Path )
2017-03-04 21:35:41 +00:00
if backend != nil {
2018-01-19 06:44:44 +00:00
backend . Cleanup ( ctx )
2015-09-10 14:11:37 +00:00
}
2018-09-18 03:03:00 +00:00
viewPath := e . ViewPath ( )
removePathCheckers ( c , e , viewPath )
2015-09-10 14:11:37 +00:00
}
}
2015-11-11 16:44:07 +00:00
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
c . mounts = nil
2019-07-03 02:16:43 +00:00
c . router . reset ( )
2015-09-04 20:58:12 +00:00
c . systemBarrierView = nil
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
return nil
}
2022-09-21 19:25:04 +00:00
// newLogicalBackend is used to create and configure a new logical backend by name.
// It also returns the SHA256 of the plugin, if available.
func ( c * Core ) newLogicalBackend ( ctx context . Context , entry * MountEntry , sysView logical . SystemView , view logical . Storage ) ( logical . Backend , string , error ) {
2018-03-21 19:04:27 +00:00
t := entry . Type
2017-09-15 13:02:29 +00:00
if alias , ok := mountAliases [ t ] ; ok {
t = alias
}
2018-11-07 01:21:24 +00:00
2022-09-21 19:25:04 +00:00
var runningSha string
2015-03-18 22:21:41 +00:00
f , ok := c . logicalBackends [ t ]
2015-03-15 23:25:38 +00:00
if ! ok {
2022-08-31 18:23:05 +00:00
plug , err := c . pluginCatalog . Get ( ctx , t , consts . PluginTypeSecrets , entry . Version )
2022-04-14 20:54:23 +00:00
if err != nil {
2022-09-21 19:25:04 +00:00
return nil , "" , err
2022-04-14 20:54:23 +00:00
}
if plug == nil {
2022-09-09 16:32:28 +00:00
errContext := t
if entry . Version != "" {
errContext += fmt . Sprintf ( ", version=%s" , entry . Version )
}
2022-09-21 19:25:04 +00:00
return nil , "" , fmt . Errorf ( "%w: %s" , ErrPluginNotFound , errContext )
}
if len ( plug . Sha256 ) > 0 {
runningSha = hex . EncodeToString ( plug . Sha256 )
2022-04-14 20:54:23 +00:00
}
f = plugin . Factory
if ! plug . Builtin {
f = wrapFactoryCheckPerms ( c , plugin . Factory )
}
2015-03-15 23:25:38 +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 {
2023-10-16 11:38:11 +00:00
case entry . Type == mountTypePlugin :
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 . PluginTypeSecrets . String ( )
2022-09-09 16:32:28 +00:00
conf [ "plugin_version" ] = entry . Version
2018-11-07 01:21:24 +00:00
2018-09-05 19:52:54 +00:00
backendLogger := c . baseLogger . Named ( fmt . Sprintf ( "secrets.%s.%s" , t , entry . Accessor ) )
2023-02-03 21:24:16 +00:00
pluginEventSender , err := c . events . WithPlugin ( entry . namespace , & logical . EventPluginInfo {
MountClass : consts . PluginTypeSecrets . String ( ) ,
MountAccessor : entry . Accessor ,
MountPath : entry . Path ,
Plugin : entry . Type ,
PluginVersion : entry . RunningVersion ,
Version : entry . Version ,
} )
if err != nil {
return nil , "" , err
}
2015-07-01 00:30:43 +00:00
config := & logical . BackendConfig {
2023-03-20 11:14:14 +00:00
StorageView : view ,
Logger : backendLogger ,
Config : conf ,
System : sysView ,
BackendUUID : entry . BackendAwareUUID ,
}
if c . IsExperimentEnabled ( experiments . VaultExperimentEventsAlpha1 ) {
config . EventsSender = pluginEventSender
2015-07-01 00:30:43 +00:00
}
2023-07-06 10:05:43 +00:00
ctx = namespace . ContextWithNamespace ( ctx , entry . namespace )
2019-06-10 18:07:16 +00:00
ctx = context . WithValue ( ctx , "core_number" , c . coreNumber )
2018-01-19 06:44:44 +00:00
b , err := f ( ctx , config )
2015-04-04 18:39:58 +00:00
if err != nil {
2022-09-21 19:25:04 +00:00
return nil , "" , err
2015-04-04 18:39:58 +00:00
}
2017-03-04 21:35:41 +00:00
if b == nil {
2022-09-21 19:25:04 +00:00
return nil , "" , fmt . Errorf ( "nil backend of type %q returned from factory" , t )
2017-03-04 21:35:41 +00:00
}
2019-06-21 00:55:10 +00:00
addLicenseCallback ( c , b )
2022-09-21 19:25:04 +00:00
return b , runningSha , nil
2015-03-15 23:25:38 +00:00
}
2015-03-11 22:19:41 +00:00
// defaultMountTable creates a default mount table
2017-06-26 17:14:36 +00:00
func ( c * Core ) defaultMountTable ( ) * MountTable {
2016-05-26 16:55:00 +00:00
table := & MountTable {
Type : mountTableType ,
}
2019-02-14 19:55:32 +00:00
table . Entries = append ( table . Entries , c . requiredMountTable ( ) . Entries ... )
2018-03-21 19:04:27 +00:00
2018-05-22 21:31:36 +00:00
if os . Getenv ( "VAULT_INTERACTIVE_DEMO_SERVER" ) != "" {
2019-02-14 19:55:32 +00:00
mountUUID , err := uuid . GenerateUUID ( )
if err != nil {
panic ( fmt . Sprintf ( "could not create default secret mount UUID: %v" , err ) )
}
2023-10-16 11:38:11 +00:00
mountAccessor , err := c . generateMountAccessor ( mountTypeKV )
2019-02-14 19:55:32 +00:00
if err != nil {
panic ( fmt . Sprintf ( "could not generate default secret mount accessor: %v" , err ) )
}
bUUID , err := uuid . GenerateUUID ( )
if err != nil {
panic ( fmt . Sprintf ( "could not create default secret mount backend UUID: %v" , err ) )
}
kvMount := & MountEntry {
Table : mountTableType ,
Path : "secret/" ,
2023-10-16 11:38:11 +00:00
Type : mountTypeKV ,
2019-02-14 19:55:32 +00:00
Description : "key/value secret storage" ,
UUID : mountUUID ,
Accessor : mountAccessor ,
BackendAwareUUID : bUUID ,
Options : map [ string ] string {
"version" : "2" ,
} ,
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 : versions . GetBuiltinVersion ( consts . PluginTypeSecrets , "kv" ) ,
2019-02-14 19:55:32 +00:00
}
table . Entries = append ( table . Entries , kvMount )
2018-05-22 21:31:36 +00:00
}
2019-02-14 19:55:32 +00:00
2015-09-21 21:54:36 +00:00
return table
}
// requiredMountTable() creates a mount table with entries required
// to be available
2017-06-26 17:14:36 +00:00
func ( c * Core ) requiredMountTable ( ) * MountTable {
2016-05-26 16:55:00 +00:00
table := & MountTable {
Type : mountTableType ,
}
2016-01-13 18:40:08 +00:00
cubbyholeUUID , err := uuid . GenerateUUID ( )
if err != nil {
panic ( fmt . Sprintf ( "could not create cubbyhole UUID: %v" , err ) )
}
2017-06-26 17:14:36 +00:00
cubbyholeAccessor , err := c . generateMountAccessor ( "cubbyhole" )
if err != nil {
panic ( fmt . Sprintf ( "could not generate cubbyhole accessor: %v" , err ) )
}
2018-03-21 19:04:27 +00:00
cubbyholeBackendUUID , err := uuid . GenerateUUID ( )
if err != nil {
panic ( fmt . Sprintf ( "could not create cubbyhole backend UUID: %v" , err ) )
}
2015-09-10 01:58:09 +00:00
cubbyholeMount := & MountEntry {
2018-03-21 19:04:27 +00:00
Table : mountTableType ,
2023-10-16 11:38:11 +00:00
Path : mountPathCubbyhole ,
Type : mountTypeCubbyhole ,
2018-03-21 19:04:27 +00:00
Description : "per-token private secret storage" ,
UUID : cubbyholeUUID ,
Accessor : cubbyholeAccessor ,
Local : true ,
BackendAwareUUID : cubbyholeBackendUUID ,
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 : versions . GetBuiltinVersion ( consts . PluginTypeSecrets , "cubbyhole" ) ,
2016-01-13 18:40:08 +00:00
}
sysUUID , err := uuid . GenerateUUID ( )
if err != nil {
panic ( fmt . Sprintf ( "could not create sys UUID: %v" , err ) )
2015-09-10 01:58:09 +00:00
}
2017-06-26 17:14:36 +00:00
sysAccessor , err := c . generateMountAccessor ( "system" )
if err != nil {
panic ( fmt . Sprintf ( "could not generate sys accessor: %v" , err ) )
}
2018-03-21 19:04:27 +00:00
sysBackendUUID , err := uuid . GenerateUUID ( )
if err != nil {
panic ( fmt . Sprintf ( "could not create sys backend UUID: %v" , err ) )
}
2015-03-11 22:19:41 +00:00
sysMount := & MountEntry {
2018-03-21 19:04:27 +00:00
Table : mountTableType ,
Path : "sys/" ,
2023-10-16 11:38:11 +00:00
Type : mountTypeSystem ,
2018-03-21 19:04:27 +00:00
Description : "system endpoints used for control, policy and debugging" ,
UUID : sysUUID ,
Accessor : sysAccessor ,
BackendAwareUUID : sysBackendUUID ,
2022-02-04 21:06:32 +00:00
SealWrap : true , // Enable SealWrap since SystemBackend utilizes SealWrapStorage, see factory in addExtraLogicalBackends().
2019-02-14 20:46:59 +00:00
Config : MountConfig {
PassthroughRequestHeaders : [ ] string { "Accept" } ,
} ,
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 : versions . DefaultBuiltinVersion ,
2015-03-11 22:19:41 +00:00
}
2017-10-11 17:21:20 +00:00
identityUUID , err := uuid . GenerateUUID ( )
if err != nil {
panic ( fmt . Sprintf ( "could not create identity mount entry UUID: %v" , err ) )
}
identityAccessor , err := c . generateMountAccessor ( "identity" )
if err != nil {
panic ( fmt . Sprintf ( "could not generate identity accessor: %v" , err ) )
}
2018-03-21 19:04:27 +00:00
identityBackendUUID , err := uuid . GenerateUUID ( )
if err != nil {
panic ( fmt . Sprintf ( "could not create identity backend UUID: %v" , err ) )
}
2017-10-11 17:21:20 +00:00
identityMount := & MountEntry {
2018-03-21 19:04:27 +00:00
Table : mountTableType ,
Path : "identity/" ,
Type : "identity" ,
Description : "identity store" ,
UUID : identityUUID ,
Accessor : identityAccessor ,
BackendAwareUUID : identityBackendUUID ,
2021-10-14 01:59:36 +00:00
Config : MountConfig {
PassthroughRequestHeaders : [ ] string { "Authorization" } ,
} ,
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 : versions . DefaultBuiltinVersion ,
2017-10-11 17:21:20 +00:00
}
2015-09-10 01:58:09 +00:00
table . Entries = append ( table . Entries , cubbyholeMount )
2015-03-11 22:19:41 +00:00
table . Entries = append ( table . Entries , sysMount )
2017-10-11 17:21:20 +00:00
table . Entries = append ( table . Entries , identityMount )
2015-03-11 22:19:41 +00:00
return table
}
2017-05-05 21:20:30 +00:00
2017-05-08 17:39:18 +00:00
// This function returns tables that are singletons. The main usage of this is
// for replication, so we can send over mount info (especially, UUIDs of
// mounts, which are used for salts) for mounts that may not be able to be
// handled normally. After saving these values on the secondary, we let normal
2017-05-09 12:36:10 +00:00
// sync invalidation do its thing. Because of its use for replication, we
// exclude local mounts.
2017-05-05 21:20:30 +00:00
func ( c * Core ) singletonMountTables ( ) ( mounts , auth * MountTable ) {
mounts = & MountTable { }
auth = & MountTable { }
c . mountsLock . RLock ( )
for _ , entry := range c . mounts . Entries {
2018-09-18 03:03:00 +00:00
if strutil . StrListContains ( singletonMounts , entry . Type ) && ! entry . Local && entry . Namespace ( ) . ID == namespace . RootNamespaceID {
2017-05-05 21:20:30 +00:00
mounts . Entries = append ( mounts . Entries , entry )
}
}
c . mountsLock . RUnlock ( )
c . authLock . RLock ( )
for _ , entry := range c . auth . Entries {
2018-09-18 03:03:00 +00:00
if strutil . StrListContains ( singletonMounts , entry . Type ) && ! entry . Local && entry . Namespace ( ) . ID == namespace . RootNamespaceID {
2017-05-05 21:20:30 +00:00
auth . Entries = append ( auth . Entries , entry )
}
}
c . authLock . RUnlock ( )
return
}
2017-10-11 17:21:20 +00:00
func ( c * Core ) setCoreBackend ( entry * MountEntry , backend logical . Backend , view * BarrierView ) {
switch entry . Type {
2023-10-16 11:38:11 +00:00
case mountTypeSystem :
2017-10-11 17:21:20 +00:00
c . systemBackend = backend . ( * SystemBackend )
c . systemBarrierView = view
2023-10-16 11:38:11 +00:00
case mountTypeCubbyhole :
2017-10-11 17:21:20 +00:00
ch := backend . ( * CubbyholeBackend )
ch . saltUUID = entry . UUID
ch . storageView = view
2018-09-18 03:03:00 +00:00
c . cubbyholeBackend = ch
2023-10-16 11:38:11 +00:00
case mountTypeIdentity :
2017-10-11 17:21:20 +00:00
c . identityStore = backend . ( * IdentityStore )
}
}
2022-02-17 20:17:59 +00:00
func ( c * Core ) createMigrationStatus ( from , to namespace . MountPathDetails ) ( string , error ) {
migrationID , err := uuid . GenerateUUID ( )
if err != nil {
return "" , fmt . Errorf ( "error generating uuid for mount move invocation: %w" , err )
}
migrationInfo := MountMigrationInfo {
SourceMount : from . Namespace . Path + from . MountPath ,
TargetMount : to . Namespace . Path + to . MountPath ,
MigrationStatus : MigrationInProgressStatus . String ( ) ,
}
c . mountMigrationTracker . Store ( migrationID , migrationInfo )
return migrationID , nil
}
func ( c * Core ) setMigrationStatus ( migrationID string , migrationStatus MountMigrationStatus ) error {
migrationInfoRaw , ok := c . mountMigrationTracker . Load ( migrationID )
if ! ok {
return fmt . Errorf ( "Migration Tracker entry missing for ID %s" , migrationID )
}
migrationInfo := migrationInfoRaw . ( MountMigrationInfo )
migrationInfo . MigrationStatus = migrationStatus . String ( )
c . mountMigrationTracker . Store ( migrationID , migrationInfo )
return nil
}
func ( c * Core ) readMigrationStatus ( migrationID string ) * MountMigrationInfo {
migrationInfoRaw , ok := c . mountMigrationTracker . Load ( migrationID )
if ! ok {
return nil
}
migrationInfo := migrationInfoRaw . ( MountMigrationInfo )
return & migrationInfo
}