mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-08 07:37:01 +02:00
This isn't perfect for sure, but it's solidifying and becoming a useful base to work off. This routes events sent from auth and secrets plugins to the main `EventBus` in the Vault Core. Events sent from plugins are automatically tagged with the namespace and plugin information associated with them.
292 lines
7.7 KiB
Go
292 lines
7.7 KiB
Go
package plugin
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
|
|
log "github.com/hashicorp/go-hclog"
|
|
"github.com/hashicorp/go-plugin"
|
|
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
"github.com/hashicorp/vault/sdk/plugin/pb"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
var ErrServerInMetadataMode = errors.New("plugin server can not perform action while in metadata mode")
|
|
|
|
// singleImplementationID is the string used to define the instance ID of a
|
|
// non-multiplexed plugin
|
|
const singleImplementationID string = "single"
|
|
|
|
type backendInstance struct {
|
|
brokeredClient *grpc.ClientConn
|
|
backend logical.Backend
|
|
}
|
|
|
|
type backendGRPCPluginServer struct {
|
|
pb.UnimplementedBackendServer
|
|
logical.UnimplementedPluginVersionServer
|
|
|
|
broker *plugin.GRPCBroker
|
|
|
|
instances map[string]backendInstance
|
|
instancesLock sync.RWMutex
|
|
multiplexingSupport bool
|
|
|
|
factory logical.Factory
|
|
|
|
logger log.Logger
|
|
}
|
|
|
|
// getBackendAndBrokeredClientInternal returns the backend and client
|
|
// connection but does not hold a lock
|
|
func (b *backendGRPCPluginServer) getBackendAndBrokeredClientInternal(ctx context.Context) (logical.Backend, *grpc.ClientConn, error) {
|
|
if b.multiplexingSupport {
|
|
id, err := pluginutil.GetMultiplexIDFromContext(ctx)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if inst, ok := b.instances[id]; ok {
|
|
return inst.backend, inst.brokeredClient, nil
|
|
}
|
|
|
|
}
|
|
|
|
if singleImpl, ok := b.instances[singleImplementationID]; ok {
|
|
return singleImpl.backend, singleImpl.brokeredClient, nil
|
|
}
|
|
|
|
return nil, nil, fmt.Errorf("no backend instance found")
|
|
}
|
|
|
|
// getBackendAndBrokeredClient holds a read lock and returns the backend and
|
|
// client connection
|
|
func (b *backendGRPCPluginServer) getBackendAndBrokeredClient(ctx context.Context) (logical.Backend, *grpc.ClientConn, error) {
|
|
b.instancesLock.RLock()
|
|
defer b.instancesLock.RUnlock()
|
|
return b.getBackendAndBrokeredClientInternal(ctx)
|
|
}
|
|
|
|
// Setup dials into the plugin's broker to get a shimmed storage, logger, and
|
|
// system view of the backend. This method also instantiates the underlying
|
|
// backend through its factory func for the server side of the plugin.
|
|
func (b *backendGRPCPluginServer) Setup(ctx context.Context, args *pb.SetupArgs) (*pb.SetupReply, error) {
|
|
var err error
|
|
id := singleImplementationID
|
|
|
|
if b.multiplexingSupport {
|
|
id, err = pluginutil.GetMultiplexIDFromContext(ctx)
|
|
if err != nil {
|
|
return &pb.SetupReply{}, err
|
|
}
|
|
}
|
|
|
|
// Dial for storage
|
|
brokeredClient, err := b.broker.Dial(args.BrokerID)
|
|
if err != nil {
|
|
return &pb.SetupReply{}, err
|
|
}
|
|
|
|
storage := newGRPCStorageClient(brokeredClient)
|
|
sysView := newGRPCSystemView(brokeredClient)
|
|
events := newGRPCEventsClient(brokeredClient)
|
|
|
|
config := &logical.BackendConfig{
|
|
StorageView: storage,
|
|
Logger: b.logger,
|
|
System: sysView,
|
|
Config: args.Config,
|
|
BackendUUID: args.BackendUUID,
|
|
EventsSender: events,
|
|
}
|
|
|
|
// Call the underlying backend factory after shims have been created
|
|
// to set b.backend
|
|
backend, err := b.factory(ctx, config)
|
|
if err != nil {
|
|
return &pb.SetupReply{
|
|
Err: pb.ErrToString(err),
|
|
}, nil
|
|
}
|
|
|
|
b.instancesLock.Lock()
|
|
defer b.instancesLock.Unlock()
|
|
b.instances[id] = backendInstance{
|
|
brokeredClient: brokeredClient,
|
|
backend: backend,
|
|
}
|
|
|
|
return &pb.SetupReply{}, nil
|
|
}
|
|
|
|
func (b *backendGRPCPluginServer) HandleRequest(ctx context.Context, args *pb.HandleRequestArgs) (*pb.HandleRequestReply, error) {
|
|
backend, brokeredClient, err := b.getBackendAndBrokeredClient(ctx)
|
|
if err != nil {
|
|
return &pb.HandleRequestReply{}, err
|
|
}
|
|
|
|
if pluginutil.InMetadataMode() {
|
|
return &pb.HandleRequestReply{}, ErrServerInMetadataMode
|
|
}
|
|
|
|
logicalReq, err := pb.ProtoRequestToLogicalRequest(args.Request)
|
|
if err != nil {
|
|
return &pb.HandleRequestReply{}, err
|
|
}
|
|
|
|
logicalReq.Storage = newGRPCStorageClient(brokeredClient)
|
|
|
|
resp, respErr := backend.HandleRequest(ctx, logicalReq)
|
|
|
|
pbResp, err := pb.LogicalResponseToProtoResponse(resp)
|
|
if err != nil {
|
|
return &pb.HandleRequestReply{}, err
|
|
}
|
|
|
|
return &pb.HandleRequestReply{
|
|
Response: pbResp,
|
|
Err: pb.ErrToProtoErr(respErr),
|
|
}, nil
|
|
}
|
|
|
|
func (b *backendGRPCPluginServer) Initialize(ctx context.Context, _ *pb.InitializeArgs) (*pb.InitializeReply, error) {
|
|
backend, brokeredClient, err := b.getBackendAndBrokeredClient(ctx)
|
|
if err != nil {
|
|
return &pb.InitializeReply{}, err
|
|
}
|
|
|
|
if pluginutil.InMetadataMode() {
|
|
return &pb.InitializeReply{}, ErrServerInMetadataMode
|
|
}
|
|
|
|
req := &logical.InitializationRequest{
|
|
Storage: newGRPCStorageClient(brokeredClient),
|
|
}
|
|
|
|
respErr := backend.Initialize(ctx, req)
|
|
|
|
return &pb.InitializeReply{
|
|
Err: pb.ErrToProtoErr(respErr),
|
|
}, nil
|
|
}
|
|
|
|
func (b *backendGRPCPluginServer) SpecialPaths(ctx context.Context, args *pb.Empty) (*pb.SpecialPathsReply, error) {
|
|
backend, _, err := b.getBackendAndBrokeredClient(ctx)
|
|
if err != nil {
|
|
return &pb.SpecialPathsReply{}, err
|
|
}
|
|
|
|
paths := backend.SpecialPaths()
|
|
if paths == nil {
|
|
return &pb.SpecialPathsReply{
|
|
Paths: nil,
|
|
}, nil
|
|
}
|
|
|
|
return &pb.SpecialPathsReply{
|
|
Paths: &pb.Paths{
|
|
Root: paths.Root,
|
|
Unauthenticated: paths.Unauthenticated,
|
|
LocalStorage: paths.LocalStorage,
|
|
SealWrapStorage: paths.SealWrapStorage,
|
|
WriteForwardedStorage: paths.WriteForwardedStorage,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (b *backendGRPCPluginServer) HandleExistenceCheck(ctx context.Context, args *pb.HandleExistenceCheckArgs) (*pb.HandleExistenceCheckReply, error) {
|
|
backend, brokeredClient, err := b.getBackendAndBrokeredClient(ctx)
|
|
if err != nil {
|
|
return &pb.HandleExistenceCheckReply{}, err
|
|
}
|
|
|
|
if pluginutil.InMetadataMode() {
|
|
return &pb.HandleExistenceCheckReply{}, ErrServerInMetadataMode
|
|
}
|
|
|
|
logicalReq, err := pb.ProtoRequestToLogicalRequest(args.Request)
|
|
if err != nil {
|
|
return &pb.HandleExistenceCheckReply{}, err
|
|
}
|
|
|
|
logicalReq.Storage = newGRPCStorageClient(brokeredClient)
|
|
|
|
checkFound, exists, err := backend.HandleExistenceCheck(ctx, logicalReq)
|
|
return &pb.HandleExistenceCheckReply{
|
|
CheckFound: checkFound,
|
|
Exists: exists,
|
|
Err: pb.ErrToProtoErr(err),
|
|
}, nil
|
|
}
|
|
|
|
func (b *backendGRPCPluginServer) Cleanup(ctx context.Context, _ *pb.Empty) (*pb.Empty, error) {
|
|
b.instancesLock.Lock()
|
|
defer b.instancesLock.Unlock()
|
|
|
|
backend, brokeredClient, err := b.getBackendAndBrokeredClientInternal(ctx)
|
|
if err != nil {
|
|
return &pb.Empty{}, err
|
|
}
|
|
|
|
backend.Cleanup(ctx)
|
|
|
|
// Close rpc clients
|
|
brokeredClient.Close()
|
|
|
|
if b.multiplexingSupport {
|
|
id, err := pluginutil.GetMultiplexIDFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
delete(b.instances, id)
|
|
} else if _, ok := b.instances[singleImplementationID]; ok {
|
|
delete(b.instances, singleImplementationID)
|
|
}
|
|
|
|
return &pb.Empty{}, nil
|
|
}
|
|
|
|
func (b *backendGRPCPluginServer) InvalidateKey(ctx context.Context, args *pb.InvalidateKeyArgs) (*pb.Empty, error) {
|
|
backend, _, err := b.getBackendAndBrokeredClient(ctx)
|
|
if err != nil {
|
|
return &pb.Empty{}, err
|
|
}
|
|
|
|
if pluginutil.InMetadataMode() {
|
|
return &pb.Empty{}, ErrServerInMetadataMode
|
|
}
|
|
|
|
backend.InvalidateKey(ctx, args.Key)
|
|
return &pb.Empty{}, nil
|
|
}
|
|
|
|
func (b *backendGRPCPluginServer) Type(ctx context.Context, _ *pb.Empty) (*pb.TypeReply, error) {
|
|
backend, _, err := b.getBackendAndBrokeredClient(ctx)
|
|
if err != nil {
|
|
return &pb.TypeReply{}, err
|
|
}
|
|
|
|
return &pb.TypeReply{
|
|
Type: uint32(backend.Type()),
|
|
}, nil
|
|
}
|
|
|
|
func (b *backendGRPCPluginServer) Version(ctx context.Context, _ *logical.Empty) (*logical.VersionReply, error) {
|
|
backend, _, err := b.getBackendAndBrokeredClient(ctx)
|
|
if err != nil {
|
|
return &logical.VersionReply{}, err
|
|
}
|
|
|
|
if versioner, ok := backend.(logical.PluginVersioner); ok {
|
|
return &logical.VersionReply{
|
|
PluginVersion: versioner.PluginVersion().Version,
|
|
}, nil
|
|
}
|
|
return &logical.VersionReply{
|
|
PluginVersion: "",
|
|
}, nil
|
|
}
|