Skip to content

Commit e823830

Browse files
Mahmood Alitgross
authored andcommitted
Render consul templates using task env only (#6055)
When rendering a task consul template, ensure that only task environment variables are used. Currently, `consul-template` always falls back to host process environment variables when key isn't a task env var[1]. Thus, we add an empty entry for each host process env-var not found in task env-vars. [1] https://github.com/hashicorp/consul-template/blob/bfa5d0e133688920afd1e012404f765182e3d5e0/template/funcs.go#L61-L75
1 parent 6c4863c commit e823830

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

client/allocrunner/taskrunner/template/template.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,8 +508,12 @@ func templateRunner(config *TaskTemplateManagerConfig) (
508508
return nil, nil, err
509509
}
510510

511-
// Set Nomad's environment variables
512-
runner.Env = config.EnvBuilder.Build().All()
511+
// Set Nomad's environment variables.
512+
// consul-template falls back to the host process environment if a
513+
// variable isn't explicitly set in the configuration, so we need
514+
// to mask the environment out to ensure only the task env vars are
515+
// available.
516+
runner.Env = maskProcessEnv(config.EnvBuilder.Build().All())
513517

514518
// Build the lookup
515519
idMap := runner.TemplateConfigMapping()
@@ -525,6 +529,20 @@ func templateRunner(config *TaskTemplateManagerConfig) (
525529
return runner, lookup, nil
526530
}
527531

532+
// maskProcessEnv masks away any environment variable not found in task env.
533+
// It manipulates the parameter directly and returns it without copying.
534+
func maskProcessEnv(env map[string]string) map[string]string {
535+
procEnvs := os.Environ()
536+
for _, e := range procEnvs {
537+
ekv := strings.SplitN(e, "=", 2)
538+
if _, ok := env[ekv[0]]; !ok {
539+
env[ekv[0]] = ""
540+
}
541+
}
542+
543+
return env
544+
}
545+
528546
// parseTemplateConfigs converts the tasks templates in the config into
529547
// consul-templates
530548
func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[ctconf.TemplateConfig]*structs.Template, error) {

client/allocrunner/taskrunner/template/template_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/hashicorp/nomad/client/config"
1717
"github.com/hashicorp/nomad/client/taskenv"
1818
"github.com/hashicorp/nomad/helper"
19+
"github.com/hashicorp/nomad/helper/uuid"
1920
"github.com/hashicorp/nomad/nomad/mock"
2021
"github.com/hashicorp/nomad/nomad/structs"
2122
sconfig "github.com/hashicorp/nomad/nomad/structs/config"
@@ -1022,6 +1023,52 @@ func TestTaskTemplateManager_Signal_Error(t *testing.T) {
10221023
require.Contains(harness.mockHooks.KillEvent.DisplayMessage, "failed to send signals")
10231024
}
10241025

1026+
// TestTaskTemplateManager_FiltersProcessEnvVars asserts that we only render
1027+
// environment variables found in task env-vars and not read the nomad host
1028+
// process environment variables. nomad host process environment variables
1029+
// are to be treated the same as not found environment variables.
1030+
func TestTaskTemplateManager_FiltersEnvVars(t *testing.T) {
1031+
t.Parallel()
1032+
1033+
defer os.Setenv("NOMAD_TASK_NAME", os.Getenv("NOMAD_TASK_NAME"))
1034+
os.Setenv("NOMAD_TASK_NAME", "should be overridden by task")
1035+
1036+
testenv := "TESTENV_" + strings.ReplaceAll(uuid.Generate(), "-", "")
1037+
os.Setenv(testenv, "MY_TEST_VALUE")
1038+
defer os.Unsetenv(testenv)
1039+
1040+
// Make a template that will render immediately
1041+
content := `Hello Nomad Task: {{env "NOMAD_TASK_NAME"}}
1042+
TEST_ENV: {{ env "` + testenv + `" }}
1043+
TEST_ENV_NOT_FOUND: {{env "` + testenv + `_NOTFOUND" }}`
1044+
expected := fmt.Sprintf("Hello Nomad Task: %s\nTEST_ENV: \nTEST_ENV_NOT_FOUND: ", TestTaskName)
1045+
1046+
file := "my.tmpl"
1047+
template := &structs.Template{
1048+
EmbeddedTmpl: content,
1049+
DestPath: file,
1050+
ChangeMode: structs.TemplateChangeModeNoop,
1051+
}
1052+
1053+
harness := newTestHarness(t, []*structs.Template{template}, false, false)
1054+
harness.start(t)
1055+
defer harness.stop()
1056+
1057+
// Wait for the unblock
1058+
select {
1059+
case <-harness.mockHooks.UnblockCh:
1060+
case <-time.After(time.Duration(5*testutil.TestMultiplier()) * time.Second):
1061+
require.Fail(t, "Task unblock should have been called")
1062+
}
1063+
1064+
// Check the file is there
1065+
path := filepath.Join(harness.taskDir, file)
1066+
raw, err := ioutil.ReadFile(path)
1067+
require.NoError(t, err)
1068+
1069+
require.Equal(t, expected, string(raw))
1070+
}
1071+
10251072
// TestTaskTemplateManager_Env asserts templates with the env flag set are read
10261073
// into the task's environment.
10271074
func TestTaskTemplateManager_Env(t *testing.T) {

0 commit comments

Comments
 (0)