fix: tasks are started with a cwd in a veiled directory - #97
Conversation
| // the environment has already been set for us by the exec2 driver; | ||
| // NOMAD_WORK_DIR is set to work_dir if configured, otherwise NOMAD_TASK_DIR | ||
| cmd := exec.Command(cmdpath, commands[1:]...) | ||
| cmd.Dir = os.Getenv("NOMAD_WORK_DIR") |
There was a problem hiding this comment.
This is the main fix.
| // set the working directory; defaults to NOMAD_TASK_DIR when not overridden | ||
| if workDir != "" { | ||
| env["NOMAD_WORK_DIR"] = workDir | ||
| } else { | ||
| env["NOMAD_WORK_DIR"] = env["NOMAD_TASK_DIR"] | ||
| } |
There was a problem hiding this comment.
This is fine but I'm beginning to think we have a large set of configuration options we're trying to pass thru the shim. Maybe we should think about generating a config file that the shim loads? That's how runc works.
We'd need to work out how we'd introduce that across task driver version upgrades, but maybe the shim for existing tasks doesn't care?
There was a problem hiding this comment.
Maybe we should think about generating a config file that the shim loads? We'd need to work out how we'd introduce that across task driver version upgrades
Yeah got this. Shall I keep the config-file refactor in this PR, or should I create a separate issue for it? I think it might require some additional effort and testing, so a follow-up might make more sense.
There was a problem hiding this comment.
I would definitely have that as a follow-up. It's a major architectural change.
Fixes: #83
Summary
A task started by exec2 ran with its working directory set to the Nomad client's private internal alloc directory (
data/alloc/<id>/<task>) rather than the unveiled bind-mount path ($NOMAD_TASK_DIR). Under Linux Landlock LSM, that private path is not in the unveil list, so any operation that reconstructs the CWD as a path string —pwd,cd $(pwd), relative file writes — either produced the wrong path or failed with Permission denied on hardened systems.The fix explicitly sets
cmd.Diron the inner task process to$NOMAD_TASK_DIR, matching the behaviour of Nomad's built-in raw_exec and exec drivers. An optionalwork_dirtask config field is also added for consistency with those drivers.Root Cause
Two-process chain
The exec2 driver runs tasks through a two-process chain. Both processes need their working directory set explicitly:
What was missing
prepare()in shim.go setcmd.Dir = e.env.TaskDir(the private path) for the outer shim process.z_shim_cmd.gobuilt the inner task exec.Command with nocmd.Dirat all — so the task inherited whatever CWD the shim had after Landlock lockdown.After lockdown() applied Landlock restrictions, the kernel could no longer traverse ancestor directories of the inherited CWD to reconstruct the path string, because those ancestors (data/alloc/…) were not in the unveil list. Only the bind-mount paths (
$NOMAD_TASK_DIR, $NOMAD_ALLOC_DIR…) were unveiled.Additional Improvement — work_dir task config field
As per discussion in the issue, exec2 should be consistent with Nomad's built-in drivers. All of
raw_exec,exec, andjavaexpose a work_dir task config field that overrides the default CWD. This PR adds the same.Testing
Details
Result Before:
Result After Fix:
Result Berfore:
/usr/bin/sh: 1: cannot create output.txt: Permission deniedResult After Fix:
Result:
Result:
Changes to Security Controls
Are there any changes to security controls (access controls, encryption, logging) in this pull request? If so, explain.