github.com/hashicorp/vault/sdk@v0.11.0/helper/pluginutil/runner.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package pluginutil 5 6 import ( 7 "context" 8 "errors" 9 "strings" 10 "time" 11 12 log "github.com/hashicorp/go-hclog" 13 "github.com/hashicorp/go-plugin" 14 "github.com/hashicorp/go-version" 15 "github.com/hashicorp/vault/sdk/helper/consts" 16 prutil "github.com/hashicorp/vault/sdk/helper/pluginruntimeutil" 17 "github.com/hashicorp/vault/sdk/helper/wrapping" 18 "google.golang.org/grpc" 19 ) 20 21 // ErrPluginNotFound is returned when a plugin does not have a pinned version. 22 var ErrPinnedVersionNotFound = errors.New("pinned version not found") 23 24 // Looker defines the plugin Lookup function that looks into the plugin catalog 25 // for available plugins and returns a PluginRunner 26 type Looker interface { 27 LookupPlugin(ctx context.Context, pluginName string, pluginType consts.PluginType) (*PluginRunner, error) 28 LookupPluginVersion(ctx context.Context, pluginName string, pluginType consts.PluginType, version string) (*PluginRunner, error) 29 } 30 31 // RunnerUtil interface defines the functions needed by the runner to wrap the 32 // metadata needed to run a plugin process. This includes looking up Mlock 33 // configuration and wrapping data in a response wrapped token. 34 // logical.SystemView implementations satisfy this interface. 35 type RunnerUtil interface { 36 NewPluginClient(ctx context.Context, config PluginClientConfig) (PluginClient, error) 37 ResponseWrapData(ctx context.Context, data map[string]interface{}, ttl time.Duration, jwt bool) (*wrapping.ResponseWrapInfo, error) 38 MlockEnabled() bool 39 VaultVersion(ctx context.Context) (string, error) 40 ClusterID(ctx context.Context) (string, error) 41 } 42 43 // LookRunnerUtil defines the functions for both Looker and Wrapper 44 type LookRunnerUtil interface { 45 Looker 46 RunnerUtil 47 } 48 49 type PluginClient interface { 50 Conn() grpc.ClientConnInterface 51 Reload() error 52 plugin.ClientProtocol 53 } 54 55 const MultiplexingCtxKey string = "multiplex_id" 56 57 // PluginRunner defines the metadata needed to run a plugin securely with 58 // go-plugin. 59 type PluginRunner struct { 60 Name string `json:"name" structs:"name"` 61 Type consts.PluginType `json:"type" structs:"type"` 62 Version string `json:"version" structs:"version"` 63 OCIImage string `json:"oci_image" structs:"oci_image"` 64 Runtime string `json:"runtime" structs:"runtime"` 65 Command string `json:"command" structs:"command"` 66 Args []string `json:"args" structs:"args"` 67 Env []string `json:"env" structs:"env"` 68 Sha256 []byte `json:"sha256" structs:"sha256"` 69 Builtin bool `json:"builtin" structs:"builtin"` 70 BuiltinFactory func() (interface{}, error) `json:"-" structs:"-"` 71 RuntimeConfig *prutil.PluginRuntimeConfig `json:"-" structs:"-"` 72 Tmpdir string `json:"-" structs:"-"` 73 } 74 75 // BinaryReference returns either the OCI image reference if it's a container 76 // plugin or the path to the binary if it's a plain process plugin. 77 func (p *PluginRunner) BinaryReference() string { 78 if p.Builtin { 79 return "" 80 } 81 if p.OCIImage == "" { 82 return p.Command 83 } 84 85 imageRef := p.OCIImage 86 if p.Version != "" { 87 imageRef += ":" + strings.TrimPrefix(p.Version, "v") 88 } 89 90 return imageRef 91 } 92 93 // SetPluginInput is only used as input for the plugin catalog's set methods. 94 // We don't use the very similar PluginRunner struct to avoid confusion about 95 // what's settable, which does not include the builtin fields. 96 type SetPluginInput struct { 97 Name string 98 Type consts.PluginType 99 Version string 100 Command string 101 OCIImage string 102 Runtime string 103 Args []string 104 Env []string 105 Sha256 []byte 106 } 107 108 // Run takes a wrapper RunnerUtil instance along with the go-plugin parameters and 109 // returns a configured plugin.Client with TLS Configured and a wrapping token set 110 // on PluginUnwrapTokenEnv for plugin process consumption. 111 func (r *PluginRunner) Run(ctx context.Context, wrapper RunnerUtil, pluginSets map[int]plugin.PluginSet, hs plugin.HandshakeConfig, env []string, logger log.Logger) (*plugin.Client, error) { 112 return r.RunConfig(ctx, 113 Runner(wrapper), 114 PluginSets(pluginSets), 115 HandshakeConfig(hs), 116 Env(env...), 117 Logger(logger), 118 MetadataMode(false), 119 ) 120 } 121 122 // RunMetadataMode returns a configured plugin.Client that will dispense a plugin 123 // in metadata mode. The PluginMetadataModeEnv is passed in as part of the Cmd to 124 // plugin.Client, and consumed by the plugin process on api.VaultPluginTLSProvider. 125 func (r *PluginRunner) RunMetadataMode(ctx context.Context, wrapper RunnerUtil, pluginSets map[int]plugin.PluginSet, hs plugin.HandshakeConfig, env []string, logger log.Logger) (*plugin.Client, error) { 126 return r.RunConfig(ctx, 127 Runner(wrapper), 128 PluginSets(pluginSets), 129 HandshakeConfig(hs), 130 Env(env...), 131 Logger(logger), 132 MetadataMode(true), 133 ) 134 } 135 136 // VersionedPlugin holds any versioning information stored about a plugin in the 137 // plugin catalog. 138 type VersionedPlugin struct { 139 Type string `json:"type"` // string instead of consts.PluginType so that we get the string form in API responses. 140 Name string `json:"name"` 141 Version string `json:"version"` 142 OCIImage string `json:"oci_image,omitempty"` 143 Runtime string `json:"runtime,omitempty"` 144 SHA256 string `json:"sha256,omitempty"` 145 Builtin bool `json:"builtin"` 146 DeprecationStatus string `json:"deprecation_status,omitempty"` 147 148 // Pre-parsed semver struct of the Version field 149 SemanticVersion *version.Version `json:"-"` 150 } 151 152 type PinnedVersion struct { 153 Name string `json:"name"` 154 Type consts.PluginType `json:"type"` 155 Version string `json:"version"` 156 } 157 158 // CtxCancelIfCanceled takes a context cancel func and a context. If the context is 159 // shutdown the cancelfunc is called. This is useful for merging two cancel 160 // functions. 161 func CtxCancelIfCanceled(f context.CancelFunc, ctxCanceler context.Context) chan struct{} { 162 quitCh := make(chan struct{}) 163 go func() { 164 select { 165 case <-quitCh: 166 case <-ctxCanceler.Done(): 167 f() 168 } 169 }() 170 return quitCh 171 }