mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 18:35:44 +03:00
artifact/template: prevent file sandbox escapes
Ensure that the client honors the client configuration for the `template.disable_file_sandbox` field when validating the jobspec's `template.source` parameter, and not just with consul-template's own `file` function. Prevent interpolated `template.source`, `template.destination`, and `artifact.destination` fields from escaping file sandbox.
This commit is contained in:
@@ -28,10 +28,6 @@ const (
|
||||
// consulTemplateSourceName is the source name when using the TaskHooks.
|
||||
consulTemplateSourceName = "Template"
|
||||
|
||||
// hostSrcOption is the Client option that determines whether the template
|
||||
// source may be from the host
|
||||
hostSrcOption = "template.allow_host_source"
|
||||
|
||||
// missingDepEventLimit is the number of missing dependencies that will be
|
||||
// logged before we switch to showing just the number of missing
|
||||
// dependencies.
|
||||
@@ -549,25 +545,27 @@ func maskProcessEnv(env map[string]string) map[string]string {
|
||||
// parseTemplateConfigs converts the tasks templates in the config into
|
||||
// consul-templates
|
||||
func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[*ctconf.TemplateConfig]*structs.Template, error) {
|
||||
allowAbs := config.ClientConfig.ReadBoolDefault(hostSrcOption, true)
|
||||
sandboxEnabled := !config.ClientConfig.TemplateConfig.DisableSandbox
|
||||
taskEnv := config.EnvBuilder.Build()
|
||||
|
||||
ctmpls := make(map[*ctconf.TemplateConfig]*structs.Template, len(config.Templates))
|
||||
for _, tmpl := range config.Templates {
|
||||
var src, dest string
|
||||
var err error
|
||||
if tmpl.SourcePath != "" {
|
||||
if filepath.IsAbs(tmpl.SourcePath) {
|
||||
if !allowAbs {
|
||||
return nil, fmt.Errorf("Specifying absolute template paths disallowed by client config: %q", tmpl.SourcePath)
|
||||
}
|
||||
|
||||
src = tmpl.SourcePath
|
||||
} else {
|
||||
src = filepath.Join(config.TaskDir, taskEnv.ReplaceEnv(tmpl.SourcePath))
|
||||
src = taskEnv.ReplaceEnv(tmpl.SourcePath)
|
||||
src, err = helper.GetPathInSandbox(config.TaskDir, src)
|
||||
if err != nil && sandboxEnabled {
|
||||
return nil, fmt.Errorf("template source path escapes alloc directory")
|
||||
}
|
||||
}
|
||||
|
||||
if tmpl.DestPath != "" {
|
||||
dest = filepath.Join(config.TaskDir, taskEnv.ReplaceEnv(tmpl.DestPath))
|
||||
dest = taskEnv.ReplaceEnv(tmpl.DestPath)
|
||||
dest, err = helper.GetPathInSandbox(config.TaskDir, dest)
|
||||
if err != nil && sandboxEnabled {
|
||||
return nil, fmt.Errorf("template destination path escapes alloc directory")
|
||||
}
|
||||
}
|
||||
|
||||
ct := ctconf.DefaultTemplateConfig()
|
||||
@@ -577,7 +575,7 @@ func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[*ctconf.Templa
|
||||
ct.LeftDelim = &tmpl.LeftDelim
|
||||
ct.RightDelim = &tmpl.RightDelim
|
||||
ct.FunctionDenylist = config.ClientConfig.TemplateConfig.FunctionDenylist
|
||||
if !config.ClientConfig.TemplateConfig.DisableSandbox {
|
||||
if sandboxEnabled {
|
||||
ct.SandboxPath = &config.TaskDir
|
||||
}
|
||||
|
||||
|
||||
@@ -382,7 +382,11 @@ func TestTaskTemplateManager_HostPath(t *testing.T) {
|
||||
}
|
||||
|
||||
harness := newTestHarness(t, []*structs.Template{template}, false, false)
|
||||
harness.start(t)
|
||||
harness.config.TemplateConfig.DisableSandbox = true
|
||||
err = harness.startWithErr()
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't setup initial harness: %v", err)
|
||||
}
|
||||
defer harness.stop()
|
||||
|
||||
// Wait for the unblock
|
||||
@@ -405,12 +409,46 @@ func TestTaskTemplateManager_HostPath(t *testing.T) {
|
||||
|
||||
// Change the config to disallow host sources
|
||||
harness = newTestHarness(t, []*structs.Template{template}, false, false)
|
||||
harness.config.Options = map[string]string{
|
||||
hostSrcOption: "false",
|
||||
err = harness.startWithErr()
|
||||
if err == nil || !strings.Contains(err.Error(), "escapes alloc directory") {
|
||||
t.Fatalf("Expected absolute template path disallowed for %q: %v",
|
||||
template.SourcePath, err)
|
||||
}
|
||||
if err := harness.startWithErr(); err == nil || !strings.Contains(err.Error(), "absolute") {
|
||||
t.Fatalf("Expected absolute template path disallowed: %v", err)
|
||||
|
||||
template.SourcePath = "../../../../../../" + file
|
||||
harness = newTestHarness(t, []*structs.Template{template}, false, false)
|
||||
err = harness.startWithErr()
|
||||
if err == nil || !strings.Contains(err.Error(), "escapes alloc directory") {
|
||||
t.Fatalf("Expected directory traversal out of %q disallowed for %q: %v",
|
||||
harness.taskDir, template.SourcePath, err)
|
||||
}
|
||||
|
||||
// Build a new task environment
|
||||
a := mock.Alloc()
|
||||
task := a.Job.TaskGroups[0].Tasks[0]
|
||||
task.Name = TestTaskName
|
||||
task.Meta = map[string]string{"ESCAPE": "../"}
|
||||
|
||||
template.SourcePath = "${NOMAD_META_ESCAPE}${NOMAD_META_ESCAPE}${NOMAD_META_ESCAPE}${NOMAD_META_ESCAPE}${NOMAD_META_ESCAPE}${NOMAD_META_ESCAPE}" + file
|
||||
harness = newTestHarness(t, []*structs.Template{template}, false, false)
|
||||
harness.envBuilder = taskenv.NewBuilder(harness.node, a, task, "global")
|
||||
err = harness.startWithErr()
|
||||
if err == nil || !strings.Contains(err.Error(), "escapes alloc directory") {
|
||||
t.Fatalf("Expected directory traversal out of %q via interpolation disallowed for %q: %v",
|
||||
harness.taskDir, template.SourcePath, err)
|
||||
}
|
||||
|
||||
// Test with desination too
|
||||
template.SourcePath = f.Name()
|
||||
template.DestPath = "../../../../../../" + file
|
||||
harness = newTestHarness(t, []*structs.Template{template}, false, false)
|
||||
harness.envBuilder = taskenv.NewBuilder(harness.node, a, task, "global")
|
||||
err = harness.startWithErr()
|
||||
if err == nil || !strings.Contains(err.Error(), "escapes alloc directory") {
|
||||
t.Fatalf("Expected directory traversal out of %q via interpolation disallowed for %q: %v",
|
||||
harness.taskDir, template.SourcePath, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTaskTemplateManager_Unblock_Static(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user