The exec2 task is starting inside the 'private' 'alloc/' dir instead of the exec2 'alloc_mounts/' dir.
As root on nomad test machine with nomad and nomad-driver-exec2 installed via apt. The
exec2 driver is in /opt/nomad/data/plugins via symlink and /usr/bin/ as installed.
data_dir is /opt/nomad-test/data
opt, nomad-test are root:root 755
data is root:root 700
as per: https://developer.hashicorp.com/nomad/docs/deploy/production/requirements#hardening-nomadnomad.hcl:
There must be a config mentioning the plugin so
nomad.hcl:
plugin "nomad-driver-exec2" {}
Start the nomad agent:
nomad agent -dev-connect -plugin-dir=/opt/nomad/data/plugins/ -config=nomad.hcl -data-dir=/opt/nomad-test/data
The job is being used is
exec2test.nomad:
exec2test.nomad:
job "e2job" {
reschedule { # keep the logs cleaner
attempts = 0
unlimited = false
}
type = "service"
group "e2g" {
count = 1
task "e2t" {
restart { # keep the logs cleaner
attempts = 0
}
driver = "exec2"
config {
command = "bash"
args = ["local/run.sh"]
}
template {
destination = "local/run.sh"
perms = "0755"
data = <<EOD
#!/bin/bash
set -x
# Keep all output interleaved :)
exec 2>&1
whoami
env | grep NOMAD_TASK_DIR
env | grep NOMAD_ALLOC_DIR
# This is a veiled directory that we should not (and can't) cd to.
pwd
cd local
pwd
cd ($pwd)
# Traverse a veiled directory (which we don't even have linux permission to enter when traversing from root in a hardened system which is odd) and arrive at an unveiled directory:
cd ../secrets
pwd
# Hang around
sleep 1d
EOD
}
}
}
}
Run the job (purge any existing runs and ensure hardened):
nomad job stop -purge e2job
chmod 700 /opt/nomad-test/data/
nomad job run exec2test.nomad
This succeeds and the nomad logs contain:
[INFO] client.driver_mgr.nomad-driver-exec2: exec2 runner: driver=exec2 oom_score_adj=0 unveil_defaults=true unveil_paths=[rwxc:/opt/nomad-test/alloc_mounts/0a53001c-1d6b-58cf-7556-7d237410a1cd-e2t/local, rwxc:/opt/nomad-test/alloc_mounts/0a53001c-1d6b-58cf-7556-7d237410a1cd-e2t/alloc, rwxc:/opt/nomad-test/alloc_mounts/0a53001c-1d6b-58cf-7556-7d237410a1cd-e2t/secrets, rwxc:/opt/nomad-test/alloc_mounts/0a53001c-1d6b-58cf-7556-7d237410a1cd-e2t/tmp] @module=exec2 args=[local/run.sh] cmd=bash timestamp=2026-03-19T12:46:46.338Z
and the job logs have:
+ whoami
whoami: cannot find name for user ID 80785
+ env
+ grep NOMAD_TASK_DIR
NOMAD_TASK_DIR=/opt/nomad-test/alloc_mounts/0a53001c-1d6b-58cf-7556-7d237410a1cd-e2t/local
+ env
+ grep NOMAD_ALLOC_DIR
NOMAD_ALLOC_DIR=/opt/nomad-test/alloc_mounts/0a53001c-1d6b-58cf-7556-7d237410a1cd-e2t/alloc
+ pwd
/opt/nomad-test/data/alloc/0a53001c-1d6b-58cf-7556-7d237410a1cd/e2t
+ cd local
+ pwd
/opt/nomad-test/data/alloc/0a53001c-1d6b-58cf-7556-7d237410a1cd/e2t/local
++ pwd
+ cd /opt/nomad-test/data/alloc/0a53001c-1d6b-58cf-7556-7d237410a1cd/e2t/local
local/run.sh: line 15: cd: /opt/nomad-test/data/alloc/0a53001c-1d6b-58cf-7556-7d237410a1cd/e2t/local: Permission denied
+ cd ../secrets
+ pwd
/opt/nomad-test/data/alloc/0a53001c-1d6b-58cf-7556-7d237410a1cd/e2t/secrets
+ sleep 1d
I recognise these are bind mounts to inodes that are unveiled and as such there is no security failing.
However having the working directory be a veiled directory feels wrong.
This file: https://github.com/hashicorp/nomad-driver-exec2/blob/main/pkg/shim/shim.go#L357
Says:
cmd.Dir = e.env.TaskDir
which is not the same as:
cmd.Dir = filepath.Dir(e.env.Env["NOMAD_TASK_DIR"])
Which is used at https://github.com/hashicorp/nomad-driver-exec2/blob/main/pkg/shim/shim.go#L265 to find the directory to make the relative tmp/ dir
https://developer.hashicorp.com/nomad/docs/reference/runtime-environment-settings#task-directories
Says:
"The allocation working directory contains a task working directory for each task".
Now I understood this to be the linux "current working directory" (and for the other drivers this is the case).
Then it says: "relative to the task working directory" ... "alloc/" "local/" "secrets/"
Then it says that the path of "alloc/" is in NOMAD_ALLOC_DIR which finally pins us down to the exec2 task's value which we see in the output above.
Working back from NOMAD_ALLOC_DIR suggests the "task working directory" should be filepath.Dir(e.env.Env["NOMAD_TASK_DIR"]) and that it would be both consistent with the docs and less surprising.
I do note that:
hashicorp/nomad#8919 says
"The NOMAD_TASK_DIR is the local/ directory provided to each task, whereas the "task directory" is the directory that Nomad uses to set up the task, and may not be the actual filesystem of the task, "
If I patch the driver the output is:
+ whoami
whoami: cannot find name for user ID 89368
+ env
+ grep NOMAD_TASK_DIR
NOMAD_TASK_DIR=/opt/nomad-test/alloc_mounts/05762172-b640-dd44-0dbb-5d7e8678e6ed-e2t/local
+ env
+ grep NOMAD_ALLOC_DIR
NOMAD_ALLOC_DIR=/opt/nomad-test/alloc_mounts/05762172-b640-dd44-0dbb-5d7e8678e6ed-e2t/alloc
+ pwd
/opt/nomad-test/alloc_mounts/05762172-b640-dd44-0dbb-5d7e8678e6ed-e2t
+ cd local
+ pwd
/opt/nomad-test/alloc_mounts/05762172-b640-dd44-0dbb-5d7e8678e6ed-e2t/local
++ pwd
+ cd /opt/nomad-test/alloc_mounts/05762172-b640-dd44-0dbb-5d7e8678e6ed-e2t/local
+ cd ../secrets
+ pwd
/opt/nomad-test/alloc_mounts/05762172-b640-dd44-0dbb-5d7e8678e6ed-e2t/secrets
+ sleep 1d
and cd $(pwd) is no longer an error :)
I'd be happy to submit the tiny PR if this is deemed correct.
The exec2 task is starting inside the 'private' 'alloc/' dir instead of the exec2 'alloc_mounts/' dir.
As root on nomad test machine with nomad and nomad-driver-exec2 installed via apt. The
exec2 driver is in /opt/nomad/data/plugins via symlink and /usr/bin/ as installed.
data_dir is /opt/nomad-test/data
opt, nomad-test are root:root 755
data is root:root 700
as per: https://developer.hashicorp.com/nomad/docs/deploy/production/requirements#hardening-nomadnomad.hcl:
There must be a config mentioning the plugin so
nomad.hcl:
Start the nomad agent:
The job is being used is
exec2test.nomad:
Run the job (purge any existing runs and ensure hardened):
This succeeds and the nomad logs contain:
and the job logs have:
I recognise these are bind mounts to inodes that are unveiled and as such there is no security failing.
However having the working directory be a veiled directory feels wrong.
This file: https://github.com/hashicorp/nomad-driver-exec2/blob/main/pkg/shim/shim.go#L357
Says:
cmd.Dir = e.env.TaskDirwhich is not the same as:
cmd.Dir = filepath.Dir(e.env.Env["NOMAD_TASK_DIR"])Which is used at https://github.com/hashicorp/nomad-driver-exec2/blob/main/pkg/shim/shim.go#L265 to find the directory to make the relative tmp/ dir
https://developer.hashicorp.com/nomad/docs/reference/runtime-environment-settings#task-directories
Says:
"The allocation working directory contains a task working directory for each task".
Now I understood this to be the linux "current working directory" (and for the other drivers this is the case).
Then it says: "relative to the task working directory" ... "alloc/" "local/" "secrets/"
Then it says that the path of "alloc/" is in NOMAD_ALLOC_DIR which finally pins us down to the exec2 task's value which we see in the output above.
Working back from NOMAD_ALLOC_DIR suggests the "task working directory" should be
filepath.Dir(e.env.Env["NOMAD_TASK_DIR"])and that it would be both consistent with the docs and less surprising.I do note that:
hashicorp/nomad#8919 says
"The NOMAD_TASK_DIR is the local/ directory provided to each task, whereas the "task directory" is the directory that Nomad uses to set up the task, and may not be the actual filesystem of the task, "
If I patch the driver the output is:
and
cd $(pwd)is no longer an error :)I'd be happy to submit the tiny PR if this is deemed correct.