{"record":{"id":"048c8707e44d40f1","repo":"hashicorp/nomad","slug":"template-source-path-escapes-alloc-directory","errorCode":null,"errorMessage":"template source path escapes alloc directory","messagePattern":"template source path escapes alloc directory","errorType":"exception","errorClass":"sourceEscapesErr","httpStatus":null,"severity":"error","filePath":"client/allocrunner/taskrunner/template/template.go","lineNumber":51,"sourceCode":"\tstructsc \"github.com/hashicorp/nomad/nomad/structs/config\"\n)\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","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocrunner/taskrunner/template/template.go#L33-L69","documentation":"Nomad's template manager rejects a template whose source_path resolves outside the allocation directory when template sandboxing is enabled. taskEnv.ClientPath computes the allocation-relative path and reports whether it escapes; if escapes && sandboxEnabled, parseTemplateConfigs returns this package-level sentinel error. It protects against templates reading arbitrary files on the client host.","triggerScenarios":"A task template block sets source to an absolute path or a ../-relative path that resolves outside the alloc dir, and the client has template sandboxing enabled (consul.template.template_sandbox enabled / nomad >= some version default).","commonSituations":"Migrating jobs from older Nomad where reading host paths via template source was allowed; referencing a shared file like /etc/ssl/certs/ca.pem as template source; accidental ../ in source paths.","solutions":["Move the template source file into the task's alloc dir (e.g. use an artifact block to fetch it) and reference it relatively","Reference files already staged in the task dir, e.g. NAMESPACE/... via ${NOMAD_TASK_DIR} style relative paths","If the host file is truly required, mount/ship it via artifact or template with content inline instead of source","Only as a last resort, disable the template sandbox in client config (understood security trade-off)"],"exampleFix":"// before\ntemplate {\n  source = \"/etc/consul.d/config.hcl\" // escapes alloc dir\n  destination = \"local/config.hcl\"\n}\n// after\nartifact {\n  source      = \"https://example.internal/config.hcl\"\n  destination = \"local/config.hcl\"\n}\ntemplate {\n  source      = \"local/config.hcl\"\n  destination = \"local/rendered.hcl\"\n}","handlingStrategy":"validation","validationCode":"// check the template source stays inside the alloc dir before submit\nsrc := filepath.Clean(tmpl.SourcePath)\nif filepath.IsAbs(src) || strings.HasPrefix(src, \"..\") {\n    return fmt.Errorf(\"template source %q must be inside the alloc dir\", src)\n}","typeGuard":null,"tryCatchPattern":"_, err := mgr.ParseConfigs()\nif err != nil {\n    if errors.Is(err, sourceEscapesErr) {\n        return fmt.Errorf(\"fix template source to an alloc-dir-relative path: %w\", err)\n    }\n    return err\n}","preventionTips":["Keep template sources under the task dir or fetch them with artifact blocks","Never reference absolute host paths in template source","Run 'nomad job validate' which surfaces sandbox violations on recent clients","Enable sandbox in staging to catch jobs that rely on host path reads"],"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-08T15:18:49.778Z"}