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

Add ability to provide env vars to plugins #5359

Merged
merged 5 commits into from
Sep 20, 2018
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
2 changes: 1 addition & 1 deletion builtin/logical/database/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func getCluster(t *testing.T) (*vault.TestCluster, logical.SystemView) {
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)

sys := vault.TestDynamicSystemView(cores[0].Core)
vault.TestAddTestPlugin(t, cores[0].Core, "postgresql-database-plugin", "TestBackend_PluginMain")
vault.TestAddTestPlugin(t, cores[0].Core, "postgresql-database-plugin", "TestBackend_PluginMain", []string{}, "")

return cluster, sys
}
Expand Down
4 changes: 2 additions & 2 deletions builtin/logical/database/dbplugin/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ func getCluster(t *testing.T) (*vault.TestCluster, logical.SystemView) {
cores := cluster.Cores

sys := vault.TestDynamicSystemView(cores[0].Core)
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin", "TestPlugin_GRPC_Main")
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin-netRPC", "TestPlugin_NetRPC_Main")
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin", "TestPlugin_GRPC_Main", []string{}, "")
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin-netRPC", "TestPlugin_NetRPC_Main", []string{}, "")

return cluster, sys
}
Expand Down
2 changes: 1 addition & 1 deletion builtin/plugin/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func testConfig(t *testing.T) (*logical.BackendConfig, func()) {

os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)

vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMain")
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMain", []string{}, "")

return config, func() {
cluster.Cleanup()
Expand Down
5 changes: 5 additions & 0 deletions helper/pluginutil/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type PluginRunner struct {
Name string `json:"name" structs:"name"`
Command string `json:"command" structs:"command"`
Args []string `json:"args" structs:"args"`
Env []string `json:"env" structs:"env"`
Sha256 []byte `json:"sha256" structs:"sha256"`
Builtin bool `json:"builtin" structs:"builtin"`
BuiltinFactory func() (interface{}, error) `json:"-" structs:"-"`
Expand All @@ -65,6 +66,10 @@ func (r *PluginRunner) RunMetadataMode(ctx context.Context, wrapper RunnerUtil,

func (r *PluginRunner) runCommon(ctx context.Context, wrapper RunnerUtil, pluginMap map[string]plugin.Plugin, 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
Expand Down
2 changes: 1 addition & 1 deletion http/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func getPluginClusterAndCore(t testing.TB, logger log.Logger) (*vault.TestCluste
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)

vault.TestWaitActive(t, core.Core)
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestPlugin_PluginMain")
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestPlugin_PluginMain", []string{}, "")

// Mount the mock plugin
err = core.Client.Sys().Mount("mock", &api.MountInput{
Expand Down
11 changes: 9 additions & 2 deletions vault/logical_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, req *logi
return logical.ErrorResponse("missing command value"), nil
}

// For backwards compatibility, also accept args as part of command. Don't
// For backwards compatibility, also accept args as part of command. Don't
// accepts args in both command and args.
args := d.Get("args").([]string)
parts := strings.Split(command, " ")
Expand All @@ -281,12 +281,14 @@ func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, req *logi
args = parts[1:]
}

env := d.Get("env").([]string)

sha256Bytes, err := hex.DecodeString(sha256)
if err != nil {
return logical.ErrorResponse("Could not decode SHA-256 value from Hex"), err
}

err = b.Core.pluginCatalog.Set(ctx, pluginName, parts[0], args, sha256Bytes)
err = b.Core.pluginCatalog.Set(ctx, pluginName, parts[0], args, env, sha256Bytes)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -3526,6 +3528,11 @@ plugin directory.`,
`The args passed to plugin command.`,
"",
},
"plugin-catalog_env": {
`The environment variables passed to plugin command.
Each entry is of the form "key=value".`,
"",
},
"leases": {
`View or list lease metadata.`,
`
Expand Down
106 changes: 99 additions & 7 deletions vault/logical_system_integ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ import (
"github.com/hashicorp/vault/vault"
)

const (
expectedEnvKey = "FOO"
expectedEnvValue = "BAR"
)

func TestSystemBackend_Plugin_secret(t *testing.T) {
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical)
defer cluster.Cleanup()
Expand Down Expand Up @@ -103,7 +108,7 @@ func TestSystemBackend_Plugin_MismatchType(t *testing.T) {
core := cluster.Cores[0]

// Replace the plugin with a credential backend
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials")
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", []string{}, "")

// Make a request to lazy load the now-credential plugin
// and expect an error
Expand Down Expand Up @@ -178,7 +183,7 @@ func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMoun
switch btype {
case logical.TypeLogical:
// Add plugin back to the catalog
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical")
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical", []string{}, "")
_, err = core.Client.Logical().Write("sys/mounts/mock-0", map[string]interface{}{
"type": "plugin",
"config": map[string]interface{}{
Expand All @@ -187,7 +192,7 @@ func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMoun
})
case logical.TypeCredential:
// Add plugin back to the catalog
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials")
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", []string{}, "")
_, err = core.Client.Logical().Write("sys/auth/mock-0", map[string]interface{}{
"type": "plugin",
"plugin_name": "mock-plugin",
Expand Down Expand Up @@ -283,9 +288,9 @@ func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatc
// Re-add the plugin to the catalog
switch btype {
case logical.TypeLogical:
vault.TestAddTestPluginTempDir(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical", cluster.TempDir)
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical", []string{}, cluster.TempDir)
case logical.TypeCredential:
vault.TestAddTestPluginTempDir(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", cluster.TempDir)
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", []string{}, cluster.TempDir)
}

// Reload the plugin
Expand Down Expand Up @@ -480,7 +485,7 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo

switch backendType {
case logical.TypeLogical:
vault.TestAddTestPluginTempDir(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical", tempDir)
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical", []string{}, tempDir)
for i := 0; i < numMounts; i++ {
// Alternate input styles for plugin_name on every other mount
options := map[string]interface{}{
Expand All @@ -502,7 +507,7 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo
}
}
case logical.TypeCredential:
vault.TestAddTestPluginTempDir(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", tempDir)
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", []string{}, tempDir)
for i := 0; i < numMounts; i++ {
// Alternate input styles for plugin_name on every other mount
options := map[string]interface{}{
Expand Down Expand Up @@ -530,6 +535,58 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo
return cluster
}

func TestSystemBackend_Plugin_Env(t *testing.T) {
kvPair := fmt.Sprintf("%s=%s", expectedEnvKey, expectedEnvValue)
cluster := testSystemBackend_SingleCluster_Env(t, []string{kvPair})
defer cluster.Cleanup()
}

// testSystemBackend_SingleCluster_Env is a helper func that returns a single
// cluster and a single mounted plugin logical backend.
func testSystemBackend_SingleCluster_Env(t *testing.T, env []string) *vault.TestCluster {
coreConfig := &vault.CoreConfig{
LogicalBackends: map[string]logical.Factory{
"plugin": plugin.Factory,
},
}

// Create a tempdir, cluster.Cleanup will clean up this directory
tempDir, err := ioutil.TempDir("", "vault-test-cluster")
if err != nil {
t.Fatal(err)
}

cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
KeepStandbysSealed: true,
NumCores: 1,
TempDir: tempDir,
})
cluster.Start()

core := cluster.Cores[0]
vault.TestWaitActive(t, core.Core)
client := core.Client

os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)

vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainEnv", env, tempDir)
options := map[string]interface{}{
"type": "plugin",
"plugin_name": "mock-plugin",
}

resp, err := client.Logical().Write("sys/mounts/mock", options)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %v", resp)
}

return cluster
}

func TestBackend_PluginMainLogical(t *testing.T) {
args := []string{}
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" && os.Getenv(pluginutil.PluginMetadataModeEnv) != "true" {
Expand Down Expand Up @@ -588,6 +645,41 @@ func TestBackend_PluginMainCredentials(t *testing.T) {
}
}

// TestBackend_PluginMainEnv is a mock plugin that simply checks for the existence of FOO env var.
func TestBackend_PluginMainEnv(t *testing.T) {
actual := os.Getenv(expectedEnvKey)
if actual != expectedEnvValue {
t.Fatalf("expected: %q, got: %q", expectedEnvValue, actual)
}

args := []string{}
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" && os.Getenv(pluginutil.PluginMetadataModeEnv) != "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 := &pluginutil.APIClientMeta{}
flags := apiClientMeta.FlagSet()
flags.Parse(args)
tlsConfig := apiClientMeta.GetTLSConfig()
tlsProviderFunc := pluginutil.VaultPluginTLSProvider(tlsConfig)

factoryFunc := mock.FactoryType(logical.TypeLogical)

err := lplugin.Serve(&lplugin.ServeOpts{
BackendFactoryFunc: factoryFunc,
TLSProviderFunc: tlsProviderFunc,
})
if err != nil {
t.Fatal(err)
}
}

func TestSystemBackend_InternalUIResultantACL(t *testing.T) {
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
Expand Down
4 changes: 4 additions & 0 deletions vault/logical_system_paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,10 @@ func (b *SystemBackend) pluginsCatalogPath() *framework.Path {
Type: framework.TypeStringSlice,
Description: strings.TrimSpace(sysHelp["plugin-catalog_args"][0]),
},
"env": &framework.FieldSchema{
Type: framework.TypeStringSlice,
Description: strings.TrimSpace(sysHelp["plugin-catalog_env"][0]),
},
},

Callbacks: map[logical.Operation]framework.OperationFunc{
Expand Down
3 changes: 2 additions & 1 deletion vault/plugin_catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (c *PluginCatalog) Get(ctx context.Context, name string) (*pluginutil.Plugi

// Set registers a new external plugin with the catalog, or updates an existing
// external plugin. It takes the name, command and SHA256 of the plugin.
func (c *PluginCatalog) Set(ctx context.Context, name, command string, args []string, sha256 []byte) error {
func (c *PluginCatalog) Set(ctx context.Context, name, command string, args []string, env []string, sha256 []byte) error {
if c.directory == "" {
return ErrDirectoryNotConfigured
}
Expand Down Expand Up @@ -122,6 +122,7 @@ func (c *PluginCatalog) Set(ctx context.Context, name, command string, args []st
Name: name,
Command: command,
Args: args,
Env: env,
Sha256: sha256,
Builtin: false,
}
Expand Down
7 changes: 4 additions & 3 deletions vault/plugin_catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func TestPluginCatalog_CRUD(t *testing.T) {
defer file.Close()

command := fmt.Sprintf("%s", filepath.Base(file.Name()))
err = core.pluginCatalog.Set(context.Background(), "mysql-database-plugin", command, []string{"--test"}, []byte{'1'})
err = core.pluginCatalog.Set(context.Background(), "mysql-database-plugin", command, []string{"--test"}, []string{"FOO=BAR"}, []byte{'1'})
if err != nil {
t.Fatal(err)
}
Expand All @@ -67,6 +67,7 @@ func TestPluginCatalog_CRUD(t *testing.T) {
Name: "mysql-database-plugin",
Command: filepath.Join(sym, filepath.Base(file.Name())),
Args: []string{"--test"},
Env: []string{"FOO=BAR"},
Sha256: []byte{'1'},
Builtin: false,
}
Expand Down Expand Up @@ -141,13 +142,13 @@ func TestPluginCatalog_List(t *testing.T) {
defer file.Close()

command := filepath.Base(file.Name())
err = core.pluginCatalog.Set(context.Background(), "mysql-database-plugin", command, []string{"--test"}, []byte{'1'})
err = core.pluginCatalog.Set(context.Background(), "mysql-database-plugin", command, []string{"--test"}, []string{}, []byte{'1'})
if err != nil {
t.Fatal(err)
}

// Set another plugin
err = core.pluginCatalog.Set(context.Background(), "aaaaaaa", command, []string{"--test"}, []byte{'1'})
err = core.pluginCatalog.Set(context.Background(), "aaaaaaa", command, []string{"--test"}, []string{}, []byte{'1'})
if err != nil {
t.Fatal(err)
}
Expand Down
Loading