[DBPW 5/X] Use AutoMTLS with DB plugins (#10008)

This commit is contained in:
Michael Golowka 2020-09-23 16:08:03 -06:00 committed by GitHub
parent ffcff10151
commit 41d8c89169
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 715 additions and 182 deletions

View File

@ -6,7 +6,6 @@ import (
"time"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/database/newdbplugin"
)
@ -26,13 +25,13 @@ func New() (interface{}, error) {
}
// Run instantiates a MongoDB object, and runs the RPC server for the plugin
func RunV5(apiTLSConfig *api.TLSConfig) error {
func RunV5() error {
dbType, err := New()
if err != nil {
return err
}
newdbplugin.Serve(dbType.(newdbplugin.Database), api.VaultPluginTLSProvider(apiTLSConfig))
newdbplugin.Serve(dbType.(newdbplugin.Database))
return nil
}

View File

@ -265,7 +265,7 @@ func TestBackend_PluginMain_MockV5(t *testing.T) {
flags := apiClientMeta.FlagSet()
flags.Parse(args)
RunV5(apiClientMeta.GetTLSConfig())
RunV5()
}
func assertNoRespData(t *testing.T, resp *logical.Response) {

View File

@ -39,13 +39,14 @@ func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunne
},
}
var client *plugin.Client
var err error
if isMetadataMode {
client, err = pluginRunner.RunMetadataMode(ctx, sys, pluginSets, handshakeConfig, []string{}, logger)
} else {
client, err = pluginRunner.Run(ctx, sys, pluginSets, handshakeConfig, []string{}, logger)
}
client, err := pluginRunner.RunConfig(ctx,
pluginutil.Runner(sys),
pluginutil.PluginSets(pluginSets),
pluginutil.HandshakeConfig(handshakeConfig),
pluginutil.Logger(logger),
pluginutil.MetadataMode(isMetadataMode),
pluginutil.AutoMTLS(true),
)
if err != nil {
return nil, err
}

View File

@ -1,7 +1,6 @@
package newdbplugin
import (
"crypto/tls"
"fmt"
"github.com/hashicorp/go-plugin"
@ -11,11 +10,11 @@ import (
// Serve is called from within a plugin and wraps the provided
// Database implementation in a databasePluginRPCServer object and starts a
// RPC server.
func Serve(db Database, tlsProvider func() (*tls.Config, error)) {
plugin.Serve(ServeConfig(db, tlsProvider))
func Serve(db Database) {
plugin.Serve(ServeConfig(db))
}
func ServeConfig(db Database, tlsProvider func() (*tls.Config, error)) *plugin.ServeConfig {
func ServeConfig(db Database) *plugin.ServeConfig {
err := pluginutil.OptionallyEnableMlock()
if err != nil {
fmt.Println(err)
@ -34,7 +33,6 @@ func ServeConfig(db Database, tlsProvider func() (*tls.Config, error)) *plugin.S
conf := &plugin.ServeConfig{
HandshakeConfig: handshakeConfig,
VersionedPlugins: pluginSets,
TLSProvider: tlsProvider,
GRPCServer: plugin.DefaultGRPCServer,
}

View File

@ -0,0 +1,161 @@
package pluginutil
import (
"context"
"crypto/sha256"
"crypto/tls"
"fmt"
"os/exec"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/sdk/version"
)
type runConfig struct {
// Provided by PluginRunner
command string
args []string
sha256 []byte
// Initialized with what's in PluginRunner.Env, but can be added to
env []string
wrapper RunnerUtil
pluginSets map[int]plugin.PluginSet
hs plugin.HandshakeConfig
logger log.Logger
isMetadataMode bool
autoMTLS bool
}
func (rc runConfig) makeConfig(ctx context.Context) (*plugin.ClientConfig, error) {
cmd := exec.Command(rc.command, rc.args...)
cmd.Env = append(cmd.Env, rc.env...)
// Add the mlock setting to the ENV of the plugin
if rc.wrapper != nil && rc.wrapper.MlockEnabled() {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginMlockEnabled, "true"))
}
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version))
if rc.isMetadataMode {
rc.logger = rc.logger.With("metadata", "true")
}
metadataEnv := fmt.Sprintf("%s=%t", PluginMetadataModeEnv, rc.isMetadataMode)
cmd.Env = append(cmd.Env, metadataEnv)
var clientTLSConfig *tls.Config
if !rc.autoMTLS && !rc.isMetadataMode {
// Get a CA TLS Certificate
certBytes, key, err := generateCert()
if err != nil {
return nil, err
}
// Use CA to sign a client cert and return a configured TLS config
clientTLSConfig, err = createClientTLSConfig(certBytes, key)
if err != nil {
return nil, err
}
// Use CA to sign a server cert and wrap the values in a response wrapped
// token.
wrapToken, err := wrapServerConfig(ctx, rc.wrapper, certBytes, key)
if err != nil {
return nil, err
}
// Add the response wrap token to the ENV of the plugin
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginUnwrapTokenEnv, wrapToken))
}
secureConfig := &plugin.SecureConfig{
Checksum: rc.sha256,
Hash: sha256.New(),
}
clientConfig := &plugin.ClientConfig{
HandshakeConfig: rc.hs,
VersionedPlugins: rc.pluginSets,
Cmd: cmd,
SecureConfig: secureConfig,
TLSConfig: clientTLSConfig,
Logger: rc.logger,
AllowedProtocols: []plugin.Protocol{
plugin.ProtocolNetRPC,
plugin.ProtocolGRPC,
},
AutoMTLS: rc.autoMTLS,
}
return clientConfig, nil
}
func (rc runConfig) run(ctx context.Context) (*plugin.Client, error) {
clientConfig, err := rc.makeConfig(ctx)
if err != nil {
return nil, err
}
client := plugin.NewClient(clientConfig)
return client, nil
}
type RunOpt func(*runConfig)
func Env(env ...string) RunOpt {
return func(rc *runConfig) {
rc.env = append(rc.env, env...)
}
}
func Runner(wrapper RunnerUtil) RunOpt {
return func(rc *runConfig) {
rc.wrapper = wrapper
}
}
func PluginSets(pluginSets map[int]plugin.PluginSet) RunOpt {
return func(rc *runConfig) {
rc.pluginSets = pluginSets
}
}
func HandshakeConfig(hs plugin.HandshakeConfig) RunOpt {
return func(rc *runConfig) {
rc.hs = hs
}
}
func Logger(logger log.Logger) RunOpt {
return func(rc *runConfig) {
rc.logger = logger
}
}
func MetadataMode(isMetadataMode bool) RunOpt {
return func(rc *runConfig) {
rc.isMetadataMode = isMetadataMode
}
}
func AutoMTLS(autoMTLS bool) RunOpt {
return func(rc *runConfig) {
rc.autoMTLS = autoMTLS
}
}
func (r *PluginRunner) RunConfig(ctx context.Context, opts ...RunOpt) (*plugin.Client, error) {
rc := runConfig{
command: r.Command,
args: r.Args,
sha256: r.Sha256,
env: r.Env,
}
for _, opt := range opts {
opt(&rc)
}
return rc.run(ctx)
}

View File

@ -0,0 +1,341 @@
package pluginutil
import (
"context"
"fmt"
"os/exec"
"reflect"
"testing"
"time"
"github.com/hashicorp/vault/sdk/version"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/sdk/helper/wrapping"
"github.com/stretchr/testify/mock"
)
func TestNameMakeConfig(t *testing.T) {
type testCase struct {
rc runConfig
responseWrapInfo *wrapping.ResponseWrapInfo
responseWrapInfoErr error
responseWrapInfoTimes int
mlockEnabled bool
mlockEnabledTimes int
expectedConfig *plugin.ClientConfig
expectTLSConfig bool
}
tests := map[string]testCase{
"metadata mode, not-AutoMTLS": {
rc: runConfig{
command: "echo",
args: []string{"foo", "bar"},
sha256: []byte("some_sha256"),
env: []string{"initial=true"},
pluginSets: map[int]plugin.PluginSet{
1: plugin.PluginSet{
"bogus": nil,
},
},
hs: plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "magic_cookie_key",
MagicCookieValue: "magic_cookie_value",
},
logger: hclog.NewNullLogger(),
isMetadataMode: true,
autoMTLS: false,
},
responseWrapInfoTimes: 0,
mlockEnabled: false,
mlockEnabledTimes: 1,
expectedConfig: &plugin.ClientConfig{
HandshakeConfig: plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "magic_cookie_key",
MagicCookieValue: "magic_cookie_value",
},
VersionedPlugins: map[int]plugin.PluginSet{
1: plugin.PluginSet{
"bogus": nil,
},
},
Cmd: commandWithEnv(
"echo",
[]string{"foo", "bar"},
[]string{
"initial=true",
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, true),
},
),
SecureConfig: &plugin.SecureConfig{
Checksum: []byte("some_sha256"),
// Hash is generated
},
AllowedProtocols: []plugin.Protocol{
plugin.ProtocolNetRPC,
plugin.ProtocolGRPC,
},
Logger: hclog.NewNullLogger(),
AutoMTLS: false,
},
expectTLSConfig: false,
},
"non-metadata mode, not-AutoMTLS": {
rc: runConfig{
command: "echo",
args: []string{"foo", "bar"},
sha256: []byte("some_sha256"),
env: []string{"initial=true"},
pluginSets: map[int]plugin.PluginSet{
1: plugin.PluginSet{
"bogus": nil,
},
},
hs: plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "magic_cookie_key",
MagicCookieValue: "magic_cookie_value",
},
logger: hclog.NewNullLogger(),
isMetadataMode: false,
autoMTLS: false,
},
responseWrapInfo: &wrapping.ResponseWrapInfo{
Token: "testtoken",
},
responseWrapInfoTimes: 1,
mlockEnabled: true,
mlockEnabledTimes: 1,
expectedConfig: &plugin.ClientConfig{
HandshakeConfig: plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "magic_cookie_key",
MagicCookieValue: "magic_cookie_value",
},
VersionedPlugins: map[int]plugin.PluginSet{
1: plugin.PluginSet{
"bogus": nil,
},
},
Cmd: commandWithEnv(
"echo",
[]string{"foo", "bar"},
[]string{
"initial=true",
fmt.Sprintf("%s=%t", PluginMlockEnabled, true),
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, false),
fmt.Sprintf("%s=%s", PluginUnwrapTokenEnv, "testtoken"),
},
),
SecureConfig: &plugin.SecureConfig{
Checksum: []byte("some_sha256"),
// Hash is generated
},
AllowedProtocols: []plugin.Protocol{
plugin.ProtocolNetRPC,
plugin.ProtocolGRPC,
},
Logger: hclog.NewNullLogger(),
AutoMTLS: false,
},
expectTLSConfig: true,
},
"metadata mode, AutoMTLS": {
rc: runConfig{
command: "echo",
args: []string{"foo", "bar"},
sha256: []byte("some_sha256"),
env: []string{"initial=true"},
pluginSets: map[int]plugin.PluginSet{
1: plugin.PluginSet{
"bogus": nil,
},
},
hs: plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "magic_cookie_key",
MagicCookieValue: "magic_cookie_value",
},
logger: hclog.NewNullLogger(),
isMetadataMode: true,
autoMTLS: true,
},
responseWrapInfoTimes: 0,
mlockEnabled: false,
mlockEnabledTimes: 1,
expectedConfig: &plugin.ClientConfig{
HandshakeConfig: plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "magic_cookie_key",
MagicCookieValue: "magic_cookie_value",
},
VersionedPlugins: map[int]plugin.PluginSet{
1: plugin.PluginSet{
"bogus": nil,
},
},
Cmd: commandWithEnv(
"echo",
[]string{"foo", "bar"},
[]string{
"initial=true",
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, true),
},
),
SecureConfig: &plugin.SecureConfig{
Checksum: []byte("some_sha256"),
// Hash is generated
},
AllowedProtocols: []plugin.Protocol{
plugin.ProtocolNetRPC,
plugin.ProtocolGRPC,
},
Logger: hclog.NewNullLogger(),
AutoMTLS: true,
},
expectTLSConfig: false,
},
"not-metadata mode, AutoMTLS": {
rc: runConfig{
command: "echo",
args: []string{"foo", "bar"},
sha256: []byte("some_sha256"),
env: []string{"initial=true"},
pluginSets: map[int]plugin.PluginSet{
1: plugin.PluginSet{
"bogus": nil,
},
},
hs: plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "magic_cookie_key",
MagicCookieValue: "magic_cookie_value",
},
logger: hclog.NewNullLogger(),
isMetadataMode: false,
autoMTLS: true,
},
responseWrapInfoTimes: 0,
mlockEnabled: false,
mlockEnabledTimes: 1,
expectedConfig: &plugin.ClientConfig{
HandshakeConfig: plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "magic_cookie_key",
MagicCookieValue: "magic_cookie_value",
},
VersionedPlugins: map[int]plugin.PluginSet{
1: plugin.PluginSet{
"bogus": nil,
},
},
Cmd: commandWithEnv(
"echo",
[]string{"foo", "bar"},
[]string{
"initial=true",
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, false),
},
),
SecureConfig: &plugin.SecureConfig{
Checksum: []byte("some_sha256"),
// Hash is generated
},
AllowedProtocols: []plugin.Protocol{
plugin.ProtocolNetRPC,
plugin.ProtocolGRPC,
},
Logger: hclog.NewNullLogger(),
AutoMTLS: true,
},
expectTLSConfig: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
mockWrapper := new(mockRunnerUtil)
mockWrapper.On("ResponseWrapData", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
Return(test.responseWrapInfo, test.responseWrapInfoErr)
mockWrapper.On("MlockEnabled").
Return(test.mlockEnabled)
test.rc.wrapper = mockWrapper
defer mockWrapper.AssertNumberOfCalls(t, "ResponseWrapData", test.responseWrapInfoTimes)
defer mockWrapper.AssertNumberOfCalls(t, "MlockEnabled", test.mlockEnabledTimes)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
config, err := test.rc.makeConfig(ctx)
if err != nil {
t.Fatalf("no error expected, got: %s", err)
}
// TODO: Certain fields will need to be checked for existence, not specific value
// The following fields are generated, so we just need to check for existence, not specific value
// The value must be nilled out before performing a DeepEqual check
hsh := config.SecureConfig.Hash
if hsh == nil {
t.Fatalf("Missing SecureConfig.Hash")
}
config.SecureConfig.Hash = nil
if test.expectTLSConfig && config.TLSConfig == nil {
t.Fatalf("TLS config expected, got nil")
}
if !test.expectTLSConfig && config.TLSConfig != nil {
t.Fatalf("no TLS config expected, got: %#v", config.TLSConfig)
}
config.TLSConfig = nil
if !reflect.DeepEqual(config, test.expectedConfig) {
t.Fatalf("Actual config: %#v\nExpected config: %#v", config, test.expectedConfig)
}
})
}
}
func commandWithEnv(cmd string, args []string, env []string) *exec.Cmd {
c := exec.Command(cmd, args...)
c.Env = env
return c
}
var _ RunnerUtil = &mockRunnerUtil{}
type mockRunnerUtil struct {
mock.Mock
}
func (m *mockRunnerUtil) ResponseWrapData(ctx context.Context, data map[string]interface{}, ttl time.Duration, jwt bool) (*wrapping.ResponseWrapInfo, error) {
args := m.Called(ctx, data, ttl, jwt)
return args.Get(0).(*wrapping.ResponseWrapInfo), args.Error(1)
}
func (m *mockRunnerUtil) MlockEnabled() bool {
args := m.Called()
return args.Bool(0)
}

View File

@ -2,17 +2,12 @@ package pluginutil
import (
"context"
"crypto/sha256"
"crypto/tls"
"fmt"
"os/exec"
"time"
log "github.com/hashicorp/go-hclog"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/wrapping"
"github.com/hashicorp/vault/sdk/version"
)
// Looker defines the plugin Lookup function that looks into the plugin catalog
@ -53,83 +48,28 @@ type PluginRunner struct {
// returns a configured plugin.Client with TLS Configured and a wrapping token set
// on PluginUnwrapTokenEnv for plugin process consumption.
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) {
return r.runCommon(ctx, wrapper, pluginSets, hs, env, logger, false)
return r.RunConfig(ctx,
Runner(wrapper),
PluginSets(pluginSets),
HandshakeConfig(hs),
Env(env...),
Logger(logger),
MetadataMode(false),
)
}
// RunMetadataMode returns a configured plugin.Client that will dispense a plugin
// in metadata mode. The PluginMetadataModeEnv is passed in as part of the Cmd to
// plugin.Client, and consumed by the plugin process on api.VaultPluginTLSProvider.
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) {
return r.runCommon(ctx, wrapper, pluginSets, hs, env, logger, true)
}
func (r *PluginRunner) runCommon(ctx context.Context, wrapper RunnerUtil, pluginSets map[int]plugin.PluginSet, hs plugin.HandshakeConfig, env []string, logger log.Logger, isMetadataMode bool) (*plugin.Client, error) {
cmd := exec.Command(r.Command, r.Args...)
// `env` should always go last to avoid overwriting internal values that might
// have been provided externally.
cmd.Env = append(cmd.Env, r.Env...)
cmd.Env = append(cmd.Env, env...)
// Add the mlock setting to the ENV of the plugin
if wrapper != nil && wrapper.MlockEnabled() {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginMlockEnabled, "true"))
}
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version))
var clientTLSConfig *tls.Config
if !isMetadataMode {
// Add the metadata mode ENV and set it to false
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginMetadataModeEnv, "false"))
// Get a CA TLS Certificate
certBytes, key, err := generateCert()
if err != nil {
return nil, err
}
// Use CA to sign a client cert and return a configured TLS config
clientTLSConfig, err = createClientTLSConfig(certBytes, key)
if err != nil {
return nil, err
}
// Use CA to sign a server cert and wrap the values in a response wrapped
// token.
wrapToken, err := wrapServerConfig(ctx, wrapper, certBytes, key)
if err != nil {
return nil, err
}
// Add the response wrap token to the ENV of the plugin
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginUnwrapTokenEnv, wrapToken))
} else {
logger = logger.With("metadata", "true")
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginMetadataModeEnv, "true"))
}
secureConfig := &plugin.SecureConfig{
Checksum: r.Sha256,
Hash: sha256.New(),
}
clientConfig := &plugin.ClientConfig{
HandshakeConfig: hs,
VersionedPlugins: pluginSets,
Cmd: cmd,
SecureConfig: secureConfig,
TLSConfig: clientTLSConfig,
Logger: logger,
AllowedProtocols: []plugin.Protocol{
plugin.ProtocolNetRPC,
plugin.ProtocolGRPC,
},
}
client := plugin.NewClient(clientConfig)
return client, nil
return r.RunConfig(ctx,
Runner(wrapper),
PluginSets(pluginSets),
HandshakeConfig(hs),
Env(env...),
Logger(logger),
MetadataMode(true),
)
}
// CtxCancelIfCanceled takes a context cancel func and a context. If the context is

View File

@ -39,13 +39,14 @@ func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunne
},
}
var client *plugin.Client
var err error
if isMetadataMode {
client, err = pluginRunner.RunMetadataMode(ctx, sys, pluginSets, handshakeConfig, []string{}, logger)
} else {
client, err = pluginRunner.Run(ctx, sys, pluginSets, handshakeConfig, []string{}, logger)
}
client, err := pluginRunner.RunConfig(ctx,
pluginutil.Runner(sys),
pluginutil.PluginSets(pluginSets),
pluginutil.HandshakeConfig(handshakeConfig),
pluginutil.Logger(logger),
pluginutil.MetadataMode(isMetadataMode),
pluginutil.AutoMTLS(true),
)
if err != nil {
return nil, err
}

View File

@ -1,7 +1,6 @@
package newdbplugin
import (
"crypto/tls"
"fmt"
"github.com/hashicorp/go-plugin"
@ -11,11 +10,11 @@ import (
// Serve is called from within a plugin and wraps the provided
// Database implementation in a databasePluginRPCServer object and starts a
// RPC server.
func Serve(db Database, tlsProvider func() (*tls.Config, error)) {
plugin.Serve(ServeConfig(db, tlsProvider))
func Serve(db Database) {
plugin.Serve(ServeConfig(db))
}
func ServeConfig(db Database, tlsProvider func() (*tls.Config, error)) *plugin.ServeConfig {
func ServeConfig(db Database) *plugin.ServeConfig {
err := pluginutil.OptionallyEnableMlock()
if err != nil {
fmt.Println(err)
@ -34,7 +33,6 @@ func ServeConfig(db Database, tlsProvider func() (*tls.Config, error)) *plugin.S
conf := &plugin.ServeConfig{
HandshakeConfig: handshakeConfig,
VersionedPlugins: pluginSets,
TLSProvider: tlsProvider,
GRPCServer: plugin.DefaultGRPCServer,
}

View File

@ -0,0 +1,154 @@
package pluginutil
import (
"context"
"crypto/sha256"
"crypto/tls"
"fmt"
"os/exec"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/sdk/version"
)
type runConfig struct {
// Provided by PluginRunner
command string
args []string
sha256 []byte
// Initialized with what's in PluginRunner.Env, but can be added to
env []string
wrapper RunnerUtil
pluginSets map[int]plugin.PluginSet
hs plugin.HandshakeConfig
logger log.Logger
isMetadataMode bool
autoMTLS bool
}
func (rc runConfig) run(ctx context.Context) (*plugin.Client, error) {
cmd := exec.Command(rc.command, rc.args...)
cmd.Env = append(cmd.Env, rc.env...)
// Add the mlock setting to the ENV of the plugin
if rc.wrapper != nil && rc.wrapper.MlockEnabled() {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginMlockEnabled, "true"))
}
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version))
var clientTLSConfig *tls.Config
if !rc.isMetadataMode {
// Add the metadata mode ENV and set it to false
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginMetadataModeEnv, "false"))
// Get a CA TLS Certificate
certBytes, key, err := generateCert()
if err != nil {
return nil, err
}
// Use CA to sign a client cert and return a configured TLS config
clientTLSConfig, err = createClientTLSConfig(certBytes, key)
if err != nil {
return nil, err
}
// Use CA to sign a server cert and wrap the values in a response wrapped
// token.
wrapToken, err := wrapServerConfig(ctx, rc.wrapper, certBytes, key)
if err != nil {
return nil, err
}
// Add the response wrap token to the ENV of the plugin
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginUnwrapTokenEnv, wrapToken))
} else {
rc.logger = rc.logger.With("metadata", "true")
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginMetadataModeEnv, "true"))
}
secureConfig := &plugin.SecureConfig{
Checksum: rc.sha256,
Hash: sha256.New(),
}
clientConfig := &plugin.ClientConfig{
HandshakeConfig: rc.hs,
VersionedPlugins: rc.pluginSets,
Cmd: cmd,
SecureConfig: secureConfig,
TLSConfig: clientTLSConfig,
Logger: rc.logger,
AllowedProtocols: []plugin.Protocol{
plugin.ProtocolNetRPC,
plugin.ProtocolGRPC,
},
AutoMTLS: rc.autoMTLS,
}
client := plugin.NewClient(clientConfig)
return client, nil
}
type RunOpt func(*runConfig)
func Env(env ...string) RunOpt {
return func(rc *runConfig) {
rc.env = append(rc.env, env...)
}
}
func Runner(wrapper RunnerUtil) RunOpt {
return func(rc *runConfig) {
rc.wrapper = wrapper
}
}
func PluginSets(pluginSets map[int]plugin.PluginSet) RunOpt {
return func(rc *runConfig) {
rc.pluginSets = pluginSets
}
}
func HandshakeConfig(hs plugin.HandshakeConfig) RunOpt {
return func(rc *runConfig) {
rc.hs = hs
}
}
func Logger(logger log.Logger) RunOpt {
return func(rc *runConfig) {
rc.logger = logger
}
}
func MetadataMode(isMetadataMode bool) RunOpt {
return func(rc *runConfig) {
rc.isMetadataMode = isMetadataMode
}
}
func AutoMTLS(autoMTLS bool) RunOpt {
return func(rc *runConfig) {
rc.autoMTLS = autoMTLS
}
}
func (r *PluginRunner) RunConfig(ctx context.Context, opts ...RunOpt) (*plugin.Client, error) {
rc := runConfig{
command: r.Command,
args: r.Args,
sha256: r.Sha256,
env: r.Env,
}
for _, opt := range opts {
opt(&rc)
}
return rc.run(ctx)
}

View File

@ -2,17 +2,12 @@ package pluginutil
import (
"context"
"crypto/sha256"
"crypto/tls"
"fmt"
"os/exec"
"time"
log "github.com/hashicorp/go-hclog"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/wrapping"
"github.com/hashicorp/vault/sdk/version"
)
// Looker defines the plugin Lookup function that looks into the plugin catalog
@ -53,83 +48,28 @@ type PluginRunner struct {
// returns a configured plugin.Client with TLS Configured and a wrapping token set
// on PluginUnwrapTokenEnv for plugin process consumption.
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) {
return r.runCommon(ctx, wrapper, pluginSets, hs, env, logger, false)
return r.RunConfig(ctx,
Runner(wrapper),
PluginSets(pluginSets),
HandshakeConfig(hs),
Env(env...),
Logger(logger),
MetadataMode(false),
)
}
// RunMetadataMode returns a configured plugin.Client that will dispense a plugin
// in metadata mode. The PluginMetadataModeEnv is passed in as part of the Cmd to
// plugin.Client, and consumed by the plugin process on api.VaultPluginTLSProvider.
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) {
return r.runCommon(ctx, wrapper, pluginSets, hs, env, logger, true)
}
func (r *PluginRunner) runCommon(ctx context.Context, wrapper RunnerUtil, pluginSets map[int]plugin.PluginSet, hs plugin.HandshakeConfig, env []string, logger log.Logger, isMetadataMode bool) (*plugin.Client, error) {
cmd := exec.Command(r.Command, r.Args...)
// `env` should always go last to avoid overwriting internal values that might
// have been provided externally.
cmd.Env = append(cmd.Env, r.Env...)
cmd.Env = append(cmd.Env, env...)
// Add the mlock setting to the ENV of the plugin
if wrapper != nil && wrapper.MlockEnabled() {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginMlockEnabled, "true"))
}
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version))
var clientTLSConfig *tls.Config
if !isMetadataMode {
// Add the metadata mode ENV and set it to false
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginMetadataModeEnv, "false"))
// Get a CA TLS Certificate
certBytes, key, err := generateCert()
if err != nil {
return nil, err
}
// Use CA to sign a client cert and return a configured TLS config
clientTLSConfig, err = createClientTLSConfig(certBytes, key)
if err != nil {
return nil, err
}
// Use CA to sign a server cert and wrap the values in a response wrapped
// token.
wrapToken, err := wrapServerConfig(ctx, wrapper, certBytes, key)
if err != nil {
return nil, err
}
// Add the response wrap token to the ENV of the plugin
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginUnwrapTokenEnv, wrapToken))
} else {
logger = logger.With("metadata", "true")
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginMetadataModeEnv, "true"))
}
secureConfig := &plugin.SecureConfig{
Checksum: r.Sha256,
Hash: sha256.New(),
}
clientConfig := &plugin.ClientConfig{
HandshakeConfig: hs,
VersionedPlugins: pluginSets,
Cmd: cmd,
SecureConfig: secureConfig,
TLSConfig: clientTLSConfig,
Logger: logger,
AllowedProtocols: []plugin.Protocol{
plugin.ProtocolNetRPC,
plugin.ProtocolGRPC,
},
}
client := plugin.NewClient(clientConfig)
return client, nil
return r.RunConfig(ctx,
Runner(wrapper),
PluginSets(pluginSets),
HandshakeConfig(hs),
Env(env...),
Logger(logger),
MetadataMode(true),
)
}
// CtxCancelIfCanceled takes a context cancel func and a context. If the context is

View File

@ -37,7 +37,7 @@ func (fo formatter) passThrough(f fmt.State, c rune) {
s := "%"
for i := 0; i < 128; i++ {
if f.Flag(i) {
s += string(i)
s += string(rune(i))
}
}
if w, ok := f.Width(); ok {

2
vendor/modules.txt vendored
View File

@ -657,7 +657,7 @@ github.com/keybase/go-crypto/openpgp/s2k
github.com/keybase/go-crypto/rsa
# github.com/konsorten/go-windows-terminal-sequences v1.0.3
github.com/konsorten/go-windows-terminal-sequences
# github.com/kr/pretty v0.2.0
# github.com/kr/pretty v0.2.1
github.com/kr/pretty
# github.com/kr/text v0.2.0
github.com/kr/text