diff --git a/e2e/consultemplate/consultemplate.go b/e2e/consultemplate/consultemplate.go index aa33f7be9..7bc158282 100644 --- a/e2e/consultemplate/consultemplate.go +++ b/e2e/consultemplate/consultemplate.go @@ -7,10 +7,13 @@ import ( "time" capi "github.com/hashicorp/consul/api" + "github.com/hashicorp/nomad/api" + "github.com/hashicorp/nomad/e2e/e2eutil" e2e "github.com/hashicorp/nomad/e2e/e2eutil" "github.com/hashicorp/nomad/e2e/framework" "github.com/hashicorp/nomad/helper/uuid" "github.com/hashicorp/nomad/jobspec" + "github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/testutil" ) @@ -223,6 +226,67 @@ job: {{ env "NOMAD_JOB_NAME" }} } } +// TestTemplatePathInterpolation_Ok asserts that NOMAD_*_DIR variables are +// properly interpolated into template source and destination paths without +// being treated as escaping. +func (tc *ConsulTemplateTest) TestTemplatePathInterpolation_Ok(f *framework.F) { + jobID := "template-paths-" + uuid.Generate()[:8] + tc.jobIDs = append(tc.jobIDs, jobID) + + allocStubs := e2eutil.RegisterAndWaitForAllocs( + f.T(), tc.Nomad(), "consultemplate/input/template_paths.nomad", jobID, "") + f.Len(allocStubs, 1) + allocID := allocStubs[0].ID + + e2eutil.WaitForAllocRunning(f.T(), tc.Nomad(), allocID) + + f.NoError(waitForTemplateRender(allocID, "task/secrets/foo/dst", + func(out string) bool { + return len(out) > 0 + }, nil), "expected file to have contents") +} + +// TestTemplatePathInterpolation_Bad asserts that template.source paths are not +// allowed to escape the sandbox directory tree by default. +func (tc *ConsulTemplateTest) TestTemplatePathInterpolation_Bad(f *framework.F) { + wc := &e2e.WaitConfig{} + interval, retries := wc.OrDefault() + + jobID := "bad-template-paths-" + uuid.Generate()[:8] + tc.jobIDs = append(tc.jobIDs, jobID) + + allocStubs := e2eutil.RegisterAndWaitForAllocs( + f.T(), tc.Nomad(), "consultemplate/input/bad_template_paths.nomad", jobID, "") + f.Len(allocStubs, 1) + allocID := allocStubs[0].ID + + // Wait for alloc to fail + var err error + var alloc *api.Allocation + testutil.WaitForResultRetries(retries, func() (bool, error) { + time.Sleep(interval) + alloc, _, err = tc.Nomad().Allocations().Info(allocID, nil) + if err != nil { + return false, err + } + + return alloc.ClientStatus == structs.AllocClientStatusFailed, fmt.Errorf("expected status failed, but was: %s", alloc.ClientStatus) + }, func(err error) { + f.T().Fatalf("failed to wait on alloc: %v", err) + }) + + // Assert the "source escapes" error occurred to prevent false + // positives. + found := false + for _, event := range alloc.TaskStates["task"].Events { + if strings.Contains(event.DisplayMessage, "template source path escapes alloc directory") { + found = true + break + } + } + f.True(found, "alloc failed but NOT due to expected source path escape error") +} + // waitForTemplateRender is a helper that grabs a file via alloc fs // and tests it for func waitForTemplateRender(allocID, path string, test func(string) bool, wc *e2e.WaitConfig) error { diff --git a/e2e/consultemplate/input/bad_template_paths.nomad b/e2e/consultemplate/input/bad_template_paths.nomad new file mode 100644 index 000000000..af9adde54 --- /dev/null +++ b/e2e/consultemplate/input/bad_template_paths.nomad @@ -0,0 +1,37 @@ +job "bad-template-paths" { + datacenters = ["dc1", "dc2"] + + constraint { + attribute = "${attr.kernel.name}" + value = "linux" + } + + group "template-paths" { + + restart { + attempts = 0 + mode = "fail" + } + + task "task" { + + driver = "docker" + + config { + image = "busybox:1" + command = "/bin/sh" + args = ["-c", "sleep 300"] + } + + template { + source = "/etc/passwd" + destination = "${NOMAD_SECRETS_DIR}/foo/dst" + } + + resources { + cpu = 128 + memory = 64 + } + } + } +} diff --git a/e2e/consultemplate/input/template_paths.nomad b/e2e/consultemplate/input/template_paths.nomad new file mode 100644 index 000000000..d52e26282 --- /dev/null +++ b/e2e/consultemplate/input/template_paths.nomad @@ -0,0 +1,37 @@ +job "template-paths" { + datacenters = ["dc1", "dc2"] + + constraint { + attribute = "${attr.kernel.name}" + value = "linux" + } + + group "template-paths" { + + task "task" { + + driver = "docker" + + config { + image = "busybox:1" + command = "/bin/sh" + args = ["-c", "sleep 300"] + } + + artifact { + source = "https://google.com" + destination = "local/foo/src" + } + + template { + source = "${NOMAD_TASK_DIR}/foo/src" + destination = "${NOMAD_SECRETS_DIR}/foo/dst" + } + + resources { + cpu = 128 + memory = 64 + } + } + } +}