[DBPW 5/X] Use AutoMTLS with DB plugins (#10008)
This commit is contained in:
parent
ffcff10151
commit
41d8c89169
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
154
vendor/github.com/hashicorp/vault/sdk/helper/pluginutil/run_config.go
generated
vendored
Normal file
154
vendor/github.com/hashicorp/vault/sdk/helper/pluginutil/run_config.go
generated
vendored
Normal 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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue