mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-03 12:07:54 +00:00
AutoMTLS for secrets/auth plugins (#15671)
* use automtls for v5 secrets/auth plugins * add automtls env guard * start backend without metadata mode * use PluginClientConfig for backend's NewPluginClient param refactor * - fix pluginutil test - do not expect plugin to be unloaded in UT - fix pluginutil tests --need new env var - use require in UT - fix lazy load test * add changelog * prioritize automtls; improve comments * user multierror; refactor pluginSet for v4 unit test * add test cases for v4 and v5 plugin versions * remove unnecessary call to AutoMTLSSupported * update comment on pluginSets * use runconfig directly in sdk newpluginclient * use automtls without metadatamode for v5 backend plugin registration * use multierror for plugin runconfig calls * remove some unnecessary code
This commit is contained in:
committed by
GitHub
parent
ba56224a2a
commit
39bcd5c715
@@ -17,6 +17,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// PluginAutoMTLSEnv ensures AutoMTLS is used. This overrides setting a
|
||||||
|
// TLSProviderFunc for a plugin.
|
||||||
|
PluginAutoMTLSEnv = "VAULT_PLUGIN_AUTOMTLS"
|
||||||
|
|
||||||
// PluginMetadataModeEnv is an ENV name used to disable TLS communication
|
// PluginMetadataModeEnv is an ENV name used to disable TLS communication
|
||||||
// to bootstrap mounting plugins.
|
// to bootstrap mounting plugins.
|
||||||
PluginMetadataModeEnv = "VAULT_PLUGIN_METADATA_MODE"
|
PluginMetadataModeEnv = "VAULT_PLUGIN_METADATA_MODE"
|
||||||
@@ -120,7 +124,7 @@ func VaultPluginTLSProvider(apiTLSConfig *TLSConfig) func() (*tls.Config, error)
|
|||||||
// VaultPluginTLSProviderContext is run inside a plugin and retrieves the response
|
// VaultPluginTLSProviderContext is run inside a plugin and retrieves the response
|
||||||
// wrapped TLS certificate from vault. It returns a configured TLS Config.
|
// wrapped TLS certificate from vault. It returns a configured TLS Config.
|
||||||
func VaultPluginTLSProviderContext(ctx context.Context, apiTLSConfig *TLSConfig) func() (*tls.Config, error) {
|
func VaultPluginTLSProviderContext(ctx context.Context, apiTLSConfig *TLSConfig) func() (*tls.Config, error) {
|
||||||
if os.Getenv(PluginMetadataModeEnv) == "true" {
|
if os.Getenv(PluginAutoMTLSEnv) == "true" || os.Getenv(PluginMetadataModeEnv) == "true" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
log "github.com/hashicorp/go-hclog"
|
log "github.com/hashicorp/go-hclog"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
uuid "github.com/hashicorp/go-uuid"
|
uuid "github.com/hashicorp/go-uuid"
|
||||||
"github.com/hashicorp/vault/sdk/framework"
|
"github.com/hashicorp/vault/sdk/framework"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
@@ -51,17 +52,32 @@ func Backend(ctx context.Context, conf *logical.BackendConfig) (*PluginBackend,
|
|||||||
|
|
||||||
sys := conf.System
|
sys := conf.System
|
||||||
|
|
||||||
// NewBackend with isMetadataMode set to true
|
merr := &multierror.Error{}
|
||||||
raw, err := bplugin.NewBackend(ctx, name, pluginType, sys, conf, true)
|
// NewBackend with isMetadataMode set to false
|
||||||
|
raw, err := bplugin.NewBackend(ctx, name, pluginType, sys, conf, false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
merr = multierror.Append(merr, err)
|
||||||
|
// NewBackend with isMetadataMode set to true
|
||||||
|
raw, err = bplugin.NewBackend(ctx, name, pluginType, sys, conf, true, false)
|
||||||
|
if err != nil {
|
||||||
|
merr = multierror.Append(merr, err)
|
||||||
|
return nil, merr
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
b.Backend = raw
|
||||||
|
b.config = conf
|
||||||
|
b.loaded = true
|
||||||
|
b.autoMTLSSupported = true
|
||||||
|
|
||||||
|
return &b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the backend so we can inspect the SpecialPaths and Type
|
||||||
err = raw.Setup(ctx, conf)
|
err = raw.Setup(ctx, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
raw.Cleanup(ctx)
|
raw.Cleanup(ctx)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Get SpecialPaths and BackendType
|
|
||||||
paths := raw.SpecialPaths()
|
paths := raw.SpecialPaths()
|
||||||
btype := raw.Type()
|
btype := raw.Type()
|
||||||
|
|
||||||
@@ -85,6 +101,7 @@ type PluginBackend struct {
|
|||||||
Backend logical.Backend
|
Backend logical.Backend
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
|
||||||
|
autoMTLSSupported bool
|
||||||
config *logical.BackendConfig
|
config *logical.BackendConfig
|
||||||
|
|
||||||
// Used to detect if we already reloaded
|
// Used to detect if we already reloaded
|
||||||
@@ -105,7 +122,7 @@ func (b *PluginBackend) startBackend(ctx context.Context, storage logical.Storag
|
|||||||
// Ensure proper cleanup of the backend (i.e. call client.Kill())
|
// Ensure proper cleanup of the backend (i.e. call client.Kill())
|
||||||
b.Backend.Cleanup(ctx)
|
b.Backend.Cleanup(ctx)
|
||||||
|
|
||||||
nb, err := bplugin.NewBackend(ctx, pluginName, pluginType, b.config.System, b.config, false)
|
nb, err := bplugin.NewBackend(ctx, pluginName, pluginType, b.config.System, b.config, false, b.autoMTLSSupported)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func testLazyLoad(t *testing.T, methodWrapper func() error) *PluginBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this is a dummy plugin that hasn't really been loaded yet
|
// this is a dummy plugin that hasn't really been loaded yet
|
||||||
orig, err := plugin.NewBackend(ctx, "test-plugin", consts.PluginTypeSecrets, sysView, config, true)
|
orig, err := plugin.NewBackend(ctx, "test-plugin", consts.PluginTypeSecrets, sysView, config, true, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
3
changelog/15671.txt
Normal file
3
changelog/15671.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:improvement
|
||||||
|
plugins: Use AutoMTLS for secrets engines and auth methods run as external plugins.
|
||||||
|
```
|
||||||
@@ -8,6 +8,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// PluginAutoMTLSEnv is used to ensure AutoMTLS is used. This will override
|
||||||
|
// setting a TLSProviderFunc for a plugin.
|
||||||
|
PluginAutoMTLSEnv = "VAULT_PLUGIN_AUTOMTLS"
|
||||||
|
|
||||||
// PluginMlockEnabled is the ENV name used to pass the configuration for
|
// PluginMlockEnabled is the ENV name used to pass the configuration for
|
||||||
// enabling mlock
|
// enabling mlock
|
||||||
PluginMlockEnabled = "VAULT_PLUGIN_MLOCK_ENABLED"
|
PluginMlockEnabled = "VAULT_PLUGIN_MLOCK_ENABLED"
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import (
|
|||||||
status "google.golang.org/grpc/status"
|
status "google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const MultiplexingCtxKey string = "multiplex_id"
|
||||||
|
|
||||||
type PluginMultiplexingServerImpl struct {
|
type PluginMultiplexingServerImpl struct {
|
||||||
UnimplementedPluginMultiplexingServer
|
UnimplementedPluginMultiplexingServer
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type PluginClientConfig struct {
|
|||||||
IsMetadataMode bool
|
IsMetadataMode bool
|
||||||
AutoMTLS bool
|
AutoMTLS bool
|
||||||
MLock bool
|
MLock bool
|
||||||
|
Wrapper RunnerUtil
|
||||||
}
|
}
|
||||||
|
|
||||||
type runConfig struct {
|
type runConfig struct {
|
||||||
@@ -33,8 +34,6 @@ type runConfig struct {
|
|||||||
// Initialized with what's in PluginRunner.Env, but can be added to
|
// Initialized with what's in PluginRunner.Env, but can be added to
|
||||||
env []string
|
env []string
|
||||||
|
|
||||||
wrapper RunnerUtil
|
|
||||||
|
|
||||||
PluginClientConfig
|
PluginClientConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ func (rc runConfig) makeConfig(ctx context.Context) (*plugin.ClientConfig, error
|
|||||||
cmd.Env = append(cmd.Env, rc.env...)
|
cmd.Env = append(cmd.Env, rc.env...)
|
||||||
|
|
||||||
// Add the mlock setting to the ENV of the plugin
|
// Add the mlock setting to the ENV of the plugin
|
||||||
if rc.MLock || (rc.wrapper != nil && rc.wrapper.MlockEnabled()) {
|
if rc.MLock || (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", PluginMlockEnabled, "true"))
|
||||||
}
|
}
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version))
|
||||||
@@ -54,6 +53,9 @@ func (rc runConfig) makeConfig(ctx context.Context) (*plugin.ClientConfig, error
|
|||||||
metadataEnv := fmt.Sprintf("%s=%t", PluginMetadataModeEnv, rc.IsMetadataMode)
|
metadataEnv := fmt.Sprintf("%s=%t", PluginMetadataModeEnv, rc.IsMetadataMode)
|
||||||
cmd.Env = append(cmd.Env, metadataEnv)
|
cmd.Env = append(cmd.Env, metadataEnv)
|
||||||
|
|
||||||
|
automtlsEnv := fmt.Sprintf("%s=%t", PluginAutoMTLSEnv, rc.AutoMTLS)
|
||||||
|
cmd.Env = append(cmd.Env, automtlsEnv)
|
||||||
|
|
||||||
var clientTLSConfig *tls.Config
|
var clientTLSConfig *tls.Config
|
||||||
if !rc.AutoMTLS && !rc.IsMetadataMode {
|
if !rc.AutoMTLS && !rc.IsMetadataMode {
|
||||||
// Get a CA TLS Certificate
|
// Get a CA TLS Certificate
|
||||||
@@ -70,7 +72,7 @@ func (rc runConfig) makeConfig(ctx context.Context) (*plugin.ClientConfig, error
|
|||||||
|
|
||||||
// Use CA to sign a server cert and wrap the values in a response wrapped
|
// Use CA to sign a server cert and wrap the values in a response wrapped
|
||||||
// token.
|
// token.
|
||||||
wrapToken, err := wrapServerConfig(ctx, rc.wrapper, certBytes, key)
|
wrapToken, err := wrapServerConfig(ctx, rc.Wrapper, certBytes, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -120,7 +122,7 @@ func Env(env ...string) RunOpt {
|
|||||||
|
|
||||||
func Runner(wrapper RunnerUtil) RunOpt {
|
func Runner(wrapper RunnerUtil) RunOpt {
|
||||||
return func(rc *runConfig) {
|
return func(rc *runConfig) {
|
||||||
rc.wrapper = wrapper
|
rc.Wrapper = wrapper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -14,6 +13,7 @@ import (
|
|||||||
"github.com/hashicorp/go-plugin"
|
"github.com/hashicorp/go-plugin"
|
||||||
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMakeConfig(t *testing.T) {
|
func TestMakeConfig(t *testing.T) {
|
||||||
@@ -78,6 +78,7 @@ func TestMakeConfig(t *testing.T) {
|
|||||||
"initial=true",
|
"initial=true",
|
||||||
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
|
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
|
||||||
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, true),
|
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, true),
|
||||||
|
fmt.Sprintf("%s=%t", PluginAutoMTLSEnv, false),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SecureConfig: &plugin.SecureConfig{
|
SecureConfig: &plugin.SecureConfig{
|
||||||
@@ -143,6 +144,7 @@ func TestMakeConfig(t *testing.T) {
|
|||||||
fmt.Sprintf("%s=%t", PluginMlockEnabled, true),
|
fmt.Sprintf("%s=%t", PluginMlockEnabled, true),
|
||||||
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
|
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
|
||||||
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, false),
|
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, false),
|
||||||
|
fmt.Sprintf("%s=%t", PluginAutoMTLSEnv, false),
|
||||||
fmt.Sprintf("%s=%s", PluginUnwrapTokenEnv, "testtoken"),
|
fmt.Sprintf("%s=%s", PluginUnwrapTokenEnv, "testtoken"),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -205,6 +207,7 @@ func TestMakeConfig(t *testing.T) {
|
|||||||
"initial=true",
|
"initial=true",
|
||||||
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
|
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
|
||||||
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, true),
|
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, true),
|
||||||
|
fmt.Sprintf("%s=%t", PluginAutoMTLSEnv, true),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SecureConfig: &plugin.SecureConfig{
|
SecureConfig: &plugin.SecureConfig{
|
||||||
@@ -266,6 +269,7 @@ func TestMakeConfig(t *testing.T) {
|
|||||||
"initial=true",
|
"initial=true",
|
||||||
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
|
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
|
||||||
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, false),
|
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, false),
|
||||||
|
fmt.Sprintf("%s=%t", PluginAutoMTLSEnv, true),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SecureConfig: &plugin.SecureConfig{
|
SecureConfig: &plugin.SecureConfig{
|
||||||
@@ -290,7 +294,7 @@ func TestMakeConfig(t *testing.T) {
|
|||||||
Return(test.responseWrapInfo, test.responseWrapInfoErr)
|
Return(test.responseWrapInfo, test.responseWrapInfoErr)
|
||||||
mockWrapper.On("MlockEnabled").
|
mockWrapper.On("MlockEnabled").
|
||||||
Return(test.mlockEnabled)
|
Return(test.mlockEnabled)
|
||||||
test.rc.wrapper = mockWrapper
|
test.rc.Wrapper = mockWrapper
|
||||||
defer mockWrapper.AssertNumberOfCalls(t, "ResponseWrapData", test.responseWrapInfoTimes)
|
defer mockWrapper.AssertNumberOfCalls(t, "ResponseWrapData", test.responseWrapInfoTimes)
|
||||||
defer mockWrapper.AssertNumberOfCalls(t, "MlockEnabled", test.mlockEnabledTimes)
|
defer mockWrapper.AssertNumberOfCalls(t, "MlockEnabled", test.mlockEnabledTimes)
|
||||||
|
|
||||||
@@ -318,9 +322,7 @@ func TestMakeConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
config.TLSConfig = nil
|
config.TLSConfig = nil
|
||||||
|
|
||||||
if !reflect.DeepEqual(config, test.expectedConfig) {
|
require.Equal(t, config, test.expectedConfig)
|
||||||
t.Fatalf("Actual config: %#v\nExpected config: %#v", config, test.expectedConfig)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,6 @@ type PluginClient interface {
|
|||||||
plugin.ClientProtocol
|
plugin.ClientProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
const MultiplexingCtxKey string = "multiplex_id"
|
|
||||||
|
|
||||||
// PluginRunner defines the metadata needed to run a plugin securely with
|
// PluginRunner defines the metadata needed to run a plugin securely with
|
||||||
// go-plugin.
|
// go-plugin.
|
||||||
type PluginRunner struct {
|
type PluginRunner struct {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ var (
|
|||||||
type GRPCBackendPlugin struct {
|
type GRPCBackendPlugin struct {
|
||||||
Factory logical.Factory
|
Factory logical.Factory
|
||||||
MetadataMode bool
|
MetadataMode bool
|
||||||
|
AutoMTLSSupported bool
|
||||||
Logger log.Logger
|
Logger log.Logger
|
||||||
|
|
||||||
// Embeding this will disable the netRPC protocol
|
// Embeding this will disable the netRPC protocol
|
||||||
@@ -46,7 +47,8 @@ func (b *GRPCBackendPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCB
|
|||||||
broker: broker,
|
broker: broker,
|
||||||
cleanupCh: make(chan struct{}),
|
cleanupCh: make(chan struct{}),
|
||||||
doneCtx: ctx,
|
doneCtx: ctx,
|
||||||
metadataMode: b.MetadataMode,
|
// Only run in metadata mode if mode is true and autoMTLS is not supported
|
||||||
|
metadataMode: b.MetadataMode && !b.AutoMTLSSupported,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the value and set the type
|
// Create the value and set the type
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
log "github.com/hashicorp/go-hclog"
|
|
||||||
plugin "github.com/hashicorp/go-plugin"
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||||
@@ -35,7 +34,7 @@ func (b *BackendPluginClient) Cleanup(ctx context.Context) {
|
|||||||
// external plugins, or a concrete implementation of the backend if it is a builtin backend.
|
// external plugins, or a concrete implementation of the backend if it is a builtin backend.
|
||||||
// The backend is returned as a logical.Backend interface. The isMetadataMode param determines whether
|
// The backend is returned as a logical.Backend interface. The isMetadataMode param determines whether
|
||||||
// the plugin should run in metadata mode.
|
// the plugin should run in metadata mode.
|
||||||
func NewBackend(ctx context.Context, pluginName string, pluginType consts.PluginType, sys pluginutil.LookRunnerUtil, conf *logical.BackendConfig, isMetadataMode bool) (logical.Backend, error) {
|
func NewBackend(ctx context.Context, pluginName string, pluginType consts.PluginType, sys pluginutil.LookRunnerUtil, conf *logical.BackendConfig, isMetadataMode bool, autoMTLS bool) (logical.Backend, error) {
|
||||||
// Look for plugin in the plugin catalog
|
// Look for plugin in the plugin catalog
|
||||||
pluginRunner, err := sys.LookupPlugin(ctx, pluginName, pluginType)
|
pluginRunner, err := sys.LookupPlugin(ctx, pluginName, pluginType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -59,8 +58,16 @@ func NewBackend(ctx context.Context, pluginName string, pluginType consts.Plugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
config := pluginutil.PluginClientConfig{
|
||||||
|
Name: pluginName,
|
||||||
|
PluginType: pluginType,
|
||||||
|
Logger: conf.Logger.Named(pluginName),
|
||||||
|
IsMetadataMode: isMetadataMode,
|
||||||
|
AutoMTLS: autoMTLS,
|
||||||
|
Wrapper: sys,
|
||||||
|
}
|
||||||
// create a backendPluginClient instance
|
// create a backendPluginClient instance
|
||||||
backend, err = NewPluginClient(ctx, sys, pluginRunner, conf.Logger, isMetadataMode)
|
backend, err = NewPluginClient(ctx, pluginRunner, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -69,34 +76,49 @@ func NewBackend(ctx context.Context, pluginName string, pluginType consts.Plugin
|
|||||||
return backend, nil
|
return backend, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginRunner, logger log.Logger, isMetadataMode bool) (logical.Backend, error) {
|
// pluginSet returns the go-plugin PluginSet that we can dispense. This ensures
|
||||||
// pluginMap is the map of plugins we can dispense.
|
// that plugins that don't support AutoMTLS are run on the appropriate version.
|
||||||
pluginSet := map[int]plugin.PluginSet{
|
func pluginSet(autoMTLS, metadataMode bool) map[int]plugin.PluginSet {
|
||||||
|
if autoMTLS {
|
||||||
|
return map[int]plugin.PluginSet{
|
||||||
|
5: {
|
||||||
|
"backend": &GRPCBackendPlugin{
|
||||||
|
MetadataMode: false,
|
||||||
|
AutoMTLSSupported: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map[int]plugin.PluginSet{
|
||||||
// Version 3 used to supports both protocols. We want to keep it around
|
// Version 3 used to supports both protocols. We want to keep it around
|
||||||
// since it's possible old plugins built against this version will still
|
// since it's possible old plugins built against this version will still
|
||||||
// work with gRPC. There is currently no difference between version 3
|
// work with gRPC. There is currently no difference between version 3
|
||||||
// and version 4.
|
// and version 4.
|
||||||
3: {
|
3: {
|
||||||
"backend": &GRPCBackendPlugin{
|
"backend": &GRPCBackendPlugin{
|
||||||
MetadataMode: isMetadataMode,
|
MetadataMode: metadataMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
"backend": &GRPCBackendPlugin{
|
"backend": &GRPCBackendPlugin{
|
||||||
MetadataMode: isMetadataMode,
|
MetadataMode: metadataMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
namedLogger := logger.Named(pluginRunner.Name)
|
|
||||||
|
|
||||||
var client *plugin.Client
|
|
||||||
var err error
|
|
||||||
if isMetadataMode {
|
|
||||||
client, err = pluginRunner.RunMetadataMode(ctx, sys, pluginSet, handshakeConfig, []string{}, namedLogger)
|
|
||||||
} else {
|
|
||||||
client, err = pluginRunner.Run(ctx, sys, pluginSet, handshakeConfig, []string{}, namedLogger)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewPluginClient(ctx context.Context, pluginRunner *pluginutil.PluginRunner, config pluginutil.PluginClientConfig) (logical.Backend, error) {
|
||||||
|
ps := pluginSet(config.AutoMTLS, config.IsMetadataMode)
|
||||||
|
|
||||||
|
client, err := pluginRunner.RunConfig(ctx,
|
||||||
|
pluginutil.Runner(config.Wrapper),
|
||||||
|
pluginutil.PluginSets(ps),
|
||||||
|
pluginutil.HandshakeConfig(handshakeConfig),
|
||||||
|
pluginutil.Env(),
|
||||||
|
pluginutil.Logger(config.Logger),
|
||||||
|
pluginutil.MetadataMode(config.IsMetadataMode),
|
||||||
|
pluginutil.AutoMTLS(config.AutoMTLS),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -126,9 +148,9 @@ func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunne
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the backend in a tracing middleware
|
// Wrap the backend in a tracing middleware
|
||||||
if namedLogger.IsTrace() {
|
if config.Logger.IsTrace() {
|
||||||
backend = &backendTracingMiddleware{
|
backend = &backendTracingMiddleware{
|
||||||
logger: namedLogger.With("transport", transport),
|
logger: config.Logger.With("transport", transport),
|
||||||
next: backend,
|
next: backend,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,12 +37,13 @@ func Serve(opts *ServeOpts) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// pluginMap is the map of plugins we can dispense.
|
// pluginSets is the map of plugins we can dispense.
|
||||||
pluginSets := map[int]plugin.PluginSet{
|
pluginSets := map[int]plugin.PluginSet{
|
||||||
// Version 3 used to supports both protocols. We want to keep it around
|
// Version 3 used to supports both protocols. We want to keep it around
|
||||||
// since it's possible old plugins built against this version will still
|
// since it's possible old plugins built against this version will still
|
||||||
// work with gRPC. There is currently no difference between version 3
|
// work with gRPC. There is currently no difference between version 3
|
||||||
// and version 4.
|
// and version 4.
|
||||||
|
// AutoMTLS is not supported by versions lower than 5.
|
||||||
3: {
|
3: {
|
||||||
"backend": &GRPCBackendPlugin{
|
"backend": &GRPCBackendPlugin{
|
||||||
Factory: opts.BackendFactoryFunc,
|
Factory: opts.BackendFactoryFunc,
|
||||||
@@ -55,6 +56,13 @@ func Serve(opts *ServeOpts) error {
|
|||||||
Logger: logger,
|
Logger: logger,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
5: {
|
||||||
|
"backend": &GRPCBackendPlugin{
|
||||||
|
Factory: opts.BackendFactoryFunc,
|
||||||
|
Logger: logger,
|
||||||
|
AutoMTLSSupported: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := pluginutil.OptionallyEnableMlock()
|
err := pluginutil.OptionallyEnableMlock()
|
||||||
|
|||||||
@@ -31,8 +31,33 @@ const (
|
|||||||
expectedEnvValue = "BAR"
|
expectedEnvValue = "BAR"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// logicalVersionMap is a map of version to test plugin
|
||||||
|
var logicalVersionMap = map[string]string{
|
||||||
|
"v4": "TestBackend_PluginMain_V4_Logical",
|
||||||
|
"v5": "TestBackend_PluginMainLogical",
|
||||||
|
}
|
||||||
|
|
||||||
|
// credentialVersionMap is a map of version to test plugin
|
||||||
|
var credentialVersionMap = map[string]string{
|
||||||
|
"v4": "TestBackend_PluginMain_V4_Credentials",
|
||||||
|
"v5": "TestBackend_PluginMainCredentials",
|
||||||
|
}
|
||||||
|
|
||||||
func TestSystemBackend_Plugin_secret(t *testing.T) {
|
func TestSystemBackend_Plugin_secret(t *testing.T) {
|
||||||
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical)
|
testCases := []struct {
|
||||||
|
pluginVersion string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pluginVersion: "v5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pluginVersion: "v4",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.pluginVersion, func(t *testing.T) {
|
||||||
|
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical, tc.pluginVersion)
|
||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
core := cluster.Cores[0]
|
core := cluster.Cores[0]
|
||||||
@@ -67,10 +92,25 @@ func TestSystemBackend_Plugin_secret(t *testing.T) {
|
|||||||
// If it fails, it means unseal process failed
|
// If it fails, it means unseal process failed
|
||||||
vault.TestWaitActive(t, core.Core)
|
vault.TestWaitActive(t, core.Core)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_Plugin_auth(t *testing.T) {
|
func TestSystemBackend_Plugin_auth(t *testing.T) {
|
||||||
cluster := testSystemBackendMock(t, 1, 1, logical.TypeCredential)
|
testCases := []struct {
|
||||||
|
pluginVersion string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pluginVersion: "v5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pluginVersion: "v4",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.pluginVersion, func(t *testing.T) {
|
||||||
|
cluster := testSystemBackendMock(t, 1, 1, logical.TypeCredential, tc.pluginVersion)
|
||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
core := cluster.Cores[0]
|
core := cluster.Cores[0]
|
||||||
@@ -105,10 +145,25 @@ func TestSystemBackend_Plugin_auth(t *testing.T) {
|
|||||||
// If it fails, it means unseal process failed
|
// If it fails, it means unseal process failed
|
||||||
vault.TestWaitActive(t, core.Core)
|
vault.TestWaitActive(t, core.Core)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_Plugin_MissingBinary(t *testing.T) {
|
func TestSystemBackend_Plugin_MissingBinary(t *testing.T) {
|
||||||
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical)
|
testCases := []struct {
|
||||||
|
pluginVersion string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pluginVersion: "v5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pluginVersion: "v4",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.pluginVersion, func(t *testing.T) {
|
||||||
|
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical, tc.pluginVersion)
|
||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
core := cluster.Cores[0]
|
core := cluster.Cores[0]
|
||||||
@@ -146,10 +201,25 @@ func TestSystemBackend_Plugin_MissingBinary(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected error")
|
t.Fatalf("expected error")
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_Plugin_MismatchType(t *testing.T) {
|
func TestSystemBackend_Plugin_MismatchType(t *testing.T) {
|
||||||
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical)
|
testCases := []struct {
|
||||||
|
pluginVersion string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pluginVersion: "v5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pluginVersion: "v4",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.pluginVersion, func(t *testing.T) {
|
||||||
|
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical, tc.pluginVersion)
|
||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
core := cluster.Cores[0]
|
core := cluster.Cores[0]
|
||||||
@@ -168,28 +238,43 @@ func TestSystemBackend_Plugin_MismatchType(t *testing.T) {
|
|||||||
|
|
||||||
// Sleep a bit before cleanup is called
|
// Sleep a bit before cleanup is called
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_Plugin_CatalogRemoved(t *testing.T) {
|
func TestSystemBackend_Plugin_CatalogRemoved(t *testing.T) {
|
||||||
t.Run("secret", func(t *testing.T) {
|
t.Run("secret", func(t *testing.T) {
|
||||||
testPlugin_CatalogRemoved(t, logical.TypeLogical, false)
|
testPlugin_CatalogRemoved(t, logical.TypeLogical, false, logicalVersionMap)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("auth", func(t *testing.T) {
|
t.Run("auth", func(t *testing.T) {
|
||||||
testPlugin_CatalogRemoved(t, logical.TypeCredential, false)
|
testPlugin_CatalogRemoved(t, logical.TypeCredential, false, credentialVersionMap)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("secret-mount-existing", func(t *testing.T) {
|
t.Run("secret-mount-existing", func(t *testing.T) {
|
||||||
testPlugin_CatalogRemoved(t, logical.TypeLogical, true)
|
testPlugin_CatalogRemoved(t, logical.TypeLogical, true, logicalVersionMap)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("auth-mount-existing", func(t *testing.T) {
|
t.Run("auth-mount-existing", func(t *testing.T) {
|
||||||
testPlugin_CatalogRemoved(t, logical.TypeCredential, true)
|
testPlugin_CatalogRemoved(t, logical.TypeCredential, true, credentialVersionMap)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMount bool) {
|
func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMount bool, versionMap map[string]string) {
|
||||||
cluster := testSystemBackendMock(t, 1, 1, btype)
|
testCases := []struct {
|
||||||
|
pluginVersion string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pluginVersion: "v5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pluginVersion: "v4",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.pluginVersion, func(t *testing.T) {
|
||||||
|
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical, tc.pluginVersion)
|
||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
core := cluster.Cores[0]
|
core := cluster.Cores[0]
|
||||||
@@ -230,13 +315,13 @@ func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMoun
|
|||||||
switch btype {
|
switch btype {
|
||||||
case logical.TypeLogical:
|
case logical.TypeLogical:
|
||||||
// Add plugin back to the catalog
|
// Add plugin back to the catalog
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "TestBackend_PluginMainLogical", []string{}, "")
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, logicalVersionMap[tc.pluginVersion], []string{}, "")
|
||||||
_, err = core.Client.Logical().Write("sys/mounts/mock-0", map[string]interface{}{
|
_, err = core.Client.Logical().Write("sys/mounts/mock-0", map[string]interface{}{
|
||||||
"type": "test",
|
"type": "test",
|
||||||
})
|
})
|
||||||
case logical.TypeCredential:
|
case logical.TypeCredential:
|
||||||
// Add plugin back to the catalog
|
// Add plugin back to the catalog
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "TestBackend_PluginMainCredentials", []string{}, "")
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, credentialVersionMap[tc.pluginVersion], []string{}, "")
|
||||||
_, err = core.Client.Logical().Write("sys/auth/mock-0", map[string]interface{}{
|
_, err = core.Client.Logical().Write("sys/auth/mock-0", map[string]interface{}{
|
||||||
"type": "test",
|
"type": "test",
|
||||||
})
|
})
|
||||||
@@ -245,6 +330,8 @@ func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMoun
|
|||||||
t.Fatal("expected error when mounting on existing path")
|
t.Fatal("expected error when mounting on existing path")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_Plugin_continueOnError(t *testing.T) {
|
func TestSystemBackend_Plugin_continueOnError(t *testing.T) {
|
||||||
@@ -278,7 +365,20 @@ func TestSystemBackend_Plugin_continueOnError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatch bool, mountPoint string, pluginType consts.PluginType) {
|
func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatch bool, mountPoint string, pluginType consts.PluginType) {
|
||||||
cluster := testSystemBackendMock(t, 1, 1, btype)
|
testCases := []struct {
|
||||||
|
pluginVersion string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pluginVersion: "v5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pluginVersion: "v4",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.pluginVersion, func(t *testing.T) {
|
||||||
|
cluster := testSystemBackendMock(t, 1, 1, btype, tc.pluginVersion)
|
||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
core := cluster.Cores[0]
|
core := cluster.Cores[0]
|
||||||
@@ -296,18 +396,6 @@ func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatc
|
|||||||
t.Fatal("invalid command")
|
t.Fatal("invalid command")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount credential type plugins
|
|
||||||
switch btype {
|
|
||||||
case logical.TypeCredential:
|
|
||||||
vault.TestAddTestPlugin(t, core.Core, mountPoint, consts.PluginTypeCredential, "TestBackend_PluginMainCredentials", []string{}, cluster.TempDir)
|
|
||||||
_, err = core.Client.Logical().Write(fmt.Sprintf("sys/auth/%s", mountPoint), map[string]interface{}{
|
|
||||||
"type": "mock-plugin",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err:%v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger a sha256 mismatch or missing plugin error
|
// Trigger a sha256 mismatch or missing plugin error
|
||||||
if mismatch {
|
if mismatch {
|
||||||
req = logical.TestRequest(t, logical.UpdateOperation, fmt.Sprintf("sys/plugins/catalog/%s/mock-plugin", pluginType))
|
req = logical.TestRequest(t, logical.UpdateOperation, fmt.Sprintf("sys/plugins/catalog/%s/mock-plugin", pluginType))
|
||||||
@@ -351,9 +439,11 @@ func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatc
|
|||||||
// Re-add the plugin to the catalog
|
// Re-add the plugin to the catalog
|
||||||
switch btype {
|
switch btype {
|
||||||
case logical.TypeLogical:
|
case logical.TypeLogical:
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "TestBackend_PluginMainLogical", []string{}, cluster.TempDir)
|
plugin := logicalVersionMap[tc.pluginVersion]
|
||||||
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, plugin, []string{}, cluster.TempDir)
|
||||||
case logical.TypeCredential:
|
case logical.TypeCredential:
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "TestBackend_PluginMainCredentials", []string{}, cluster.TempDir)
|
plugin := credentialVersionMap[tc.pluginVersion]
|
||||||
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, plugin, []string{}, cluster.TempDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload the plugin
|
// Reload the plugin
|
||||||
@@ -385,10 +475,25 @@ func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatc
|
|||||||
if resp == nil {
|
if resp == nil {
|
||||||
t.Fatalf("bad: response should not be nil")
|
t.Fatalf("bad: response should not be nil")
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_Plugin_autoReload(t *testing.T) {
|
func TestSystemBackend_Plugin_autoReload(t *testing.T) {
|
||||||
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical)
|
testCases := []struct {
|
||||||
|
pluginVersion string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pluginVersion: "v5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pluginVersion: "v4",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.pluginVersion, func(t *testing.T) {
|
||||||
|
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical, tc.pluginVersion)
|
||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
core := cluster.Cores[0]
|
core := cluster.Cores[0]
|
||||||
@@ -426,10 +531,25 @@ func TestSystemBackend_Plugin_autoReload(t *testing.T) {
|
|||||||
if resp.Data["value"].(string) == "baz" {
|
if resp.Data["value"].(string) == "baz" {
|
||||||
t.Fatal("did not expect backend internal value to be 'baz'")
|
t.Fatal("did not expect backend internal value to be 'baz'")
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_Plugin_SealUnseal(t *testing.T) {
|
func TestSystemBackend_Plugin_SealUnseal(t *testing.T) {
|
||||||
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical)
|
testCases := []struct {
|
||||||
|
pluginVersion string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pluginVersion: "v5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pluginVersion: "v4",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.pluginVersion, func(t *testing.T) {
|
||||||
|
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical, tc.pluginVersion)
|
||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
// Seal the cluster
|
// Seal the cluster
|
||||||
@@ -452,6 +572,8 @@ func TestSystemBackend_Plugin_SealUnseal(t *testing.T) {
|
|||||||
// Wait for active so post-unseal takes place
|
// Wait for active so post-unseal takes place
|
||||||
// If it fails, it means unseal process failed
|
// If it fails, it means unseal process failed
|
||||||
vault.TestWaitActive(t, cluster.Cores[0].Core)
|
vault.TestWaitActive(t, cluster.Cores[0].Core)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_Plugin_reload(t *testing.T) {
|
func TestSystemBackend_Plugin_reload(t *testing.T) {
|
||||||
@@ -498,7 +620,20 @@ func TestSystemBackend_Plugin_reload(t *testing.T) {
|
|||||||
|
|
||||||
// Helper func to test different reload methods on plugin reload endpoint
|
// Helper func to test different reload methods on plugin reload endpoint
|
||||||
func testSystemBackend_PluginReload(t *testing.T, reqData map[string]interface{}, backendType logical.BackendType) {
|
func testSystemBackend_PluginReload(t *testing.T, reqData map[string]interface{}, backendType logical.BackendType) {
|
||||||
cluster := testSystemBackendMock(t, 1, 2, backendType)
|
testCases := []struct {
|
||||||
|
pluginVersion string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pluginVersion: "v5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pluginVersion: "v4",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.pluginVersion, func(t *testing.T) {
|
||||||
|
cluster := testSystemBackendMock(t, 1, 2, backendType, tc.pluginVersion)
|
||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
core := cluster.Cores[0]
|
core := cluster.Cores[0]
|
||||||
@@ -546,6 +681,8 @@ func testSystemBackend_PluginReload(t *testing.T, reqData map[string]interface{}
|
|||||||
t.Fatal("did not expect backend internal value to be 'baz'")
|
t.Fatal("did not expect backend internal value to be 'baz'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// testSystemBackendMock returns a systemBackend with the desired number
|
// testSystemBackendMock returns a systemBackend with the desired number
|
||||||
@@ -553,7 +690,7 @@ func testSystemBackend_PluginReload(t *testing.T, reqData map[string]interface{}
|
|||||||
// ways of providing the plugin_name.
|
// ways of providing the plugin_name.
|
||||||
//
|
//
|
||||||
// The mounts are mounted at sys/mounts/mock-[numMounts] or sys/auth/mock-[numMounts]
|
// The mounts are mounted at sys/mounts/mock-[numMounts] or sys/auth/mock-[numMounts]
|
||||||
func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType logical.BackendType) *vault.TestCluster {
|
func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType logical.BackendType, pluginVersion string) *vault.TestCluster {
|
||||||
coreConfig := &vault.CoreConfig{
|
coreConfig := &vault.CoreConfig{
|
||||||
LogicalBackends: map[string]logical.Factory{
|
LogicalBackends: map[string]logical.Factory{
|
||||||
"plugin": plugin.Factory,
|
"plugin": plugin.Factory,
|
||||||
@@ -585,7 +722,8 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo
|
|||||||
|
|
||||||
switch backendType {
|
switch backendType {
|
||||||
case logical.TypeLogical:
|
case logical.TypeLogical:
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "TestBackend_PluginMainLogical", []string{}, tempDir)
|
plugin := logicalVersionMap[pluginVersion]
|
||||||
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, plugin, []string{}, tempDir)
|
||||||
for i := 0; i < numMounts; i++ {
|
for i := 0; i < numMounts; i++ {
|
||||||
// Alternate input styles for plugin_name on every other mount
|
// Alternate input styles for plugin_name on every other mount
|
||||||
options := map[string]interface{}{
|
options := map[string]interface{}{
|
||||||
@@ -600,7 +738,8 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case logical.TypeCredential:
|
case logical.TypeCredential:
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "TestBackend_PluginMainCredentials", []string{}, tempDir)
|
plugin := credentialVersionMap[pluginVersion]
|
||||||
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, plugin, []string{}, tempDir)
|
||||||
for i := 0; i < numMounts; i++ {
|
for i := 0; i < numMounts; i++ {
|
||||||
// Alternate input styles for plugin_name on every other mount
|
// Alternate input styles for plugin_name on every other mount
|
||||||
options := map[string]interface{}{
|
options := map[string]interface{}{
|
||||||
@@ -671,9 +810,15 @@ func testSystemBackend_SingleCluster_Env(t *testing.T, env []string) *vault.Test
|
|||||||
return cluster
|
return cluster
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBackend_PluginMainLogical(t *testing.T) {
|
func TestBackend_PluginMain_V4_Logical(t *testing.T) {
|
||||||
args := []string{}
|
args := []string{}
|
||||||
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" && os.Getenv(pluginutil.PluginMetadataModeEnv) != "true" {
|
// don't run as a standalone unit test
|
||||||
|
if os.Getenv(pluginutil.PluginVaultVersionEnv) == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't run as a V5 plugin
|
||||||
|
if os.Getenv(pluginutil.PluginAutoMTLSEnv) == "true" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,6 +831,8 @@ func TestBackend_PluginMainLogical(t *testing.T) {
|
|||||||
apiClientMeta := &api.PluginAPIClientMeta{}
|
apiClientMeta := &api.PluginAPIClientMeta{}
|
||||||
flags := apiClientMeta.FlagSet()
|
flags := apiClientMeta.FlagSet()
|
||||||
flags.Parse(args)
|
flags.Parse(args)
|
||||||
|
|
||||||
|
// V4 does not support AutoMTLS so we set a TLSConfig via TLSProviderFunc
|
||||||
tlsConfig := apiClientMeta.GetTLSConfig()
|
tlsConfig := apiClientMeta.GetTLSConfig()
|
||||||
tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)
|
tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)
|
||||||
|
|
||||||
@@ -700,9 +847,9 @@ func TestBackend_PluginMainLogical(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBackend_PluginMainCredentials(t *testing.T) {
|
func TestBackend_PluginMainLogical(t *testing.T) {
|
||||||
args := []string{}
|
args := []string{}
|
||||||
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" && os.Getenv(pluginutil.PluginMetadataModeEnv) != "true" {
|
if os.Getenv(pluginutil.PluginVaultVersionEnv) == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,6 +862,40 @@ func TestBackend_PluginMainCredentials(t *testing.T) {
|
|||||||
apiClientMeta := &api.PluginAPIClientMeta{}
|
apiClientMeta := &api.PluginAPIClientMeta{}
|
||||||
flags := apiClientMeta.FlagSet()
|
flags := apiClientMeta.FlagSet()
|
||||||
flags.Parse(args)
|
flags.Parse(args)
|
||||||
|
|
||||||
|
factoryFunc := mock.FactoryType(logical.TypeLogical)
|
||||||
|
|
||||||
|
err := lplugin.Serve(&lplugin.ServeOpts{
|
||||||
|
BackendFactoryFunc: factoryFunc,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackend_PluginMain_V4_Credentials(t *testing.T) {
|
||||||
|
args := []string{}
|
||||||
|
// don't run as a standalone unit test
|
||||||
|
if os.Getenv(pluginutil.PluginVaultVersionEnv) == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't run as a V5 plugin
|
||||||
|
if os.Getenv(pluginutil.PluginAutoMTLSEnv) == "true" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
caPEM := os.Getenv(pluginutil.PluginCACertPEMEnv)
|
||||||
|
if caPEM == "" {
|
||||||
|
t.Fatal("CA cert not passed in")
|
||||||
|
}
|
||||||
|
args = append(args, fmt.Sprintf("--ca-cert=%s", caPEM))
|
||||||
|
|
||||||
|
apiClientMeta := &api.PluginAPIClientMeta{}
|
||||||
|
flags := apiClientMeta.FlagSet()
|
||||||
|
flags.Parse(args)
|
||||||
|
|
||||||
|
// V4 does not support AutoMTLS so we set a TLSConfig via TLSProviderFunc
|
||||||
tlsConfig := apiClientMeta.GetTLSConfig()
|
tlsConfig := apiClientMeta.GetTLSConfig()
|
||||||
tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)
|
tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)
|
||||||
|
|
||||||
@@ -729,6 +910,32 @@ func TestBackend_PluginMainCredentials(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBackend_PluginMainCredentials(t *testing.T) {
|
||||||
|
args := []string{}
|
||||||
|
if os.Getenv(pluginutil.PluginVaultVersionEnv) == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
caPEM := os.Getenv(pluginutil.PluginCACertPEMEnv)
|
||||||
|
if caPEM == "" {
|
||||||
|
t.Fatal("CA cert not passed in")
|
||||||
|
}
|
||||||
|
args = append(args, fmt.Sprintf("--ca-cert=%s", caPEM))
|
||||||
|
|
||||||
|
apiClientMeta := &api.PluginAPIClientMeta{}
|
||||||
|
flags := apiClientMeta.FlagSet()
|
||||||
|
flags.Parse(args)
|
||||||
|
|
||||||
|
factoryFunc := mock.FactoryType(logical.TypeCredential)
|
||||||
|
|
||||||
|
err := lplugin.Serve(&lplugin.ServeOpts{
|
||||||
|
BackendFactoryFunc: factoryFunc,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestBackend_PluginMainEnv is a mock plugin that simply checks for the existence of FOO env var.
|
// TestBackend_PluginMainEnv is a mock plugin that simply checks for the existence of FOO env var.
|
||||||
func TestBackend_PluginMainEnv(t *testing.T) {
|
func TestBackend_PluginMainEnv(t *testing.T) {
|
||||||
args := []string{}
|
args := []string{}
|
||||||
@@ -751,14 +958,11 @@ func TestBackend_PluginMainEnv(t *testing.T) {
|
|||||||
apiClientMeta := &api.PluginAPIClientMeta{}
|
apiClientMeta := &api.PluginAPIClientMeta{}
|
||||||
flags := apiClientMeta.FlagSet()
|
flags := apiClientMeta.FlagSet()
|
||||||
flags.Parse(args)
|
flags.Parse(args)
|
||||||
tlsConfig := apiClientMeta.GetTLSConfig()
|
|
||||||
tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)
|
|
||||||
|
|
||||||
factoryFunc := mock.FactoryType(logical.TypeLogical)
|
factoryFunc := mock.FactoryType(logical.TypeLogical)
|
||||||
|
|
||||||
err := lplugin.Serve(&lplugin.ServeOpts{
|
err := lplugin.Serve(&lplugin.ServeOpts{
|
||||||
BackendFactoryFunc: factoryFunc,
|
BackendFactoryFunc: factoryFunc,
|
||||||
TLSProviderFunc: tlsProviderFunc,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -325,14 +325,48 @@ func (c *PluginCatalog) getPluginTypeFromUnknown(ctx context.Context, logger log
|
|||||||
}
|
}
|
||||||
merr = multierror.Append(merr, err)
|
merr = multierror.Append(merr, err)
|
||||||
|
|
||||||
// Attempt to run as backend plugin
|
pluginType, err := c.getBackendPluginType(ctx, plugin)
|
||||||
client, err := backendplugin.NewPluginClient(ctx, nil, plugin, log.NewNullLogger(), true)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err := client.Setup(ctx, &logical.BackendConfig{})
|
return pluginType, nil
|
||||||
|
}
|
||||||
|
merr = multierror.Append(merr, err)
|
||||||
|
|
||||||
|
return consts.PluginTypeUnknown, merr
|
||||||
|
}
|
||||||
|
|
||||||
|
// getBackendPluginType returns the plugin type (secrets/auth) and an error if
|
||||||
|
// the plugin is not a backend plugin.
|
||||||
|
func (c *PluginCatalog) getBackendPluginType(ctx context.Context, pluginRunner *pluginutil.PluginRunner) (consts.PluginType, error) {
|
||||||
|
var client logical.Backend
|
||||||
|
var merr *multierror.Error
|
||||||
|
config := pluginutil.PluginClientConfig{
|
||||||
|
Name: pluginRunner.Name,
|
||||||
|
Logger: log.NewNullLogger(),
|
||||||
|
IsMetadataMode: false,
|
||||||
|
AutoMTLS: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to run as backend V5 plugin
|
||||||
|
c.logger.Debug("attempting to load backend plugin", "name", pluginRunner.Name)
|
||||||
|
client, err := backendplugin.NewPluginClient(ctx, pluginRunner, config)
|
||||||
|
if err != nil {
|
||||||
|
merr = multierror.Append(merr, err)
|
||||||
|
c.logger.Debug("failed to dispense v5 backend plugin", "name", pluginRunner.Name, "error", err)
|
||||||
|
config.AutoMTLS = false
|
||||||
|
config.IsMetadataMode = true
|
||||||
|
// attemtp to run as a v4 backend plugin
|
||||||
|
client, err = backendplugin.NewPluginClient(ctx, pluginRunner, config)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Debug("failed to dispense v4 backend plugin", "name", pluginRunner.Name, "error", err)
|
||||||
|
return consts.PluginTypeUnknown, merr.ErrorOrNil()
|
||||||
|
}
|
||||||
|
c.logger.Debug("successfully dispensed v4 backend plugin", "name", pluginRunner.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.Setup(ctx, &logical.BackendConfig{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return consts.PluginTypeUnknown, err
|
return consts.PluginTypeUnknown, err
|
||||||
}
|
}
|
||||||
|
|
||||||
backendType := client.Type()
|
backendType := client.Type()
|
||||||
client.Cleanup(ctx)
|
client.Cleanup(ctx)
|
||||||
|
|
||||||
@@ -342,22 +376,21 @@ func (c *PluginCatalog) getPluginTypeFromUnknown(ctx context.Context, logger log
|
|||||||
case logical.TypeLogical:
|
case logical.TypeLogical:
|
||||||
return consts.PluginTypeSecrets, nil
|
return consts.PluginTypeSecrets, nil
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
merr = multierror.Append(merr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if client == nil || client.Type() == logical.TypeUnknown {
|
if client == nil || client.Type() == logical.TypeUnknown {
|
||||||
logger.Warn("unknown plugin type",
|
c.logger.Warn("unknown plugin type",
|
||||||
"plugin name", plugin.Name,
|
"plugin name", pluginRunner.Name,
|
||||||
"error", merr.Error())
|
"error", merr.Error())
|
||||||
} else {
|
} else {
|
||||||
logger.Warn("unsupported plugin type",
|
c.logger.Warn("unsupported plugin type",
|
||||||
"plugin name", plugin.Name,
|
"plugin name", pluginRunner.Name,
|
||||||
"plugin type", client.Type().String(),
|
"plugin type", client.Type().String(),
|
||||||
"error", merr.Error())
|
"error", merr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return consts.PluginTypeUnknown, nil
|
merr = multierror.Append(merr, fmt.Errorf("failed to load plugin as backend plugin: %w", err))
|
||||||
|
|
||||||
|
return consts.PluginTypeUnknown, merr.ErrorOrNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
// isDatabasePlugin returns true if the plugin supports multiplexing. An error
|
// isDatabasePlugin returns true if the plugin supports multiplexing. An error
|
||||||
|
|||||||
Reference in New Issue
Block a user