diff --git a/client/allocrunner/taskrunner/template/template.go b/client/allocrunner/taskrunner/template/template.go index d0b2d1a4b43f..fe1338c40b9c 100644 --- a/client/allocrunner/taskrunner/template/template.go +++ b/client/allocrunner/taskrunner/template/template.go @@ -508,8 +508,12 @@ func templateRunner(config *TaskTemplateManagerConfig) ( return nil, nil, err } - // Set Nomad's environment variables - runner.Env = config.EnvBuilder.Build().All() + // Set Nomad's environment variables. + // consul-template falls back to the host process environment if a + // variable isn't explicitly set in the configuration, so we need + // to mask the environment out to ensure only the task env vars are + // available. + runner.Env = maskProcessEnv(config.EnvBuilder.Build().All()) // Build the lookup idMap := runner.TemplateConfigMapping() @@ -525,6 +529,20 @@ func templateRunner(config *TaskTemplateManagerConfig) ( return runner, lookup, nil } +// maskProcessEnv masks away any environment variable not found in task env. +// It manipulates the parameter directly and returns it without copying. +func maskProcessEnv(env map[string]string) map[string]string { + procEnvs := os.Environ() + for _, e := range procEnvs { + ekv := strings.SplitN(e, "=", 2) + if _, ok := env[ekv[0]]; !ok { + env[ekv[0]] = "" + } + } + + return env +} + // parseTemplateConfigs converts the tasks templates in the config into // consul-templates func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[ctconf.TemplateConfig]*structs.Template, error) { diff --git a/client/allocrunner/taskrunner/template/template_test.go b/client/allocrunner/taskrunner/template/template_test.go index db8c1ba01c9d..bf834f512c59 100644 --- a/client/allocrunner/taskrunner/template/template_test.go +++ b/client/allocrunner/taskrunner/template/template_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/taskenv" "github.com/hashicorp/nomad/helper" + "github.com/hashicorp/nomad/helper/uuid" "github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/nomad/structs" sconfig "github.com/hashicorp/nomad/nomad/structs/config" @@ -1022,6 +1023,52 @@ func TestTaskTemplateManager_Signal_Error(t *testing.T) { require.Contains(harness.mockHooks.KillEvent.DisplayMessage, "failed to send signals") } +// TestTaskTemplateManager_FiltersProcessEnvVars asserts that we only render +// environment variables found in task env-vars and not read the nomad host +// process environment variables. nomad host process environment variables +// are to be treated the same as not found environment variables. +func TestTaskTemplateManager_FiltersEnvVars(t *testing.T) { + t.Parallel() + + defer os.Setenv("NOMAD_TASK_NAME", os.Getenv("NOMAD_TASK_NAME")) + os.Setenv("NOMAD_TASK_NAME", "should be overridden by task") + + testenv := "TESTENV_" + strings.ReplaceAll(uuid.Generate(), "-", "") + os.Setenv(testenv, "MY_TEST_VALUE") + defer os.Unsetenv(testenv) + + // Make a template that will render immediately + content := `Hello Nomad Task: {{env "NOMAD_TASK_NAME"}} +TEST_ENV: {{ env "` + testenv + `" }} +TEST_ENV_NOT_FOUND: {{env "` + testenv + `_NOTFOUND" }}` + expected := fmt.Sprintf("Hello Nomad Task: %s\nTEST_ENV: \nTEST_ENV_NOT_FOUND: ", TestTaskName) + + file := "my.tmpl" + template := &structs.Template{ + EmbeddedTmpl: content, + DestPath: file, + ChangeMode: structs.TemplateChangeModeNoop, + } + + harness := newTestHarness(t, []*structs.Template{template}, false, false) + harness.start(t) + defer harness.stop() + + // Wait for the unblock + select { + case <-harness.mockHooks.UnblockCh: + case <-time.After(time.Duration(5*testutil.TestMultiplier()) * time.Second): + require.Fail(t, "Task unblock should have been called") + } + + // Check the file is there + path := filepath.Join(harness.taskDir, file) + raw, err := ioutil.ReadFile(path) + require.NoError(t, err) + + require.Equal(t, expected, string(raw)) +} + // TestTaskTemplateManager_Env asserts templates with the env flag set are read // into the task's environment. func TestTaskTemplateManager_Env(t *testing.T) {