mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
secrets: pass key/value config data to plugins as env (#26455)
Co-authored-by: Michael Schurter <mschurter@hashicorp.com> Co-authored-by: Tim Gross <tgross@hashicorp.com>
This commit is contained in:
@@ -198,7 +198,7 @@ func (h *secretsHook) buildSecretProviders(secretDir string) ([]TemplateProvider
|
||||
tmplProvider = append(tmplProvider, p)
|
||||
}
|
||||
default:
|
||||
plug, err := commonplugins.NewExternalSecretsPlugin(h.clientConfig.CommonPluginDir, s.Provider)
|
||||
plug, err := commonplugins.NewExternalSecretsPlugin(h.clientConfig.CommonPluginDir, s.Provider, s.Env)
|
||||
if err != nil {
|
||||
multierror.Append(mErr, err)
|
||||
continue
|
||||
|
||||
@@ -41,9 +41,16 @@ type externalSecretsPlugin struct {
|
||||
|
||||
// pluginPath is the path on the host to the plugin executable
|
||||
pluginPath string
|
||||
|
||||
// env is optional envVars passed to the plugin process
|
||||
env map[string]string
|
||||
}
|
||||
|
||||
func NewExternalSecretsPlugin(commonPluginDir string, name string) (*externalSecretsPlugin, error) {
|
||||
// NewExternalSecretsPlugin creates an instance of a secrets plugin by validating the plugin
|
||||
// binary exists and is executable, and parsing any string key/value pairs out of the config
|
||||
// which will be used as environment variables for Fetch.
|
||||
func NewExternalSecretsPlugin(commonPluginDir string, name string, env map[string]string) (*externalSecretsPlugin, error) {
|
||||
// validate plugin
|
||||
executable := filepath.Join(commonPluginDir, SecretsPluginDir, name)
|
||||
f, err := os.Stat(executable)
|
||||
if err != nil {
|
||||
@@ -58,6 +65,7 @@ func NewExternalSecretsPlugin(commonPluginDir string, name string) (*externalSec
|
||||
|
||||
return &externalSecretsPlugin{
|
||||
pluginPath: executable,
|
||||
env: env,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -96,6 +104,10 @@ func (e *externalSecretsPlugin) Fetch(ctx context.Context, path string) (*Secret
|
||||
"CPI_OPERATION=fetch",
|
||||
}
|
||||
|
||||
for env, val := range e.env {
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", env, val))
|
||||
}
|
||||
|
||||
stdout, stderr, err := runPlugin(cmd, SecretsKillTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -21,7 +21,7 @@ func TestExternalSecretsPlugin_Fingerprint(t *testing.T) {
|
||||
t.Run("runs successfully", func(t *testing.T) {
|
||||
pluginDir, pluginName := setupTestPlugin(t, fmt.Appendf([]byte{}, "#!/bin/sh\ncat <<EOF\n%s\nEOF\n", `{"type": "secrets", "version": "1.0.0"}`))
|
||||
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName)
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName, nil)
|
||||
must.NoError(t, err)
|
||||
|
||||
res, err := plugin.Fingerprint(context.Background())
|
||||
@@ -34,7 +34,7 @@ func TestExternalSecretsPlugin_Fingerprint(t *testing.T) {
|
||||
t.Run("errors on non-zero exit code", func(t *testing.T) {
|
||||
pluginDir, pluginName := setupTestPlugin(t, fmt.Append([]byte{}, "#!/bin/sh\nexit 1\n"))
|
||||
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName)
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName, nil)
|
||||
must.NoError(t, err)
|
||||
|
||||
res, err := plugin.Fingerprint(context.Background())
|
||||
@@ -45,7 +45,7 @@ func TestExternalSecretsPlugin_Fingerprint(t *testing.T) {
|
||||
t.Run("errors on timeout", func(t *testing.T) {
|
||||
pluginDir, pluginName := setupTestPlugin(t, fmt.Appendf([]byte{}, "#!/bin/sh\nleep .5\n"))
|
||||
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName)
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName, nil)
|
||||
must.NoError(t, err)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
@@ -58,7 +58,7 @@ func TestExternalSecretsPlugin_Fingerprint(t *testing.T) {
|
||||
t.Run("errors on invalid json", func(t *testing.T) {
|
||||
pluginDir, pluginName := setupTestPlugin(t, fmt.Append([]byte{}, "#!/bin/sh\ncat <<EOF\ninvalid\nEOF\n"))
|
||||
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName)
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName, nil)
|
||||
must.NoError(t, err)
|
||||
|
||||
res, err := plugin.Fingerprint(context.Background())
|
||||
@@ -73,7 +73,7 @@ func TestExternalSecretsPlugin_Fetch(t *testing.T) {
|
||||
t.Run("runs successfully", func(t *testing.T) {
|
||||
pluginDir, pluginName := setupTestPlugin(t, fmt.Appendf([]byte{}, "#!/bin/sh\ncat <<EOF\n%s\nEOF\n", `{"result": {"key": "value"}}`))
|
||||
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName)
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName, nil)
|
||||
must.NoError(t, err)
|
||||
|
||||
res, err := plugin.Fetch(context.Background(), "test-path")
|
||||
@@ -86,7 +86,7 @@ func TestExternalSecretsPlugin_Fetch(t *testing.T) {
|
||||
t.Run("errors on non-zero exit code", func(t *testing.T) {
|
||||
pluginDir, pluginName := setupTestPlugin(t, fmt.Append([]byte{}, "#!/bin/sh\nexit 1\n"))
|
||||
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName)
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName, nil)
|
||||
must.NoError(t, err)
|
||||
|
||||
_, err = plugin.Fetch(context.Background(), "test-path")
|
||||
@@ -96,7 +96,7 @@ func TestExternalSecretsPlugin_Fetch(t *testing.T) {
|
||||
t.Run("errors on timeout", func(t *testing.T) {
|
||||
pluginDir, pluginName := setupTestPlugin(t, fmt.Append([]byte{}, "#!/bin/sh\nsleep .5\n"))
|
||||
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName)
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName, nil)
|
||||
must.NoError(t, err)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
@@ -109,12 +109,24 @@ func TestExternalSecretsPlugin_Fetch(t *testing.T) {
|
||||
t.Run("errors on timeout", func(t *testing.T) {
|
||||
pluginDir, pluginName := setupTestPlugin(t, fmt.Appendf([]byte{}, "#!/bin/sh\ncat <<EOF\n%s\nEOF\n", `invalid`))
|
||||
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName)
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName, nil)
|
||||
must.NoError(t, err)
|
||||
|
||||
_, err = plugin.Fetch(context.Background(), "dummy-path")
|
||||
must.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("can be passed environment variables via config", func(t *testing.T) {
|
||||
// test the passed envVar is parsed and set correctly by printing it as part of the SecretResponse
|
||||
pluginDir, pluginName := setupTestPlugin(t, fmt.Appendf([]byte{}, "#!/bin/sh\ncat <<EOF\n%s\nEOF\n", `{"result": {"foo": "$TEST_KEY"}}`))
|
||||
|
||||
plugin, err := NewExternalSecretsPlugin(pluginDir, pluginName, map[string]string{"TEST_KEY": "TEST_VALUE"})
|
||||
must.NoError(t, err)
|
||||
|
||||
res, err := plugin.Fetch(context.Background(), "dummy-path")
|
||||
must.NoError(t, err)
|
||||
must.Eq(t, res.Result, map[string]string{"foo": "TEST_VALUE"})
|
||||
})
|
||||
}
|
||||
|
||||
func setupTestPlugin(t *testing.T, b []byte) (string, string) {
|
||||
|
||||
@@ -53,7 +53,7 @@ func (s *SecretsPluginFingerprint) Fingerprint(request *FingerprintRequest, resp
|
||||
// map of plugin names to fingerprinted versions
|
||||
plugins := map[string]string{}
|
||||
for name := range files {
|
||||
plug, err := commonplugins.NewExternalSecretsPlugin(request.Config.CommonPluginDir, name)
|
||||
plug, err := commonplugins.NewExternalSecretsPlugin(request.Config.CommonPluginDir, name, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user