{"record":{"id":"773bc68ac875b0bc","repo":"hashicorp/nomad","slug":"template-destination-path-escapes-alloc-directory","errorCode":null,"errorMessage":"template destination path escapes alloc directory","messagePattern":"template destination path escapes alloc directory","errorType":"exception","errorClass":"destEscapesErr","httpStatus":null,"severity":"error","filePath":"client/allocrunner/taskrunner/template/template.go","lineNumber":52,"sourceCode":")\n\nconst (\n\t// consulTemplateSourceName is the source name when using the TaskHooks.\n\tconsulTemplateSourceName = \"Template\"\n\n\t// missingDepEventLimit is the number of missing dependencies that will be\n\t// logged before we switch to showing just the number of missing\n\t// dependencies.\n\tmissingDepEventLimit = 3\n\n\t// DefaultMaxTemplateEventRate is the default maximum rate at which a\n\t// template event should be fired.\n\tDefaultMaxTemplateEventRate = 3 * time.Second\n)\n\nvar (\n\tsourceEscapesErr = errors.New(\"template source path escapes alloc directory\")\n\tdestEscapesErr   = errors.New(\"template destination path escapes alloc directory\")\n)\n\n// TaskTemplateManager is used to run a set of templates for a given task\ntype TaskTemplateManager struct {\n\t// config holds the template managers configuration\n\tconfig *TaskTemplateManagerConfig\n\n\t// lookup allows looking up the set of Nomad templates by their consul-template ID\n\tlookup map[string][]*structs.Template\n\n\t// runner is the consul-template runner\n\trunner *manager.Runner\n\n\t// signals is a lookup map from the string representation of a signal to its\n\t// actual signal\n\tsignals map[string]os.Signal\n\n\t// shutdownCh is used to signal and started goroutine to shutdown","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocrunner/taskrunner/template/template.go#L34-L70","documentation":"Companion to the source sandbox check: Nomad's template manager rejects a template whose destination path resolves outside the allocation directory when sandboxing is enabled. taskEnv.ClientPath(tmpl.DestPath, true) computes the path; if it escapes and sandboxEnabled, parseTemplateConfigs returns this sentinel error. It prevents templates from writing to arbitrary host locations.","triggerScenarios":"A task template block sets destination to an absolute path (e.g. /etc/nginx/nginx.conf) or a ../-relative path that resolves outside the alloc dir while the client's template sandbox is enabled.","commonSituations":"Jobs carried over from pre-sandbox Nomad that wrote rendered files to host paths; using ${NOMAD_META...} interpolation accidentally producing an absolute path; typos like destination = \"local/../local/../out\" escaping the dir.","solutions":["Set destination to a path inside the task directory, e.g. local/rendered.conf or NAMESPACE-relative paths","Use ${NOMAD_TASK_DIR} or ${NOMAD_SECRETS_DIR} interpolations instead of absolute host paths","If another process needs the file at a host path, use a script/task that copies it rather than the template destination","As a last resort, disable the template sandbox in client config (security trade-off)"],"exampleFix":"// before\ntemplate {\n  data        = \"...\"\n  destination = \"/etc/nginx/nginx.conf\" // escapes alloc dir\n}\n// after\ntemplate {\n  data        = \"...\"\n  destination = \"local/nginx.conf\"\n}","handlingStrategy":"validation","validationCode":"// check the template destination stays inside the alloc dir before submit\ndst := filepath.Clean(tmpl.DestPath)\nif filepath.IsAbs(dst) || strings.HasPrefix(dst, \"..\") {\n    return fmt.Errorf(\"template destination %q must be inside the alloc dir\", dst)\n}","typeGuard":null,"tryCatchPattern":"_, err := mgr.ParseConfigs()\nif err != nil {\n    if errors.Is(err, destEscapesErr) {\n        return fmt.Errorf(\"use local/ or NAMESPACE-relative destination: %w\", err)\n    }\n    return err\n}","preventionTips":["Always use local/, secrets/, or ${NOMAD_TASK_DIR}/${NOMAD_SECRETS_DIR} for destinations","Avoid absolute host paths in template destination","Clean/normalize interpolated destination paths before submission","Test jobs in a sandboxed dev cluster to catch escape paths early"],"tags":["nomad","template","path-escape","security-sandbox"],"backgroundTag":"path-escapes-alloc-directory","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}