mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	 3565c90cf8
			
		
	
	3565c90cf8
	
	
	
		
			
			* feat: DB plugin multiplexing (#13734)
* WIP: start from main and get a plugin runner from core
* move MultiplexedClient map to plugin catalog
- call sys.NewPluginClient from PluginFactory
- updates to getPluginClient
- thread through isMetadataMode
* use go-plugin ClientProtocol interface
- call sys.NewPluginClient from dbplugin.NewPluginClient
* move PluginSets to dbplugin package
- export dbplugin HandshakeConfig
- small refactor of PluginCatalog.getPluginClient
* add removeMultiplexedClient; clean up on Close()
- call client.Kill from plugin catalog
- set rpcClient when muxed client exists
* add ID to dbplugin.DatabasePluginClient struct
* only create one plugin process per plugin type
* update NewPluginClient to return connection ID to sdk
- wrap grpc.ClientConn so we can inject the ID into context
- get ID from context on grpc server
* add v6 multiplexing  protocol version
* WIP: backwards compat for db plugins
* Ensure locking on plugin catalog access
- Create public GetPluginClient method for plugin catalog
- rename postgres db plugin
* use the New constructor for db plugins
* grpc server: use write lock for Close and rlock for CRUD
* cleanup MultiplexedClients on Close
* remove TODO
* fix multiplexing regression with grpc server connection
* cleanup grpc server instances on close
* embed ClientProtocol in Multiplexer interface
* use PluginClientConfig arg to make NewPluginClient plugin type agnostic
* create a new plugin process for non-muxed plugins
* feat: plugin multiplexing: handle plugin client cleanup (#13896)
* use closure for plugin client cleanup
* log and return errors; add comments
* move rpcClient wrapping to core for ID injection
* refactor core plugin client and sdk
* remove unused ID method
* refactor and only wrap clientConn on multiplexed plugins
* rename structs and do not export types
* Slight refactor of system view interface
* Revert "Slight refactor of system view interface"
This reverts commit 73d420e5cd.
* Revert "Revert "Slight refactor of system view interface""
This reverts commit f75527008a1db06d04a23e04c3059674be8adb5f.
* only provide pluginRunner arg to the internal newPluginClient method
* embed ClientProtocol in pluginClient and name logger
* Add back MLock support
* remove enableMlock arg from setupPluginCatalog
* rename plugin util interface to PluginClient
Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
* feature: multiplexing: fix unit tests (#14007)
* fix grpc_server tests and add coverage
* update run_config tests
* add happy path test case for grpc_server ID from context
* update test helpers
* feat: multiplexing: handle v5 plugin compiled with new sdk
* add mux supported flag and increase test coverage
* set multiplexingSupport field in plugin server
* remove multiplexingSupport field in sdk
* revert postgres to non-multiplexed
* add comments on grpc server fields
* use pointer receiver on grpc server methods
* add changelog
* use pointer for grpcserver instance
* Use a gRPC server to determine if a plugin should be multiplexed
* Apply suggestions from code review
Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com>
* add lock to removePluginClient
* add multiplexingSupport field to externalPlugin struct
* do not send nil to grpc MultiplexingSupport
* check err before logging
* handle locking scenario for cleanupFunc
* allow ServeConfigMultiplex to dispense v5 plugin
* reposition structs, add err check and comments
* add comment on locking for cleanupExternalPlugin
Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com>
		
	
		
			
				
	
	
		
			220 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
		
			7.1 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(context.Context, string, consts.PluginType) (*pluginutil.PluginRunner, 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)
 | |
| 
 | |
| 	// 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)
 | |
| }
 | |
| 
 | |
| 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
 | |
| 	VaultVersion        string
 | |
| 	PluginEnvironment   *PluginEnvironment
 | |
| 	PasswordPolicies    map[string]PasswordGenerator
 | |
| }
 | |
| 
 | |
| 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) 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) 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
 | |
| }
 |