github.com/hashicorp/go-plugin@v1.6.0/client.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package plugin
     5  
     6  import (
     7  	"bufio"
     8  	"context"
     9  	"crypto/subtle"
    10  	"crypto/tls"
    11  	"crypto/x509"
    12  	"encoding/base64"
    13  	"errors"
    14  	"fmt"
    15  	"hash"
    16  	"io"
    17  	"io/ioutil"
    18  	"net"
    19  	"os"
    20  	"os/exec"
    21  	"path/filepath"
    22  	"strconv"
    23  	"strings"
    24  	"sync"
    25  	"sync/atomic"
    26  	"time"
    27  
    28  	"github.com/hashicorp/go-hclog"
    29  	"github.com/hashicorp/go-plugin/internal/cmdrunner"
    30  	"github.com/hashicorp/go-plugin/internal/grpcmux"
    31  	"github.com/hashicorp/go-plugin/runner"
    32  	"google.golang.org/grpc"
    33  )
    34  
    35  // If this is 1, then we've called CleanupClients. This can be used
    36  // by plugin RPC implementations to change error behavior since you
    37  // can expected network connection errors at this point. This should be
    38  // read by using sync/atomic.
    39  var Killed uint32 = 0
    40  
    41  // This is a slice of the "managed" clients which are cleaned up when
    42  // calling Cleanup
    43  var managedClients = make([]*Client, 0, 5)
    44  var managedClientsLock sync.Mutex
    45  
    46  // Error types
    47  var (
    48  	// ErrProcessNotFound is returned when a client is instantiated to
    49  	// reattach to an existing process and it isn't found.
    50  	ErrProcessNotFound = cmdrunner.ErrProcessNotFound
    51  
    52  	// ErrChecksumsDoNotMatch is returned when binary's checksum doesn't match
    53  	// the one provided in the SecureConfig.
    54  	ErrChecksumsDoNotMatch = errors.New("checksums did not match")
    55  
    56  	// ErrSecureNoChecksum is returned when an empty checksum is provided to the
    57  	// SecureConfig.
    58  	ErrSecureConfigNoChecksum = errors.New("no checksum provided")
    59  
    60  	// ErrSecureNoHash is returned when a nil Hash object is provided to the
    61  	// SecureConfig.
    62  	ErrSecureConfigNoHash = errors.New("no hash implementation provided")
    63  
    64  	// ErrSecureConfigAndReattach is returned when both Reattach and
    65  	// SecureConfig are set.
    66  	ErrSecureConfigAndReattach = errors.New("only one of Reattach or SecureConfig can be set")
    67  
    68  	// ErrGRPCBrokerMuxNotSupported is returned when the client requests
    69  	// multiplexing over the gRPC broker, but the plugin does not support the
    70  	// feature. In most cases, this should be resolvable by updating and
    71  	// rebuilding the plugin, or restarting the plugin with
    72  	// ClientConfig.GRPCBrokerMultiplex set to false.
    73  	ErrGRPCBrokerMuxNotSupported = errors.New("client requested gRPC broker multiplexing but plugin does not support the feature")
    74  )
    75  
    76  // defaultPluginLogBufferSize is the default size of the buffer used to read from stderr for plugin log lines.
    77  const defaultPluginLogBufferSize = 64 * 1024
    78  
    79  // Client handles the lifecycle of a plugin application. It launches
    80  // plugins, connects to them, dispenses interface implementations, and handles
    81  // killing the process.
    82  //
    83  // Plugin hosts should use one Client for each plugin executable. To
    84  // dispense a plugin type, use the `Client.Client` function, and then
    85  // cal `Dispense`. This awkward API is mostly historical but is used to split
    86  // the client that deals with subprocess management and the client that
    87  // does RPC management.
    88  //
    89  // See NewClient and ClientConfig for using a Client.
    90  type Client struct {
    91  	config            *ClientConfig
    92  	exited            bool
    93  	l                 sync.Mutex
    94  	address           net.Addr
    95  	runner            runner.AttachedRunner
    96  	client            ClientProtocol
    97  	protocol          Protocol
    98  	logger            hclog.Logger
    99  	doneCtx           context.Context
   100  	ctxCancel         context.CancelFunc
   101  	negotiatedVersion int
   102  
   103  	// clientWaitGroup is used to manage the lifecycle of the plugin management
   104  	// goroutines.
   105  	clientWaitGroup sync.WaitGroup
   106  
   107  	// stderrWaitGroup is used to prevent the command's Wait() function from
   108  	// being called before we've finished reading from the stderr pipe.
   109  	stderrWaitGroup sync.WaitGroup
   110  
   111  	// processKilled is used for testing only, to flag when the process was
   112  	// forcefully killed.
   113  	processKilled bool
   114  
   115  	unixSocketCfg UnixSocketConfig
   116  
   117  	grpcMuxerOnce sync.Once
   118  	grpcMuxer     *grpcmux.GRPCClientMuxer
   119  }
   120  
   121  // NegotiatedVersion returns the protocol version negotiated with the server.
   122  // This is only valid after Start() is called.
   123  func (c *Client) NegotiatedVersion() int {
   124  	return c.negotiatedVersion
   125  }
   126  
   127  // ID returns a unique ID for the running plugin. By default this is the process
   128  // ID (pid), but it could take other forms if RunnerFunc was provided.
   129  func (c *Client) ID() string {
   130  	c.l.Lock()
   131  	defer c.l.Unlock()
   132  
   133  	if c.runner != nil {
   134  		return c.runner.ID()
   135  	}
   136  
   137  	return ""
   138  }
   139  
   140  // ClientConfig is the configuration used to initialize a new
   141  // plugin client. After being used to initialize a plugin client,
   142  // that configuration must not be modified again.
   143  type ClientConfig struct {
   144  	// HandshakeConfig is the configuration that must match servers.
   145  	HandshakeConfig
   146  
   147  	// Plugins are the plugins that can be consumed.
   148  	// The implied version of this PluginSet is the Handshake.ProtocolVersion.
   149  	Plugins PluginSet
   150  
   151  	// VersionedPlugins is a map of PluginSets for specific protocol versions.
   152  	// These can be used to negotiate a compatible version between client and
   153  	// server. If this is set, Handshake.ProtocolVersion is not required.
   154  	VersionedPlugins map[int]PluginSet
   155  
   156  	// One of the following must be set, but not both.
   157  	//
   158  	// Cmd is the unstarted subprocess for starting the plugin. If this is
   159  	// set, then the Client starts the plugin process on its own and connects
   160  	// to it.
   161  	//
   162  	// Reattach is configuration for reattaching to an existing plugin process
   163  	// that is already running. This isn't common.
   164  	Cmd      *exec.Cmd
   165  	Reattach *ReattachConfig
   166  
   167  	// RunnerFunc allows consumers to provide their own implementation of
   168  	// runner.Runner and control the context within which a plugin is executed.
   169  	// The cmd argument will have been copied from the config and populated with
   170  	// environment variables that a go-plugin server expects to read such as
   171  	// AutoMTLS certs and the magic cookie key.
   172  	RunnerFunc func(l hclog.Logger, cmd *exec.Cmd, tmpDir string) (runner.Runner, error)
   173  
   174  	// SecureConfig is configuration for verifying the integrity of the
   175  	// executable. It can not be used with Reattach.
   176  	SecureConfig *SecureConfig
   177  
   178  	// TLSConfig is used to enable TLS on the RPC client.
   179  	TLSConfig *tls.Config
   180  
   181  	// Managed represents if the client should be managed by the
   182  	// plugin package or not. If true, then by calling CleanupClients,
   183  	// it will automatically be cleaned up. Otherwise, the client
   184  	// user is fully responsible for making sure to Kill all plugin
   185  	// clients. By default the client is _not_ managed.
   186  	Managed bool
   187  
   188  	// The minimum and maximum port to use for communicating with
   189  	// the subprocess. If not set, this defaults to 10,000 and 25,000
   190  	// respectively.
   191  	MinPort, MaxPort uint
   192  
   193  	// StartTimeout is the timeout to wait for the plugin to say it
   194  	// has started successfully.
   195  	StartTimeout time.Duration
   196  
   197  	// If non-nil, then the stderr of the client will be written to here
   198  	// (as well as the log). This is the original os.Stderr of the subprocess.
   199  	// This isn't the output of synced stderr.
   200  	Stderr io.Writer
   201  
   202  	// SyncStdout, SyncStderr can be set to override the
   203  	// respective os.Std* values in the plugin. Care should be taken to
   204  	// avoid races here. If these are nil, then this will be set to
   205  	// ioutil.Discard.
   206  	SyncStdout io.Writer
   207  	SyncStderr io.Writer
   208  
   209  	// AllowedProtocols is a list of allowed protocols. If this isn't set,
   210  	// then only netrpc is allowed. This is so that older go-plugin systems
   211  	// can show friendly errors if they see a plugin with an unknown
   212  	// protocol.
   213  	//
   214  	// By setting this, you can cause an error immediately on plugin start
   215  	// if an unsupported protocol is used with a good error message.
   216  	//
   217  	// If this isn't set at all (nil value), then only net/rpc is accepted.
   218  	// This is done for legacy reasons. You must explicitly opt-in to
   219  	// new protocols.
   220  	AllowedProtocols []Protocol
   221  
   222  	// Logger is the logger that the client will used. If none is provided,
   223  	// it will default to hclog's default logger.
   224  	Logger hclog.Logger
   225  
   226  	// PluginLogBufferSize is the buffer size(bytes) to read from stderr for plugin log lines.
   227  	// If this is 0, then the default of 64KB is used.
   228  	PluginLogBufferSize int
   229  
   230  	// AutoMTLS has the client and server automatically negotiate mTLS for
   231  	// transport authentication. This ensures that only the original client will
   232  	// be allowed to connect to the server, and all other connections will be
   233  	// rejected. The client will also refuse to connect to any server that isn't
   234  	// the original instance started by the client.
   235  	//
   236  	// In this mode of operation, the client generates a one-time use tls
   237  	// certificate, sends the public x.509 certificate to the new server, and
   238  	// the server generates a one-time use tls certificate, and sends the public
   239  	// x.509 certificate back to the client. These are used to authenticate all
   240  	// rpc connections between the client and server.
   241  	//
   242  	// Setting AutoMTLS to true implies that the server must support the
   243  	// protocol, and correctly negotiate the tls certificates, or a connection
   244  	// failure will result.
   245  	//
   246  	// The client should not set TLSConfig, nor should the server set a
   247  	// TLSProvider, because AutoMTLS implies that a new certificate and tls
   248  	// configuration will be generated at startup.
   249  	//
   250  	// You cannot Reattach to a server with this option enabled.
   251  	AutoMTLS bool
   252  
   253  	// GRPCDialOptions allows plugin users to pass custom grpc.DialOption
   254  	// to create gRPC connections. This only affects plugins using the gRPC
   255  	// protocol.
   256  	GRPCDialOptions []grpc.DialOption
   257  
   258  	// GRPCBrokerMultiplex turns on multiplexing for the gRPC broker. The gRPC
   259  	// broker will multiplex all brokered gRPC servers over the plugin's original
   260  	// listener socket instead of making a new listener for each server. The
   261  	// go-plugin library currently only includes a Go implementation for the
   262  	// server (i.e. plugin) side of gRPC broker multiplexing.
   263  	//
   264  	// Does not support reattaching.
   265  	//
   266  	// Multiplexed gRPC streams MUST be established sequentially, i.e. after
   267  	// calling AcceptAndServe from one side, wait for the other side to Dial
   268  	// before calling AcceptAndServe again.
   269  	GRPCBrokerMultiplex bool
   270  
   271  	// SkipHostEnv allows plugins to run without inheriting the parent process'
   272  	// environment variables.
   273  	SkipHostEnv bool
   274  
   275  	// UnixSocketConfig configures additional options for any Unix sockets
   276  	// that are created. Not normally required. Not supported on Windows.
   277  	UnixSocketConfig *UnixSocketConfig
   278  }
   279  
   280  type UnixSocketConfig struct {
   281  	// If set, go-plugin will change the owner of any Unix sockets created to
   282  	// this group, and set them as group-writable. Can be a name or gid. The
   283  	// client process must be a member of this group or chown will fail.
   284  	Group string
   285  
   286  	// TempDir specifies the base directory to use when creating a plugin-specific
   287  	// temporary directory. It is expected to already exist and be writable. If
   288  	// not set, defaults to the directory chosen by os.MkdirTemp.
   289  	TempDir string
   290  
   291  	// The directory to create Unix sockets in. Internally created and managed
   292  	// by go-plugin and deleted when the plugin is killed. Will be created
   293  	// inside TempDir if specified.
   294  	socketDir string
   295  }
   296  
   297  // ReattachConfig is used to configure a client to reattach to an
   298  // already-running plugin process. You can retrieve this information by
   299  // calling ReattachConfig on Client.
   300  type ReattachConfig struct {
   301  	Protocol        Protocol
   302  	ProtocolVersion int
   303  	Addr            net.Addr
   304  	Pid             int
   305  
   306  	// ReattachFunc allows consumers to provide their own implementation of
   307  	// runner.AttachedRunner and attach to something other than a plain process.
   308  	// At least one of Pid or ReattachFunc must be set.
   309  	ReattachFunc runner.ReattachFunc
   310  
   311  	// Test is set to true if this is reattaching to to a plugin in "test mode"
   312  	// (see ServeConfig.Test). In this mode, client.Kill will NOT kill the
   313  	// process and instead will rely on the plugin to terminate itself. This
   314  	// should not be used in non-test environments.
   315  	Test bool
   316  }
   317  
   318  // SecureConfig is used to configure a client to verify the integrity of an
   319  // executable before running. It does this by verifying the checksum is
   320  // expected. Hash is used to specify the hashing method to use when checksumming
   321  // the file.  The configuration is verified by the client by calling the
   322  // SecureConfig.Check() function.
   323  //
   324  // The host process should ensure the checksum was provided by a trusted and
   325  // authoritative source. The binary should be installed in such a way that it
   326  // can not be modified by an unauthorized user between the time of this check
   327  // and the time of execution.
   328  type SecureConfig struct {
   329  	Checksum []byte
   330  	Hash     hash.Hash
   331  }
   332  
   333  // Check takes the filepath to an executable and returns true if the checksum of
   334  // the file matches the checksum provided in the SecureConfig.
   335  func (s *SecureConfig) Check(filePath string) (bool, error) {
   336  	if len(s.Checksum) == 0 {
   337  		return false, ErrSecureConfigNoChecksum
   338  	}
   339  
   340  	if s.Hash == nil {
   341  		return false, ErrSecureConfigNoHash
   342  	}
   343  
   344  	file, err := os.Open(filePath)
   345  	if err != nil {
   346  		return false, err
   347  	}
   348  	defer file.Close()
   349  
   350  	_, err = io.Copy(s.Hash, file)
   351  	if err != nil {
   352  		return false, err
   353  	}
   354  
   355  	sum := s.Hash.Sum(nil)
   356  
   357  	return subtle.ConstantTimeCompare(sum, s.Checksum) == 1, nil
   358  }
   359  
   360  // This makes sure all the managed subprocesses are killed and properly
   361  // logged. This should be called before the parent process running the
   362  // plugins exits.
   363  //
   364  // This must only be called _once_.
   365  func CleanupClients() {
   366  	// Set the killed to true so that we don't get unexpected panics
   367  	atomic.StoreUint32(&Killed, 1)
   368  
   369  	// Kill all the managed clients in parallel and use a WaitGroup
   370  	// to wait for them all to finish up.
   371  	var wg sync.WaitGroup
   372  	managedClientsLock.Lock()
   373  	for _, client := range managedClients {
   374  		wg.Add(1)
   375  
   376  		go func(client *Client) {
   377  			client.Kill()
   378  			wg.Done()
   379  		}(client)
   380  	}
   381  	managedClientsLock.Unlock()
   382  
   383  	wg.Wait()
   384  }
   385  
   386  // NewClient creates a new plugin client which manages the lifecycle of an external
   387  // plugin and gets the address for the RPC connection.
   388  //
   389  // The client must be cleaned up at some point by calling Kill(). If
   390  // the client is a managed client (created with ClientConfig.Managed) you
   391  // can just call CleanupClients at the end of your program and they will
   392  // be properly cleaned.
   393  func NewClient(config *ClientConfig) (c *Client) {
   394  	if config.MinPort == 0 && config.MaxPort == 0 {
   395  		config.MinPort = 10000
   396  		config.MaxPort = 25000
   397  	}
   398  
   399  	if config.StartTimeout == 0 {
   400  		config.StartTimeout = 1 * time.Minute
   401  	}
   402  
   403  	if config.Stderr == nil {
   404  		config.Stderr = ioutil.Discard
   405  	}
   406  
   407  	if config.SyncStdout == nil {
   408  		config.SyncStdout = io.Discard
   409  	}
   410  	if config.SyncStderr == nil {
   411  		config.SyncStderr = io.Discard
   412  	}
   413  
   414  	if config.AllowedProtocols == nil {
   415  		config.AllowedProtocols = []Protocol{ProtocolNetRPC}
   416  	}
   417  
   418  	if config.Logger == nil {
   419  		config.Logger = hclog.New(&hclog.LoggerOptions{
   420  			Output: hclog.DefaultOutput,
   421  			Level:  hclog.Trace,
   422  			Name:   "plugin",
   423  		})
   424  	}
   425  
   426  	if config.PluginLogBufferSize == 0 {
   427  		config.PluginLogBufferSize = defaultPluginLogBufferSize
   428  	}
   429  
   430  	c = &Client{
   431  		config: config,
   432  		logger: config.Logger,
   433  	}
   434  	if config.Managed {
   435  		managedClientsLock.Lock()
   436  		managedClients = append(managedClients, c)
   437  		managedClientsLock.Unlock()
   438  	}
   439  
   440  	return
   441  }
   442  
   443  // Client returns the protocol client for this connection.
   444  //
   445  // Subsequent calls to this will return the same client.
   446  func (c *Client) Client() (ClientProtocol, error) {
   447  	_, err := c.Start()
   448  	if err != nil {
   449  		return nil, err
   450  	}
   451  
   452  	c.l.Lock()
   453  	defer c.l.Unlock()
   454  
   455  	if c.client != nil {
   456  		return c.client, nil
   457  	}
   458  
   459  	switch c.protocol {
   460  	case ProtocolNetRPC:
   461  		c.client, err = newRPCClient(c)
   462  
   463  	case ProtocolGRPC:
   464  		c.client, err = newGRPCClient(c.doneCtx, c)
   465  
   466  	default:
   467  		return nil, fmt.Errorf("unknown server protocol: %s", c.protocol)
   468  	}
   469  
   470  	if err != nil {
   471  		c.client = nil
   472  		return nil, err
   473  	}
   474  
   475  	return c.client, nil
   476  }
   477  
   478  // Tells whether or not the underlying process has exited.
   479  func (c *Client) Exited() bool {
   480  	c.l.Lock()
   481  	defer c.l.Unlock()
   482  	return c.exited
   483  }
   484  
   485  // killed is used in tests to check if a process failed to exit gracefully, and
   486  // needed to be killed.
   487  func (c *Client) killed() bool {
   488  	c.l.Lock()
   489  	defer c.l.Unlock()
   490  	return c.processKilled
   491  }
   492  
   493  // End the executing subprocess (if it is running) and perform any cleanup
   494  // tasks necessary such as capturing any remaining logs and so on.
   495  //
   496  // This method blocks until the process successfully exits.
   497  //
   498  // This method can safely be called multiple times.
   499  func (c *Client) Kill() {
   500  	// Grab a lock to read some private fields.
   501  	c.l.Lock()
   502  	runner := c.runner
   503  	addr := c.address
   504  	hostSocketDir := c.unixSocketCfg.socketDir
   505  	c.l.Unlock()
   506  
   507  	// If there is no runner or ID, there is nothing to kill.
   508  	if runner == nil || runner.ID() == "" {
   509  		return
   510  	}
   511  
   512  	defer func() {
   513  		// Wait for the all client goroutines to finish.
   514  		c.clientWaitGroup.Wait()
   515  
   516  		if hostSocketDir != "" {
   517  			os.RemoveAll(hostSocketDir)
   518  		}
   519  
   520  		// Make sure there is no reference to the old process after it has been
   521  		// killed.
   522  		c.l.Lock()
   523  		c.runner = nil
   524  		c.l.Unlock()
   525  	}()
   526  
   527  	// We need to check for address here. It is possible that the plugin
   528  	// started (process != nil) but has no address (addr == nil) if the
   529  	// plugin failed at startup. If we do have an address, we need to close
   530  	// the plugin net connections.
   531  	graceful := false
   532  	if addr != nil {
   533  		// Close the client to cleanly exit the process.
   534  		client, err := c.Client()
   535  		if err == nil {
   536  			err = client.Close()
   537  
   538  			// If there is no error, then we attempt to wait for a graceful
   539  			// exit. If there was an error, we assume that graceful cleanup
   540  			// won't happen and just force kill.
   541  			graceful = err == nil
   542  			if err != nil {
   543  				// If there was an error just log it. We're going to force
   544  				// kill in a moment anyways.
   545  				c.logger.Warn("error closing client during Kill", "err", err)
   546  			}
   547  		} else {
   548  			c.logger.Error("client", "error", err)
   549  		}
   550  	}
   551  
   552  	// If we're attempting a graceful exit, then we wait for a short period
   553  	// of time to allow that to happen. To wait for this we just wait on the
   554  	// doneCh which would be closed if the process exits.
   555  	if graceful {
   556  		select {
   557  		case <-c.doneCtx.Done():
   558  			c.logger.Debug("plugin exited")
   559  			return
   560  		case <-time.After(2 * time.Second):
   561  		}
   562  	}
   563  
   564  	// If graceful exiting failed, just kill it
   565  	c.logger.Warn("plugin failed to exit gracefully")
   566  	if err := runner.Kill(context.Background()); err != nil {
   567  		c.logger.Debug("error killing plugin", "error", err)
   568  	}
   569  
   570  	c.l.Lock()
   571  	c.processKilled = true
   572  	c.l.Unlock()
   573  }
   574  
   575  // Start the underlying subprocess, communicating with it to negotiate
   576  // a port for RPC connections, and returning the address to connect via RPC.
   577  //
   578  // This method is safe to call multiple times. Subsequent calls have no effect.
   579  // Once a client has been started once, it cannot be started again, even if
   580  // it was killed.
   581  func (c *Client) Start() (addr net.Addr, err error) {
   582  	c.l.Lock()
   583  	defer c.l.Unlock()
   584  
   585  	if c.address != nil {
   586  		return c.address, nil
   587  	}
   588  
   589  	// If one of cmd or reattach isn't set, then it is an error. We wrap
   590  	// this in a {} for scoping reasons, and hopeful that the escape
   591  	// analysis will pop the stack here.
   592  	{
   593  		var mutuallyExclusiveOptions int
   594  		if c.config.Cmd != nil {
   595  			mutuallyExclusiveOptions += 1
   596  		}
   597  		if c.config.Reattach != nil {
   598  			mutuallyExclusiveOptions += 1
   599  		}
   600  		if c.config.RunnerFunc != nil {
   601  			mutuallyExclusiveOptions += 1
   602  		}
   603  		if mutuallyExclusiveOptions != 1 {
   604  			return nil, fmt.Errorf("exactly one of Cmd, or Reattach, or RunnerFunc must be set")
   605  		}
   606  
   607  		if c.config.SecureConfig != nil && c.config.Reattach != nil {
   608  			return nil, ErrSecureConfigAndReattach
   609  		}
   610  
   611  		if c.config.GRPCBrokerMultiplex && c.config.Reattach != nil {
   612  			return nil, fmt.Errorf("gRPC broker multiplexing is not supported with Reattach config")
   613  		}
   614  	}
   615  
   616  	if c.config.Reattach != nil {
   617  		return c.reattach()
   618  	}
   619  
   620  	if c.config.VersionedPlugins == nil {
   621  		c.config.VersionedPlugins = make(map[int]PluginSet)
   622  	}
   623  
   624  	// handle all plugins as versioned, using the handshake config as the default.
   625  	version := int(c.config.ProtocolVersion)
   626  
   627  	// Make sure we're not overwriting a real version 0. If ProtocolVersion was
   628  	// non-zero, then we have to just assume the user made sure that
   629  	// VersionedPlugins doesn't conflict.
   630  	if _, ok := c.config.VersionedPlugins[version]; !ok && c.config.Plugins != nil {
   631  		c.config.VersionedPlugins[version] = c.config.Plugins
   632  	}
   633  
   634  	var versionStrings []string
   635  	for v := range c.config.VersionedPlugins {
   636  		versionStrings = append(versionStrings, strconv.Itoa(v))
   637  	}
   638  
   639  	env := []string{
   640  		fmt.Sprintf("%s=%s", c.config.MagicCookieKey, c.config.MagicCookieValue),
   641  		fmt.Sprintf("PLUGIN_MIN_PORT=%d", c.config.MinPort),
   642  		fmt.Sprintf("PLUGIN_MAX_PORT=%d", c.config.MaxPort),
   643  		fmt.Sprintf("PLUGIN_PROTOCOL_VERSIONS=%s", strings.Join(versionStrings, ",")),
   644  	}
   645  	if c.config.GRPCBrokerMultiplex {
   646  		env = append(env, fmt.Sprintf("%s=true", envMultiplexGRPC))
   647  	}
   648  
   649  	cmd := c.config.Cmd
   650  	if cmd == nil {
   651  		// It's only possible to get here if RunnerFunc is non-nil, but we'll
   652  		// still use cmd as a spec to populate metadata for the external
   653  		// implementation to consume.
   654  		cmd = exec.Command("")
   655  	}
   656  	if !c.config.SkipHostEnv {
   657  		cmd.Env = append(cmd.Env, os.Environ()...)
   658  	}
   659  	cmd.Env = append(cmd.Env, env...)
   660  	cmd.Stdin = os.Stdin
   661  
   662  	if c.config.SecureConfig != nil {
   663  		if ok, err := c.config.SecureConfig.Check(cmd.Path); err != nil {
   664  			return nil, fmt.Errorf("error verifying checksum: %s", err)
   665  		} else if !ok {
   666  			return nil, ErrChecksumsDoNotMatch
   667  		}
   668  	}
   669  
   670  	// Setup a temporary certificate for client/server mtls, and send the public
   671  	// certificate to the plugin.
   672  	if c.config.AutoMTLS {
   673  		c.logger.Info("configuring client automatic mTLS")
   674  		certPEM, keyPEM, err := generateCert()
   675  		if err != nil {
   676  			c.logger.Error("failed to generate client certificate", "error", err)
   677  			return nil, err
   678  		}
   679  		cert, err := tls.X509KeyPair(certPEM, keyPEM)
   680  		if err != nil {
   681  			c.logger.Error("failed to parse client certificate", "error", err)
   682  			return nil, err
   683  		}
   684  
   685  		cmd.Env = append(cmd.Env, fmt.Sprintf("PLUGIN_CLIENT_CERT=%s", certPEM))
   686  
   687  		c.config.TLSConfig = &tls.Config{
   688  			Certificates: []tls.Certificate{cert},
   689  			ClientAuth:   tls.RequireAndVerifyClientCert,
   690  			MinVersion:   tls.VersionTLS12,
   691  			ServerName:   "localhost",
   692  		}
   693  	}
   694  
   695  	if c.config.UnixSocketConfig != nil {
   696  		c.unixSocketCfg = *c.config.UnixSocketConfig
   697  	}
   698  
   699  	if c.unixSocketCfg.Group != "" {
   700  		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", EnvUnixSocketGroup, c.unixSocketCfg.Group))
   701  	}
   702  
   703  	var runner runner.Runner
   704  	switch {
   705  	case c.config.RunnerFunc != nil:
   706  		c.unixSocketCfg.socketDir, err = os.MkdirTemp(c.unixSocketCfg.TempDir, "plugin-dir")
   707  		if err != nil {
   708  			return nil, err
   709  		}
   710  		// os.MkdirTemp creates folders with 0o700, so if we have a group
   711  		// configured we need to make it group-writable.
   712  		if c.unixSocketCfg.Group != "" {
   713  			err = setGroupWritable(c.unixSocketCfg.socketDir, c.unixSocketCfg.Group, 0o770)
   714  			if err != nil {
   715  				return nil, err
   716  			}
   717  		}
   718  		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", EnvUnixSocketDir, c.unixSocketCfg.socketDir))
   719  		c.logger.Trace("created temporary directory for unix sockets", "dir", c.unixSocketCfg.socketDir)
   720  
   721  		runner, err = c.config.RunnerFunc(c.logger, cmd, c.unixSocketCfg.socketDir)
   722  		if err != nil {
   723  			return nil, err
   724  		}
   725  	default:
   726  		runner, err = cmdrunner.NewCmdRunner(c.logger, cmd)
   727  		if err != nil {
   728  			return nil, err
   729  		}
   730  
   731  	}
   732  
   733  	c.runner = runner
   734  	startCtx, startCtxCancel := context.WithTimeout(context.Background(), c.config.StartTimeout)
   735  	defer startCtxCancel()
   736  	err = runner.Start(startCtx)
   737  	if err != nil {
   738  		return nil, err
   739  	}
   740  
   741  	// Make sure the command is properly cleaned up if there is an error
   742  	defer func() {
   743  		rErr := recover()
   744  
   745  		if err != nil || rErr != nil {
   746  			runner.Kill(context.Background())
   747  		}
   748  
   749  		if rErr != nil {
   750  			panic(rErr)
   751  		}
   752  	}()
   753  
   754  	// Create a context for when we kill
   755  	c.doneCtx, c.ctxCancel = context.WithCancel(context.Background())
   756  
   757  	// Start goroutine that logs the stderr
   758  	c.clientWaitGroup.Add(1)
   759  	c.stderrWaitGroup.Add(1)
   760  	// logStderr calls Done()
   761  	go c.logStderr(runner.Name(), runner.Stderr())
   762  
   763  	c.clientWaitGroup.Add(1)
   764  	go func() {
   765  		// ensure the context is cancelled when we're done
   766  		defer c.ctxCancel()
   767  
   768  		defer c.clientWaitGroup.Done()
   769  
   770  		// wait to finish reading from stderr since the stderr pipe reader
   771  		// will be closed by the subsequent call to cmd.Wait().
   772  		c.stderrWaitGroup.Wait()
   773  
   774  		// Wait for the command to end.
   775  		err := runner.Wait(context.Background())
   776  		if err != nil {
   777  			c.logger.Error("plugin process exited", "plugin", runner.Name(), "id", runner.ID(), "error", err.Error())
   778  		} else {
   779  			// Log and make sure to flush the logs right away
   780  			c.logger.Info("plugin process exited", "plugin", runner.Name(), "id", runner.ID())
   781  		}
   782  
   783  		os.Stderr.Sync()
   784  
   785  		// Set that we exited, which takes a lock
   786  		c.l.Lock()
   787  		defer c.l.Unlock()
   788  		c.exited = true
   789  	}()
   790  
   791  	// Start a goroutine that is going to be reading the lines
   792  	// out of stdout
   793  	linesCh := make(chan string)
   794  	c.clientWaitGroup.Add(1)
   795  	go func() {
   796  		defer c.clientWaitGroup.Done()
   797  		defer close(linesCh)
   798  
   799  		scanner := bufio.NewScanner(runner.Stdout())
   800  		for scanner.Scan() {
   801  			linesCh <- scanner.Text()
   802  		}
   803  		if scanner.Err() != nil {
   804  			c.logger.Error("error encountered while scanning stdout", "error", scanner.Err())
   805  		}
   806  	}()
   807  
   808  	// Make sure after we exit we read the lines from stdout forever
   809  	// so they don't block since it is a pipe.
   810  	// The scanner goroutine above will close this, but track it with a wait
   811  	// group for completeness.
   812  	c.clientWaitGroup.Add(1)
   813  	defer func() {
   814  		go func() {
   815  			defer c.clientWaitGroup.Done()
   816  			for range linesCh {
   817  			}
   818  		}()
   819  	}()
   820  
   821  	// Some channels for the next step
   822  	timeout := time.After(c.config.StartTimeout)
   823  
   824  	// Start looking for the address
   825  	c.logger.Debug("waiting for RPC address", "plugin", runner.Name())
   826  	select {
   827  	case <-timeout:
   828  		err = errors.New("timeout while waiting for plugin to start")
   829  	case <-c.doneCtx.Done():
   830  		err = errors.New("plugin exited before we could connect")
   831  	case line, ok := <-linesCh:
   832  		// Trim the line and split by "|" in order to get the parts of
   833  		// the output.
   834  		line = strings.TrimSpace(line)
   835  		parts := strings.Split(line, "|")
   836  		if len(parts) < 4 {
   837  			errText := fmt.Sprintf("Unrecognized remote plugin message: %s", line)
   838  			if !ok {
   839  				errText += "\n" + "Failed to read any lines from plugin's stdout"
   840  			}
   841  			additionalNotes := runner.Diagnose(context.Background())
   842  			if additionalNotes != "" {
   843  				errText += "\n" + additionalNotes
   844  			}
   845  			err = errors.New(errText)
   846  			return
   847  		}
   848  
   849  		// Check the core protocol. Wrapped in a {} for scoping.
   850  		{
   851  			var coreProtocol int
   852  			coreProtocol, err = strconv.Atoi(parts[0])
   853  			if err != nil {
   854  				err = fmt.Errorf("Error parsing core protocol version: %s", err)
   855  				return
   856  			}
   857  
   858  			if coreProtocol != CoreProtocolVersion {
   859  				err = fmt.Errorf("Incompatible core API version with plugin. "+
   860  					"Plugin version: %s, Core version: %d\n\n"+
   861  					"To fix this, the plugin usually only needs to be recompiled.\n"+
   862  					"Please report this to the plugin author.", parts[0], CoreProtocolVersion)
   863  				return
   864  			}
   865  		}
   866  
   867  		// Test the API version
   868  		version, pluginSet, err := c.checkProtoVersion(parts[1])
   869  		if err != nil {
   870  			return addr, err
   871  		}
   872  
   873  		// set the Plugins value to the compatible set, so the version
   874  		// doesn't need to be passed through to the ClientProtocol
   875  		// implementation.
   876  		c.config.Plugins = pluginSet
   877  		c.negotiatedVersion = version
   878  		c.logger.Debug("using plugin", "version", version)
   879  
   880  		network, address, err := runner.PluginToHost(parts[2], parts[3])
   881  		if err != nil {
   882  			return addr, err
   883  		}
   884  
   885  		switch network {
   886  		case "tcp":
   887  			addr, err = net.ResolveTCPAddr("tcp", address)
   888  		case "unix":
   889  			addr, err = net.ResolveUnixAddr("unix", address)
   890  		default:
   891  			err = fmt.Errorf("Unknown address type: %s", address)
   892  		}
   893  
   894  		// If we have a server type, then record that. We default to net/rpc
   895  		// for backwards compatibility.
   896  		c.protocol = ProtocolNetRPC
   897  		if len(parts) >= 5 {
   898  			c.protocol = Protocol(parts[4])
   899  		}
   900  
   901  		found := false
   902  		for _, p := range c.config.AllowedProtocols {
   903  			if p == c.protocol {
   904  				found = true
   905  				break
   906  			}
   907  		}
   908  		if !found {
   909  			err = fmt.Errorf("Unsupported plugin protocol %q. Supported: %v",
   910  				c.protocol, c.config.AllowedProtocols)
   911  			return addr, err
   912  		}
   913  
   914  		// See if we have a TLS certificate from the server.
   915  		// Checking if the length is > 50 rules out catching the unused "extra"
   916  		// data returned from some older implementations.
   917  		if len(parts) >= 6 && len(parts[5]) > 50 {
   918  			err := c.loadServerCert(parts[5])
   919  			if err != nil {
   920  				return nil, fmt.Errorf("error parsing server cert: %s", err)
   921  			}
   922  		}
   923  
   924  		if c.config.GRPCBrokerMultiplex && c.protocol == ProtocolGRPC {
   925  			if len(parts) <= 6 {
   926  				return nil, fmt.Errorf("%w; for Go plugins, you will need to update the "+
   927  					"github.com/hashicorp/go-plugin dependency and recompile", ErrGRPCBrokerMuxNotSupported)
   928  			}
   929  			if muxSupported, err := strconv.ParseBool(parts[6]); err != nil {
   930  				return nil, fmt.Errorf("error parsing %q as a boolean for gRPC broker multiplexing support", parts[6])
   931  			} else if !muxSupported {
   932  				return nil, ErrGRPCBrokerMuxNotSupported
   933  			}
   934  		}
   935  	}
   936  
   937  	c.address = addr
   938  	return
   939  }
   940  
   941  // loadServerCert is used by AutoMTLS to read an x.509 cert returned by the
   942  // server, and load it as the RootCA and ClientCA for the client TLSConfig.
   943  func (c *Client) loadServerCert(cert string) error {
   944  	certPool := x509.NewCertPool()
   945  
   946  	asn1, err := base64.RawStdEncoding.DecodeString(cert)
   947  	if err != nil {
   948  		return err
   949  	}
   950  
   951  	x509Cert, err := x509.ParseCertificate([]byte(asn1))
   952  	if err != nil {
   953  		return err
   954  	}
   955  
   956  	certPool.AddCert(x509Cert)
   957  
   958  	c.config.TLSConfig.RootCAs = certPool
   959  	c.config.TLSConfig.ClientCAs = certPool
   960  	return nil
   961  }
   962  
   963  func (c *Client) reattach() (net.Addr, error) {
   964  	reattachFunc := c.config.Reattach.ReattachFunc
   965  	// For backwards compatibility default to cmdrunner.ReattachFunc
   966  	if reattachFunc == nil {
   967  		reattachFunc = cmdrunner.ReattachFunc(c.config.Reattach.Pid, c.config.Reattach.Addr)
   968  	}
   969  
   970  	r, err := reattachFunc()
   971  	if err != nil {
   972  		return nil, err
   973  	}
   974  
   975  	// Create a context for when we kill
   976  	c.doneCtx, c.ctxCancel = context.WithCancel(context.Background())
   977  
   978  	c.clientWaitGroup.Add(1)
   979  	// Goroutine to mark exit status
   980  	go func(r runner.AttachedRunner) {
   981  		defer c.clientWaitGroup.Done()
   982  
   983  		// ensure the context is cancelled when we're done
   984  		defer c.ctxCancel()
   985  
   986  		// Wait for the process to die
   987  		r.Wait(context.Background())
   988  
   989  		// Log so we can see it
   990  		c.logger.Debug("reattached plugin process exited")
   991  
   992  		// Mark it
   993  		c.l.Lock()
   994  		defer c.l.Unlock()
   995  		c.exited = true
   996  	}(r)
   997  
   998  	// Set the address and protocol
   999  	c.address = c.config.Reattach.Addr
  1000  	c.protocol = c.config.Reattach.Protocol
  1001  	if c.protocol == "" {
  1002  		// Default the protocol to net/rpc for backwards compatibility
  1003  		c.protocol = ProtocolNetRPC
  1004  	}
  1005  
  1006  	if c.config.Reattach.Test {
  1007  		c.negotiatedVersion = c.config.Reattach.ProtocolVersion
  1008  	} else {
  1009  		// If we're in test mode, we do NOT set the runner. This avoids the
  1010  		// runner being killed (the only purpose we have for setting c.runner
  1011  		// when reattaching), since in test mode the process is responsible for
  1012  		// exiting on its own.
  1013  		c.runner = r
  1014  	}
  1015  
  1016  	return c.address, nil
  1017  }
  1018  
  1019  // checkProtoVersion returns the negotiated version and PluginSet.
  1020  // This returns an error if the server returned an incompatible protocol
  1021  // version, or an invalid handshake response.
  1022  func (c *Client) checkProtoVersion(protoVersion string) (int, PluginSet, error) {
  1023  	serverVersion, err := strconv.Atoi(protoVersion)
  1024  	if err != nil {
  1025  		return 0, nil, fmt.Errorf("Error parsing protocol version %q: %s", protoVersion, err)
  1026  	}
  1027  
  1028  	// record these for the error message
  1029  	var clientVersions []int
  1030  
  1031  	// all versions, including the legacy ProtocolVersion have been added to
  1032  	// the versions set
  1033  	for version, plugins := range c.config.VersionedPlugins {
  1034  		clientVersions = append(clientVersions, version)
  1035  
  1036  		if serverVersion != version {
  1037  			continue
  1038  		}
  1039  		return version, plugins, nil
  1040  	}
  1041  
  1042  	return 0, nil, fmt.Errorf("Incompatible API version with plugin. "+
  1043  		"Plugin version: %d, Client versions: %d", serverVersion, clientVersions)
  1044  }
  1045  
  1046  // ReattachConfig returns the information that must be provided to NewClient
  1047  // to reattach to the plugin process that this client started. This is
  1048  // useful for plugins that detach from their parent process.
  1049  //
  1050  // If this returns nil then the process hasn't been started yet. Please
  1051  // call Start or Client before calling this.
  1052  //
  1053  // Clients who specified a RunnerFunc will need to populate their own
  1054  // ReattachFunc in the returned ReattachConfig before it can be used.
  1055  func (c *Client) ReattachConfig() *ReattachConfig {
  1056  	c.l.Lock()
  1057  	defer c.l.Unlock()
  1058  
  1059  	if c.address == nil {
  1060  		return nil
  1061  	}
  1062  
  1063  	if c.config.Cmd != nil && c.config.Cmd.Process == nil {
  1064  		return nil
  1065  	}
  1066  
  1067  	// If we connected via reattach, just return the information as-is
  1068  	if c.config.Reattach != nil {
  1069  		return c.config.Reattach
  1070  	}
  1071  
  1072  	reattach := &ReattachConfig{
  1073  		Protocol: c.protocol,
  1074  		Addr:     c.address,
  1075  	}
  1076  
  1077  	if c.config.Cmd != nil && c.config.Cmd.Process != nil {
  1078  		reattach.Pid = c.config.Cmd.Process.Pid
  1079  	}
  1080  
  1081  	return reattach
  1082  }
  1083  
  1084  // Protocol returns the protocol of server on the remote end. This will
  1085  // start the plugin process if it isn't already started. Errors from
  1086  // starting the plugin are surpressed and ProtocolInvalid is returned. It
  1087  // is recommended you call Start explicitly before calling Protocol to ensure
  1088  // no errors occur.
  1089  func (c *Client) Protocol() Protocol {
  1090  	_, err := c.Start()
  1091  	if err != nil {
  1092  		return ProtocolInvalid
  1093  	}
  1094  
  1095  	return c.protocol
  1096  }
  1097  
  1098  func netAddrDialer(addr net.Addr) func(string, time.Duration) (net.Conn, error) {
  1099  	return func(_ string, _ time.Duration) (net.Conn, error) {
  1100  		// Connect to the client
  1101  		conn, err := net.Dial(addr.Network(), addr.String())
  1102  		if err != nil {
  1103  			return nil, err
  1104  		}
  1105  		if tcpConn, ok := conn.(*net.TCPConn); ok {
  1106  			// Make sure to set keep alive so that the connection doesn't die
  1107  			tcpConn.SetKeepAlive(true)
  1108  		}
  1109  
  1110  		return conn, nil
  1111  	}
  1112  }
  1113  
  1114  // dialer is compatible with grpc.WithDialer and creates the connection
  1115  // to the plugin.
  1116  func (c *Client) dialer(_ string, timeout time.Duration) (net.Conn, error) {
  1117  	muxer, err := c.getGRPCMuxer(c.address)
  1118  	if err != nil {
  1119  		return nil, err
  1120  	}
  1121  
  1122  	var conn net.Conn
  1123  	if muxer.Enabled() {
  1124  		conn, err = muxer.Dial()
  1125  		if err != nil {
  1126  			return nil, err
  1127  		}
  1128  	} else {
  1129  		conn, err = netAddrDialer(c.address)("", timeout)
  1130  		if err != nil {
  1131  			return nil, err
  1132  		}
  1133  	}
  1134  
  1135  	// If we have a TLS config we wrap our connection. We only do this
  1136  	// for net/rpc since gRPC uses its own mechanism for TLS.
  1137  	if c.protocol == ProtocolNetRPC && c.config.TLSConfig != nil {
  1138  		conn = tls.Client(conn, c.config.TLSConfig)
  1139  	}
  1140  
  1141  	return conn, nil
  1142  }
  1143  
  1144  func (c *Client) getGRPCMuxer(addr net.Addr) (*grpcmux.GRPCClientMuxer, error) {
  1145  	if c.protocol != ProtocolGRPC || !c.config.GRPCBrokerMultiplex {
  1146  		return nil, nil
  1147  	}
  1148  
  1149  	var err error
  1150  	c.grpcMuxerOnce.Do(func() {
  1151  		c.grpcMuxer, err = grpcmux.NewGRPCClientMuxer(c.logger, addr)
  1152  	})
  1153  	if err != nil {
  1154  		return nil, err
  1155  	}
  1156  
  1157  	return c.grpcMuxer, nil
  1158  }
  1159  
  1160  func (c *Client) logStderr(name string, r io.Reader) {
  1161  	defer c.clientWaitGroup.Done()
  1162  	defer c.stderrWaitGroup.Done()
  1163  	l := c.logger.Named(filepath.Base(name))
  1164  
  1165  	reader := bufio.NewReaderSize(r, c.config.PluginLogBufferSize)
  1166  	// continuation indicates the previous line was a prefix
  1167  	continuation := false
  1168  
  1169  	for {
  1170  		line, isPrefix, err := reader.ReadLine()
  1171  		switch {
  1172  		case err == io.EOF:
  1173  			return
  1174  		case err != nil:
  1175  			l.Error("reading plugin stderr", "error", err)
  1176  			return
  1177  		}
  1178  
  1179  		c.config.Stderr.Write(line)
  1180  
  1181  		// The line was longer than our max token size, so it's likely
  1182  		// incomplete and won't unmarshal.
  1183  		if isPrefix || continuation {
  1184  			l.Debug(string(line))
  1185  
  1186  			// if we're finishing a continued line, add the newline back in
  1187  			if !isPrefix {
  1188  				c.config.Stderr.Write([]byte{'\n'})
  1189  			}
  1190  
  1191  			continuation = isPrefix
  1192  			continue
  1193  		}
  1194  
  1195  		c.config.Stderr.Write([]byte{'\n'})
  1196  
  1197  		entry, err := parseJSON(line)
  1198  		// If output is not JSON format, print directly to Debug
  1199  		if err != nil {
  1200  			// Attempt to infer the desired log level from the commonly used
  1201  			// string prefixes
  1202  			switch line := string(line); {
  1203  			case strings.HasPrefix(line, "[TRACE]"):
  1204  				l.Trace(line)
  1205  			case strings.HasPrefix(line, "[DEBUG]"):
  1206  				l.Debug(line)
  1207  			case strings.HasPrefix(line, "[INFO]"):
  1208  				l.Info(line)
  1209  			case strings.HasPrefix(line, "[WARN]"):
  1210  				l.Warn(line)
  1211  			case strings.HasPrefix(line, "[ERROR]"):
  1212  				l.Error(line)
  1213  			default:
  1214  				l.Debug(line)
  1215  			}
  1216  		} else {
  1217  			out := flattenKVPairs(entry.KVPairs)
  1218  
  1219  			out = append(out, "timestamp", entry.Timestamp.Format(hclog.TimeFormat))
  1220  			switch hclog.LevelFromString(entry.Level) {
  1221  			case hclog.Trace:
  1222  				l.Trace(entry.Message, out...)
  1223  			case hclog.Debug:
  1224  				l.Debug(entry.Message, out...)
  1225  			case hclog.Info:
  1226  				l.Info(entry.Message, out...)
  1227  			case hclog.Warn:
  1228  				l.Warn(entry.Message, out...)
  1229  			case hclog.Error:
  1230  				l.Error(entry.Message, out...)
  1231  			default:
  1232  				// if there was no log level, it's likely this is unexpected
  1233  				// json from something other than hclog, and we should output
  1234  				// it verbatim.
  1235  				l.Debug(string(line))
  1236  			}
  1237  		}
  1238  	}
  1239  }