github.com/hashicorp/vault/sdk@v0.13.0/plugin/grpc_backend_server.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package plugin
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"sync"
    11  
    12  	log "github.com/hashicorp/go-hclog"
    13  	"github.com/hashicorp/go-plugin"
    14  	"github.com/hashicorp/vault/sdk/helper/pluginutil"
    15  	"github.com/hashicorp/vault/sdk/logical"
    16  	"github.com/hashicorp/vault/sdk/plugin/pb"
    17  	"google.golang.org/grpc"
    18  )
    19  
    20  var ErrServerInMetadataMode = errors.New("plugin server can not perform action while in metadata mode")
    21  
    22  // singleImplementationID is the string used to define the instance ID of a
    23  // non-multiplexed plugin
    24  const singleImplementationID string = "single"
    25  
    26  type backendInstance struct {
    27  	brokeredClient *grpc.ClientConn
    28  	backend        logical.Backend
    29  }
    30  
    31  type backendGRPCPluginServer struct {
    32  	pb.UnimplementedBackendServer
    33  	logical.UnimplementedPluginVersionServer
    34  
    35  	broker *plugin.GRPCBroker
    36  
    37  	instances           map[string]backendInstance
    38  	instancesLock       sync.RWMutex
    39  	multiplexingSupport bool
    40  
    41  	factory logical.Factory
    42  
    43  	logger log.Logger
    44  }
    45  
    46  // getBackendAndBrokeredClientInternal returns the backend and client
    47  // connection but does not hold a lock
    48  func (b *backendGRPCPluginServer) getBackendAndBrokeredClientInternal(ctx context.Context) (logical.Backend, *grpc.ClientConn, error) {
    49  	if b.multiplexingSupport {
    50  		id, err := pluginutil.GetMultiplexIDFromContext(ctx)
    51  		if err != nil {
    52  			return nil, nil, err
    53  		}
    54  
    55  		if inst, ok := b.instances[id]; ok {
    56  			return inst.backend, inst.brokeredClient, nil
    57  		}
    58  
    59  	}
    60  
    61  	if singleImpl, ok := b.instances[singleImplementationID]; ok {
    62  		return singleImpl.backend, singleImpl.brokeredClient, nil
    63  	}
    64  
    65  	return nil, nil, fmt.Errorf("no backend instance found")
    66  }
    67  
    68  // getBackendAndBrokeredClient holds a read lock and returns the backend and
    69  // client connection
    70  func (b *backendGRPCPluginServer) getBackendAndBrokeredClient(ctx context.Context) (logical.Backend, *grpc.ClientConn, error) {
    71  	b.instancesLock.RLock()
    72  	defer b.instancesLock.RUnlock()
    73  	return b.getBackendAndBrokeredClientInternal(ctx)
    74  }
    75  
    76  // Setup dials into the plugin's broker to get a shimmed storage, logger, and
    77  // system view of the backend. This method also instantiates the underlying
    78  // backend through its factory func for the server side of the plugin.
    79  func (b *backendGRPCPluginServer) Setup(ctx context.Context, args *pb.SetupArgs) (*pb.SetupReply, error) {
    80  	var err error
    81  	id := singleImplementationID
    82  
    83  	if b.multiplexingSupport {
    84  		id, err = pluginutil.GetMultiplexIDFromContext(ctx)
    85  		if err != nil {
    86  			return &pb.SetupReply{}, err
    87  		}
    88  	}
    89  
    90  	// Dial for storage
    91  	brokeredClient, err := b.broker.Dial(args.BrokerID)
    92  	if err != nil {
    93  		return &pb.SetupReply{}, err
    94  	}
    95  
    96  	storage := newGRPCStorageClient(brokeredClient)
    97  	sysView := newGRPCSystemView(brokeredClient)
    98  	events := newGRPCEventsClient(brokeredClient)
    99  
   100  	config := &logical.BackendConfig{
   101  		StorageView:  storage,
   102  		Logger:       b.logger,
   103  		System:       sysView,
   104  		Config:       args.Config,
   105  		BackendUUID:  args.BackendUUID,
   106  		EventsSender: events,
   107  	}
   108  
   109  	// Call the underlying backend factory after shims have been created
   110  	// to set b.backend
   111  	backend, err := b.factory(ctx, config)
   112  	if err != nil {
   113  		return &pb.SetupReply{
   114  			Err: pb.ErrToString(err),
   115  		}, nil
   116  	}
   117  
   118  	b.instancesLock.Lock()
   119  	defer b.instancesLock.Unlock()
   120  	b.instances[id] = backendInstance{
   121  		brokeredClient: brokeredClient,
   122  		backend:        backend,
   123  	}
   124  
   125  	return &pb.SetupReply{}, nil
   126  }
   127  
   128  func (b *backendGRPCPluginServer) HandleRequest(ctx context.Context, args *pb.HandleRequestArgs) (*pb.HandleRequestReply, error) {
   129  	backend, brokeredClient, err := b.getBackendAndBrokeredClient(ctx)
   130  	if err != nil {
   131  		return &pb.HandleRequestReply{}, err
   132  	}
   133  
   134  	if pluginutil.InMetadataMode() {
   135  		return &pb.HandleRequestReply{}, ErrServerInMetadataMode
   136  	}
   137  
   138  	logicalReq, err := pb.ProtoRequestToLogicalRequest(args.Request)
   139  	if err != nil {
   140  		return &pb.HandleRequestReply{}, err
   141  	}
   142  
   143  	logicalReq.Storage = newGRPCStorageClient(brokeredClient)
   144  
   145  	resp, respErr := backend.HandleRequest(ctx, logicalReq)
   146  
   147  	pbResp, err := pb.LogicalResponseToProtoResponse(resp)
   148  	if err != nil {
   149  		return &pb.HandleRequestReply{}, err
   150  	}
   151  
   152  	return &pb.HandleRequestReply{
   153  		Response: pbResp,
   154  		Err:      pb.ErrToProtoErr(respErr),
   155  	}, nil
   156  }
   157  
   158  func (b *backendGRPCPluginServer) Initialize(ctx context.Context, _ *pb.InitializeArgs) (*pb.InitializeReply, error) {
   159  	backend, brokeredClient, err := b.getBackendAndBrokeredClient(ctx)
   160  	if err != nil {
   161  		return &pb.InitializeReply{}, err
   162  	}
   163  
   164  	if pluginutil.InMetadataMode() {
   165  		return &pb.InitializeReply{}, ErrServerInMetadataMode
   166  	}
   167  
   168  	req := &logical.InitializationRequest{
   169  		Storage: newGRPCStorageClient(brokeredClient),
   170  	}
   171  
   172  	respErr := backend.Initialize(ctx, req)
   173  
   174  	return &pb.InitializeReply{
   175  		Err: pb.ErrToProtoErr(respErr),
   176  	}, nil
   177  }
   178  
   179  func (b *backendGRPCPluginServer) SpecialPaths(ctx context.Context, args *pb.Empty) (*pb.SpecialPathsReply, error) {
   180  	backend, _, err := b.getBackendAndBrokeredClient(ctx)
   181  	if err != nil {
   182  		return &pb.SpecialPathsReply{}, err
   183  	}
   184  
   185  	paths := backend.SpecialPaths()
   186  	if paths == nil {
   187  		return &pb.SpecialPathsReply{
   188  			Paths: nil,
   189  		}, nil
   190  	}
   191  
   192  	return &pb.SpecialPathsReply{
   193  		Paths: &pb.Paths{
   194  			Root:                  paths.Root,
   195  			Unauthenticated:       paths.Unauthenticated,
   196  			LocalStorage:          paths.LocalStorage,
   197  			SealWrapStorage:       paths.SealWrapStorage,
   198  			WriteForwardedStorage: paths.WriteForwardedStorage,
   199  			Binary:                paths.Binary,
   200  			Limited:               paths.Limited,
   201  		},
   202  	}, nil
   203  }
   204  
   205  func (b *backendGRPCPluginServer) HandleExistenceCheck(ctx context.Context, args *pb.HandleExistenceCheckArgs) (*pb.HandleExistenceCheckReply, error) {
   206  	backend, brokeredClient, err := b.getBackendAndBrokeredClient(ctx)
   207  	if err != nil {
   208  		return &pb.HandleExistenceCheckReply{}, err
   209  	}
   210  
   211  	if pluginutil.InMetadataMode() {
   212  		return &pb.HandleExistenceCheckReply{}, ErrServerInMetadataMode
   213  	}
   214  
   215  	logicalReq, err := pb.ProtoRequestToLogicalRequest(args.Request)
   216  	if err != nil {
   217  		return &pb.HandleExistenceCheckReply{}, err
   218  	}
   219  
   220  	logicalReq.Storage = newGRPCStorageClient(brokeredClient)
   221  
   222  	checkFound, exists, err := backend.HandleExistenceCheck(ctx, logicalReq)
   223  	return &pb.HandleExistenceCheckReply{
   224  		CheckFound: checkFound,
   225  		Exists:     exists,
   226  		Err:        pb.ErrToProtoErr(err),
   227  	}, nil
   228  }
   229  
   230  func (b *backendGRPCPluginServer) Cleanup(ctx context.Context, _ *pb.Empty) (*pb.Empty, error) {
   231  	b.instancesLock.Lock()
   232  	defer b.instancesLock.Unlock()
   233  
   234  	backend, brokeredClient, err := b.getBackendAndBrokeredClientInternal(ctx)
   235  	if err != nil {
   236  		return &pb.Empty{}, err
   237  	}
   238  
   239  	backend.Cleanup(ctx)
   240  
   241  	// Close rpc clients
   242  	brokeredClient.Close()
   243  
   244  	if b.multiplexingSupport {
   245  		id, err := pluginutil.GetMultiplexIDFromContext(ctx)
   246  		if err != nil {
   247  			return nil, err
   248  		}
   249  		delete(b.instances, id)
   250  	} else if _, ok := b.instances[singleImplementationID]; ok {
   251  		delete(b.instances, singleImplementationID)
   252  	}
   253  
   254  	return &pb.Empty{}, nil
   255  }
   256  
   257  func (b *backendGRPCPluginServer) InvalidateKey(ctx context.Context, args *pb.InvalidateKeyArgs) (*pb.Empty, error) {
   258  	backend, _, err := b.getBackendAndBrokeredClient(ctx)
   259  	if err != nil {
   260  		return &pb.Empty{}, err
   261  	}
   262  
   263  	if pluginutil.InMetadataMode() {
   264  		return &pb.Empty{}, ErrServerInMetadataMode
   265  	}
   266  
   267  	backend.InvalidateKey(ctx, args.Key)
   268  	return &pb.Empty{}, nil
   269  }
   270  
   271  func (b *backendGRPCPluginServer) Type(ctx context.Context, _ *pb.Empty) (*pb.TypeReply, error) {
   272  	backend, _, err := b.getBackendAndBrokeredClient(ctx)
   273  	if err != nil {
   274  		return &pb.TypeReply{}, err
   275  	}
   276  
   277  	return &pb.TypeReply{
   278  		Type: uint32(backend.Type()),
   279  	}, nil
   280  }
   281  
   282  func (b *backendGRPCPluginServer) Version(ctx context.Context, _ *logical.Empty) (*logical.VersionReply, error) {
   283  	backend, _, err := b.getBackendAndBrokeredClient(ctx)
   284  	if err != nil {
   285  		return &logical.VersionReply{}, err
   286  	}
   287  
   288  	if versioner, ok := backend.(logical.PluginVersioner); ok {
   289  		return &logical.VersionReply{
   290  			PluginVersion: versioner.PluginVersion().Version,
   291  		}, nil
   292  	}
   293  	return &logical.VersionReply{
   294  		PluginVersion: "",
   295  	}, nil
   296  }