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  }