Skip to content

Commit cfb2bbc

Browse files
secrets: add job id and namespace to plugin env (#27207)
1 parent 0ed7c55 commit cfb2bbc

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

.changelog/27207.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:improvement
2+
secrets: Adds nomad job ID and namespace to plugin environment
3+
```

client/allocrunner/taskrunner/secrets_hook.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ type secretsHookConfig struct {
4949

5050
// nomadNamespace is the job's Nomad namespace
5151
nomadNamespace string
52+
53+
// jobId is the ID of the job
54+
jobId string
5255
}
5356

5457
type secretsHook struct {
@@ -70,6 +73,9 @@ type secretsHook struct {
7073
// nomadNamespace is the job's Nomad namespace
7174
nomadNamespace string
7275

76+
// jobId is the nomad job's ID
77+
jobId string
78+
7379
// secrets to be fetched and populated for interpolation
7480
secrets []*structs.Secret
7581
}
@@ -82,6 +88,7 @@ func newSecretsHook(conf *secretsHookConfig, secrets []*structs.Secret) *secrets
8288
clientConfig: conf.clientConfig,
8389
envBuilder: conf.envBuilder,
8490
nomadNamespace: conf.nomadNamespace,
91+
jobId: conf.jobId,
8592
secrets: secrets,
8693
}
8794
}
@@ -198,6 +205,9 @@ func (h *secretsHook) buildSecretProviders(secretDir string) ([]TemplateProvider
198205
tmplProvider = append(tmplProvider, p)
199206
}
200207
default:
208+
// Add/overwrite the nomad namespace and jobID envVars
209+
s.Env = h.setupPluginEnv(s.Env)
210+
201211
plug, err := commonplugins.NewExternalSecretsPlugin(h.clientConfig.CommonPluginDir, s.Provider, s.Env)
202212
if err != nil {
203213
multierror.Append(mErr, err)
@@ -209,3 +219,15 @@ func (h *secretsHook) buildSecretProviders(secretDir string) ([]TemplateProvider
209219

210220
return tmplProvider, pluginProvider, mErr.ErrorOrNil()
211221
}
222+
223+
func (h *secretsHook) setupPluginEnv(env map[string]string) map[string]string {
224+
if env == nil {
225+
env = make(map[string]string)
226+
}
227+
228+
// set jobID and namespace, overwriting anything already set
229+
env[taskenv.JobID] = h.jobId
230+
env[taskenv.Namespace] = h.nomadNamespace
231+
232+
return env
233+
}

client/allocrunner/taskrunner/secrets_hook_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"fmt"
99
"net/http"
1010
"net/http/httptest"
11+
"os"
12+
"path/filepath"
1113
"strconv"
1214
"testing"
1315
"time"
@@ -317,3 +319,92 @@ func TestSecretsHook_Prestart_Vault(t *testing.T) {
317319

318320
must.Eq(t, exp, taskEnv.Build().TaskSecrets)
319321
}
322+
323+
func TestSecretsHook_Prestart_Plugin(t *testing.T) {
324+
basePlugin := `#!/bin/bash
325+
if [ "$1" = "fingerprint" ]; then
326+
cat <<EOF
327+
{
328+
"type": "secrets",
329+
"version": "0.0.1"
330+
}
331+
EOF
332+
elif [ "$1" = "fetch" ]; then
333+
cat <<EOF
334+
{
335+
"result": {
336+
%s
337+
}
338+
}
339+
EOF
340+
fi`
341+
342+
t.Run("sets plugin environment correctly", func(t *testing.T) {
343+
clientConfig := config.DefaultConfig()
344+
clientConfig.CommonPluginDir = t.TempDir()
345+
346+
pluginDir := filepath.Join(clientConfig.CommonPluginDir, "secrets")
347+
err := os.MkdirAll(pluginDir, 0755)
348+
must.NoError(t, err)
349+
350+
pluginPath := filepath.Join(pluginDir, "test")
351+
testPlugin := fmt.Sprintf(basePlugin, `
352+
"jobID": "${NOMAD_JOB_ID}",
353+
"namespace": "${NOMAD_NAMESPACE}"`)
354+
err = os.WriteFile(pluginPath, []byte(testPlugin), 0755)
355+
must.NoError(t, err)
356+
357+
taskDir := t.TempDir()
358+
alloc := mock.MinAlloc()
359+
task := alloc.Job.TaskGroups[0].Tasks[0]
360+
361+
taskEnv := taskenv.NewBuilder(mock.Node(), alloc, task, clientConfig.Region)
362+
conf := &secretsHookConfig{
363+
logger: testlog.HCLogger(t),
364+
lifecycle: trtesting.NewMockTaskHooks(),
365+
events: &trtesting.MockEmitter{},
366+
clientConfig: clientConfig,
367+
envBuilder: taskEnv,
368+
nomadNamespace: "test-namespace",
369+
jobId: "test-jobid",
370+
}
371+
secretHook := newSecretsHook(conf, []*structs.Secret{
372+
{
373+
Name: "test_secret0",
374+
Provider: "test",
375+
Path: "/test/path",
376+
Env: map[string]string{
377+
"NOMAD_NAMESPACE": "incorrect",
378+
"NOMAD_JOB_ID": "also-incorrect",
379+
},
380+
},
381+
{
382+
Name: "test_secret1",
383+
Provider: "test",
384+
Path: "/test/path",
385+
},
386+
})
387+
388+
// Start template hook with a timeout context to ensure it exists.
389+
req := &interfaces.TaskPrestartRequest{
390+
Alloc: alloc,
391+
Task: task,
392+
TaskDir: &allocdir.TaskDir{Dir: taskDir, SecretsDir: taskDir},
393+
}
394+
395+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
396+
t.Cleanup(cancel)
397+
398+
err = secretHook.Prestart(ctx, req, &interfaces.TaskPrestartResponse{})
399+
must.NoError(t, err)
400+
401+
exp := map[string]string{
402+
"secret.test_secret0.jobID": "test-jobid",
403+
"secret.test_secret0.namespace": "test-namespace",
404+
"secret.test_secret1.jobID": "test-jobid",
405+
"secret.test_secret1.namespace": "test-namespace",
406+
}
407+
408+
must.Eq(t, exp, taskEnv.Build().TaskSecrets)
409+
})
410+
}

client/allocrunner/taskrunner/task_runner_hooks.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ func (tr *TaskRunner) initHooks() {
8989
clientConfig: tr.clientConfig,
9090
envBuilder: tr.envBuilder,
9191
nomadNamespace: tr.alloc.Job.Namespace,
92+
jobId: tr.alloc.Job.ID,
9293
}, task.Secrets))
9394
}
9495

0 commit comments

Comments
 (0)