Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport of AutoMTLS for secrets/auth plugins into release/1.11.x #16343

Merged
merged 1 commit into from
Jul 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion api/plugin_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import (
)

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
// to bootstrap mounting plugins.
PluginMetadataModeEnv = "VAULT_PLUGIN_METADATA_MODE"
Expand Down Expand Up @@ -120,7 +124,7 @@ func VaultPluginTLSProvider(apiTLSConfig *TLSConfig) func() (*tls.Config, error)
// VaultPluginTLSProviderContext is run inside a plugin and retrieves the response
// wrapped TLS certificate from vault. It returns a configured TLS Config.
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
}

Expand Down
29 changes: 23 additions & 6 deletions builtin/plugin/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"reflect"
"sync"

"github.com/hashicorp/go-multierror"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/consts"
Expand Down Expand Up @@ -49,17 +50,32 @@ func Backend(ctx context.Context, conf *logical.BackendConfig) (logical.Backend,

sys := conf.System

// NewBackend with isMetadataMode set to true
raw, err := bplugin.NewBackend(ctx, name, pluginType, sys, conf, true)
merr := &multierror.Error{}
// NewBackend with isMetadataMode set to false
raw, err := bplugin.NewBackend(ctx, name, pluginType, sys, conf, false, true)
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this else supposed to go with the if right above instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though I'm not sure I follow the logic here 100% in either case, tbh.

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)
if err != nil {
raw.Cleanup(ctx)
return nil, err
}
// Get SpecialPaths and BackendType
paths := raw.SpecialPaths()
btype := raw.Type()

Expand All @@ -83,7 +99,8 @@ type PluginBackend struct {
logical.Backend
sync.RWMutex

config *logical.BackendConfig
autoMTLSSupported bool
config *logical.BackendConfig

// Used to detect if we already reloaded
canary string
Expand All @@ -103,7 +120,7 @@ func (b *PluginBackend) startBackend(ctx context.Context, storage logical.Storag
// Ensure proper cleanup of the backend (i.e. call client.Kill())
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 {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion builtin/plugin/backend_lazyLoad_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
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 {
t.Fatal(err)
}
Expand Down
3 changes: 3 additions & 0 deletions changelog/15671.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
plugins: Use AutoMTLS for secrets engines and auth methods run as external plugins.
```
4 changes: 4 additions & 0 deletions sdk/helper/pluginutil/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import (
)

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
// enabling mlock
PluginMlockEnabled = "VAULT_PLUGIN_MLOCK_ENABLED"
Expand Down
2 changes: 2 additions & 0 deletions sdk/helper/pluginutil/multiplexing.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
status "google.golang.org/grpc/status"
)

const MultiplexingCtxKey string = "multiplex_id"

type PluginMultiplexingServerImpl struct {
UnimplementedPluginMultiplexingServer

Expand Down
12 changes: 7 additions & 5 deletions sdk/helper/pluginutil/run_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type PluginClientConfig struct {
IsMetadataMode bool
AutoMTLS bool
MLock bool
Wrapper RunnerUtil
}

type runConfig struct {
Expand All @@ -33,8 +34,6 @@ type runConfig struct {
// Initialized with what's in PluginRunner.Env, but can be added to
env []string

wrapper RunnerUtil

PluginClientConfig
}

Expand All @@ -43,7 +42,7 @@ func (rc runConfig) makeConfig(ctx context.Context) (*plugin.ClientConfig, error
cmd.Env = append(cmd.Env, rc.env...)

// 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", PluginVaultVersionEnv, version.GetVersion().Version))
Expand All @@ -54,6 +53,9 @@ func (rc runConfig) makeConfig(ctx context.Context) (*plugin.ClientConfig, error
metadataEnv := fmt.Sprintf("%s=%t", PluginMetadataModeEnv, rc.IsMetadataMode)
cmd.Env = append(cmd.Env, metadataEnv)

automtlsEnv := fmt.Sprintf("%s=%t", PluginAutoMTLSEnv, rc.AutoMTLS)
cmd.Env = append(cmd.Env, automtlsEnv)

var clientTLSConfig *tls.Config
if !rc.AutoMTLS && !rc.IsMetadataMode {
// Get a CA TLS Certificate
Expand All @@ -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
// token.
wrapToken, err := wrapServerConfig(ctx, rc.wrapper, certBytes, key)
wrapToken, err := wrapServerConfig(ctx, rc.Wrapper, certBytes, key)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -120,7 +122,7 @@ func Env(env ...string) RunOpt {

func Runner(wrapper RunnerUtil) RunOpt {
return func(rc *runConfig) {
rc.wrapper = wrapper
rc.Wrapper = wrapper
}
}

Expand Down
12 changes: 7 additions & 5 deletions sdk/helper/pluginutil/run_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"os/exec"
"reflect"
"testing"
"time"

Expand All @@ -14,6 +13,7 @@ import (
"github.com/hashicorp/go-plugin"
"github.com/hashicorp/vault/sdk/helper/wrapping"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

func TestMakeConfig(t *testing.T) {
Expand Down Expand Up @@ -78,6 +78,7 @@ func TestMakeConfig(t *testing.T) {
"initial=true",
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, true),
fmt.Sprintf("%s=%t", PluginAutoMTLSEnv, false),
},
),
SecureConfig: &plugin.SecureConfig{
Expand Down Expand Up @@ -143,6 +144,7 @@ func TestMakeConfig(t *testing.T) {
fmt.Sprintf("%s=%t", PluginMlockEnabled, true),
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, false),
fmt.Sprintf("%s=%t", PluginAutoMTLSEnv, false),
fmt.Sprintf("%s=%s", PluginUnwrapTokenEnv, "testtoken"),
},
),
Expand Down Expand Up @@ -205,6 +207,7 @@ func TestMakeConfig(t *testing.T) {
"initial=true",
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, true),
fmt.Sprintf("%s=%t", PluginAutoMTLSEnv, true),
},
),
SecureConfig: &plugin.SecureConfig{
Expand Down Expand Up @@ -266,6 +269,7 @@ func TestMakeConfig(t *testing.T) {
"initial=true",
fmt.Sprintf("%s=%s", PluginVaultVersionEnv, version.GetVersion().Version),
fmt.Sprintf("%s=%t", PluginMetadataModeEnv, false),
fmt.Sprintf("%s=%t", PluginAutoMTLSEnv, true),
},
),
SecureConfig: &plugin.SecureConfig{
Expand All @@ -290,7 +294,7 @@ func TestMakeConfig(t *testing.T) {
Return(test.responseWrapInfo, test.responseWrapInfoErr)
mockWrapper.On("MlockEnabled").
Return(test.mlockEnabled)
test.rc.wrapper = mockWrapper
test.rc.Wrapper = mockWrapper
defer mockWrapper.AssertNumberOfCalls(t, "ResponseWrapData", test.responseWrapInfoTimes)
defer mockWrapper.AssertNumberOfCalls(t, "MlockEnabled", test.mlockEnabledTimes)

Expand Down Expand Up @@ -318,9 +322,7 @@ func TestMakeConfig(t *testing.T) {
}
config.TLSConfig = nil

if !reflect.DeepEqual(config, test.expectedConfig) {
t.Fatalf("Actual config: %#v\nExpected config: %#v", config, test.expectedConfig)
}
require.Equal(t, config, test.expectedConfig)
})
}
}
Expand Down
2 changes: 0 additions & 2 deletions sdk/helper/pluginutil/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ type PluginClient interface {
plugin.ClientProtocol
}

const MultiplexingCtxKey string = "multiplex_id"

// PluginRunner defines the metadata needed to run a plugin securely with
// go-plugin.
type PluginRunner struct {
Expand Down
20 changes: 11 additions & 9 deletions sdk/plugin/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ var (
// GRPCBackendPlugin is the plugin.Plugin implementation that only supports GRPC
// transport
type GRPCBackendPlugin struct {
Factory logical.Factory
MetadataMode bool
Logger log.Logger
Factory logical.Factory
MetadataMode bool
AutoMTLSSupported bool
Logger log.Logger

// Embeding this will disable the netRPC protocol
plugin.NetRPCUnsupportedPlugin
Expand All @@ -41,12 +42,13 @@ func (b GRPCBackendPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server)

func (b *GRPCBackendPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
ret := &backendGRPCPluginClient{
client: pb.NewBackendClient(c),
clientConn: c,
broker: broker,
cleanupCh: make(chan struct{}),
doneCtx: ctx,
metadataMode: b.MetadataMode,
client: pb.NewBackendClient(c),
clientConn: c,
broker: broker,
cleanupCh: make(chan struct{}),
doneCtx: ctx,
// 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
Expand Down
60 changes: 41 additions & 19 deletions sdk/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"sync"

"github.com/hashicorp/errwrap"
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/pluginutil"
Expand Down Expand Up @@ -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.
// The backend is returned as a logical.Backend interface. The isMetadataMode param determines whether
// 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
pluginRunner, err := sys.LookupPlugin(ctx, pluginName, pluginType)
if err != nil {
Expand All @@ -59,8 +58,16 @@ func NewBackend(ctx context.Context, pluginName string, pluginType consts.Plugin
}
}
} else {
config := pluginutil.PluginClientConfig{
Name: pluginName,
PluginType: pluginType,
Logger: conf.Logger.Named(pluginName),
IsMetadataMode: isMetadataMode,
AutoMTLS: autoMTLS,
Wrapper: sys,
}
// create a backendPluginClient instance
backend, err = NewPluginClient(ctx, sys, pluginRunner, conf.Logger, isMetadataMode)
backend, err = NewPluginClient(ctx, pluginRunner, config)
if err != nil {
return nil, err
}
Expand All @@ -69,34 +76,49 @@ func NewBackend(ctx context.Context, pluginName string, pluginType consts.Plugin
return backend, nil
}

func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginRunner, logger log.Logger, isMetadataMode bool) (logical.Backend, error) {
// pluginMap is the map of plugins we can dispense.
pluginSet := map[int]plugin.PluginSet{
// pluginSet returns the go-plugin PluginSet that we can dispense. This ensures
// that plugins that don't support AutoMTLS are run on the appropriate version.
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
// since it's possible old plugins built against this version will still
// work with gRPC. There is currently no difference between version 3
// and version 4.
3: {
"backend": &GRPCBackendPlugin{
MetadataMode: isMetadataMode,
MetadataMode: metadataMode,
},
},
4: {
"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 {
return nil, err
}
Expand Down Expand Up @@ -126,9 +148,9 @@ func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunne
}

// Wrap the backend in a tracing middleware
if namedLogger.IsTrace() {
if config.Logger.IsTrace() {
backend = &backendTracingMiddleware{
logger: namedLogger.With("transport", transport),
logger: config.Logger.With("transport", transport),
next: backend,
}
}
Expand Down
Loading