6930568076
* 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>
253 lines
8.7 KiB
Go
253 lines
8.7 KiB
Go
package logical
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
|
"github.com/hashicorp/vault/sdk/helper/license"
|
|
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
|
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
|
)
|
|
|
|
// SystemView exposes system configuration information in a safe way
|
|
// for logical backends to consume
|
|
type SystemView interface {
|
|
// DefaultLeaseTTL returns the default lease TTL set in Vault configuration
|
|
DefaultLeaseTTL() time.Duration
|
|
|
|
// MaxLeaseTTL returns the max lease TTL set in Vault configuration; backend
|
|
// authors should take care not to issue credentials that last longer than
|
|
// this value, as Vault will revoke them
|
|
MaxLeaseTTL() time.Duration
|
|
|
|
// Returns true if the mount is tainted. A mount is tainted if it is in the
|
|
// process of being unmounted. This should only be used in special
|
|
// circumstances; a primary use-case is as a guard in revocation functions.
|
|
// If revocation of a backend's leases fails it can keep the unmounting
|
|
// process from being successful. If the reason for this failure is not
|
|
// relevant when the mount is tainted (for instance, saving a CRL to disk
|
|
// when the stored CRL will be removed during the unmounting process
|
|
// anyways), we can ignore the errors to allow unmounting to complete.
|
|
Tainted() bool
|
|
|
|
// Returns true if caching is disabled. If true, no caches should be used,
|
|
// despite known slowdowns.
|
|
CachingDisabled() bool
|
|
|
|
// When run from a system view attached to a request, indicates whether the
|
|
// request is affecting a local mount or not
|
|
LocalMount() bool
|
|
|
|
// ReplicationState indicates the state of cluster replication
|
|
ReplicationState() consts.ReplicationState
|
|
|
|
// HasFeature returns true if the feature is currently enabled
|
|
HasFeature(feature license.Features) bool
|
|
|
|
// ResponseWrapData wraps the given data in a cubbyhole and returns the
|
|
// token used to unwrap.
|
|
ResponseWrapData(ctx context.Context, data map[string]interface{}, ttl time.Duration, jwt bool) (*wrapping.ResponseWrapInfo, error)
|
|
|
|
// LookupPlugin looks into the plugin catalog for a plugin with the given
|
|
// name. Returns a PluginRunner or an error if a plugin can not be found.
|
|
LookupPlugin(ctx context.Context, pluginName string, pluginType consts.PluginType) (*pluginutil.PluginRunner, error)
|
|
|
|
// LookupPluginVersion looks into the plugin catalog for a plugin with the given
|
|
// name and version. Returns a PluginRunner or an error if a plugin can not be found.
|
|
LookupPluginVersion(ctx context.Context, pluginName string, pluginType consts.PluginType, version string) (*pluginutil.PluginRunner, error)
|
|
|
|
// ListVersionedPlugins returns information about all plugins of a certain
|
|
// type in the catalog, including any versioning information stored for them.
|
|
ListVersionedPlugins(ctx context.Context, pluginType consts.PluginType) ([]pluginutil.VersionedPlugin, error)
|
|
|
|
// NewPluginClient returns a client for managing the lifecycle of plugin
|
|
// processes
|
|
NewPluginClient(ctx context.Context, config pluginutil.PluginClientConfig) (pluginutil.PluginClient, error)
|
|
|
|
// MlockEnabled returns the configuration setting for enabling mlock on
|
|
// plugins.
|
|
MlockEnabled() bool
|
|
|
|
// EntityInfo returns a subset of information related to the identity entity
|
|
// for the given entity id
|
|
EntityInfo(entityID string) (*Entity, error)
|
|
|
|
// GroupsForEntity returns the group membership information for the provided
|
|
// entity id
|
|
GroupsForEntity(entityID string) ([]*Group, error)
|
|
|
|
// PluginEnv returns Vault environment information used by plugins
|
|
PluginEnv(context.Context) (*PluginEnvironment, error)
|
|
|
|
// VaultVersion returns the version string for the currently running Vault.
|
|
VaultVersion(context.Context) (string, error)
|
|
|
|
// GeneratePasswordFromPolicy generates a password from the policy referenced.
|
|
// If the policy does not exist, this will return an error.
|
|
GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error)
|
|
|
|
// ClusterID returns the replication ClusterID, for use with path-based
|
|
// write forwarding (WriteForwardedPaths). This value will be templated
|
|
// in for the {{cluterId}} sentinel.
|
|
ClusterID(ctx context.Context) (string, error)
|
|
}
|
|
|
|
type PasswordPolicy interface {
|
|
// Generate a random password
|
|
Generate(context.Context, io.Reader) (string, error)
|
|
}
|
|
|
|
type ExtendedSystemView interface {
|
|
Auditor() Auditor
|
|
ForwardGenericRequest(context.Context, *Request) (*Response, error)
|
|
}
|
|
|
|
type PasswordGenerator func() (password string, err error)
|
|
|
|
type StaticSystemView struct {
|
|
DefaultLeaseTTLVal time.Duration
|
|
MaxLeaseTTLVal time.Duration
|
|
SudoPrivilegeVal bool
|
|
TaintedVal bool
|
|
CachingDisabledVal bool
|
|
Primary bool
|
|
EnableMlock bool
|
|
LocalMountVal bool
|
|
ReplicationStateVal consts.ReplicationState
|
|
EntityVal *Entity
|
|
GroupsVal []*Group
|
|
Features license.Features
|
|
PluginEnvironment *PluginEnvironment
|
|
PasswordPolicies map[string]PasswordGenerator
|
|
VersionString string
|
|
ClusterUUID string
|
|
}
|
|
|
|
type noopAuditor struct{}
|
|
|
|
func (a noopAuditor) AuditRequest(ctx context.Context, input *LogInput) error {
|
|
return nil
|
|
}
|
|
|
|
func (a noopAuditor) AuditResponse(ctx context.Context, input *LogInput) error {
|
|
return nil
|
|
}
|
|
|
|
func (d StaticSystemView) Auditor() Auditor {
|
|
return noopAuditor{}
|
|
}
|
|
|
|
func (d StaticSystemView) ForwardGenericRequest(ctx context.Context, req *Request) (*Response, error) {
|
|
return nil, errors.New("ForwardGenericRequest is not implemented in StaticSystemView")
|
|
}
|
|
|
|
func (d StaticSystemView) DefaultLeaseTTL() time.Duration {
|
|
return d.DefaultLeaseTTLVal
|
|
}
|
|
|
|
func (d StaticSystemView) MaxLeaseTTL() time.Duration {
|
|
return d.MaxLeaseTTLVal
|
|
}
|
|
|
|
func (d StaticSystemView) SudoPrivilege(_ context.Context, path string, token string) bool {
|
|
return d.SudoPrivilegeVal
|
|
}
|
|
|
|
func (d StaticSystemView) Tainted() bool {
|
|
return d.TaintedVal
|
|
}
|
|
|
|
func (d StaticSystemView) CachingDisabled() bool {
|
|
return d.CachingDisabledVal
|
|
}
|
|
|
|
func (d StaticSystemView) LocalMount() bool {
|
|
return d.LocalMountVal
|
|
}
|
|
|
|
func (d StaticSystemView) ReplicationState() consts.ReplicationState {
|
|
return d.ReplicationStateVal
|
|
}
|
|
|
|
func (d StaticSystemView) NewPluginClient(ctx context.Context, config pluginutil.PluginClientConfig) (pluginutil.PluginClient, error) {
|
|
return nil, errors.New("NewPluginClient is not implemented in StaticSystemView")
|
|
}
|
|
|
|
func (d StaticSystemView) ResponseWrapData(_ context.Context, data map[string]interface{}, ttl time.Duration, jwt bool) (*wrapping.ResponseWrapInfo, error) {
|
|
return nil, errors.New("ResponseWrapData is not implemented in StaticSystemView")
|
|
}
|
|
|
|
func (d StaticSystemView) LookupPlugin(_ context.Context, _ string, _ consts.PluginType) (*pluginutil.PluginRunner, error) {
|
|
return nil, errors.New("LookupPlugin is not implemented in StaticSystemView")
|
|
}
|
|
|
|
func (d StaticSystemView) LookupPluginVersion(_ context.Context, _ string, _ consts.PluginType, _ string) (*pluginutil.PluginRunner, error) {
|
|
return nil, errors.New("LookupPluginVersion is not implemented in StaticSystemView")
|
|
}
|
|
|
|
func (d StaticSystemView) ListVersionedPlugins(_ context.Context, _ consts.PluginType) ([]pluginutil.VersionedPlugin, error) {
|
|
return nil, errors.New("ListVersionedPlugins is not implemented in StaticSystemView")
|
|
}
|
|
|
|
func (d StaticSystemView) MlockEnabled() bool {
|
|
return d.EnableMlock
|
|
}
|
|
|
|
func (d StaticSystemView) EntityInfo(entityID string) (*Entity, error) {
|
|
return d.EntityVal, nil
|
|
}
|
|
|
|
func (d StaticSystemView) GroupsForEntity(entityID string) ([]*Group, error) {
|
|
return d.GroupsVal, nil
|
|
}
|
|
|
|
func (d StaticSystemView) HasFeature(feature license.Features) bool {
|
|
return d.Features.HasFeature(feature)
|
|
}
|
|
|
|
func (d StaticSystemView) PluginEnv(_ context.Context) (*PluginEnvironment, error) {
|
|
return d.PluginEnvironment, nil
|
|
}
|
|
|
|
func (d StaticSystemView) VaultVersion(_ context.Context) (string, error) {
|
|
return d.VersionString, nil
|
|
}
|
|
|
|
func (d StaticSystemView) GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return "", fmt.Errorf("context timed out")
|
|
default:
|
|
}
|
|
|
|
if d.PasswordPolicies == nil {
|
|
return "", fmt.Errorf("password policy not found")
|
|
}
|
|
policy, exists := d.PasswordPolicies[policyName]
|
|
if !exists {
|
|
return "", fmt.Errorf("password policy not found")
|
|
}
|
|
return policy()
|
|
}
|
|
|
|
func (d *StaticSystemView) SetPasswordPolicy(name string, generator PasswordGenerator) {
|
|
if d.PasswordPolicies == nil {
|
|
d.PasswordPolicies = map[string]PasswordGenerator{}
|
|
}
|
|
d.PasswordPolicies[name] = generator
|
|
}
|
|
|
|
func (d *StaticSystemView) DeletePasswordPolicy(name string) (existed bool) {
|
|
_, existed = d.PasswordPolicies[name]
|
|
delete(d.PasswordPolicies, name)
|
|
return existed
|
|
}
|
|
|
|
func (d StaticSystemView) ClusterID(ctx context.Context) (string, error) {
|
|
return d.ClusterUUID, nil
|
|
}
|