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 }