Skip to content

client: add extra checks to nomad alloc fs - #28468

Merged
Juanadelacuesta merged 7 commits into
mainfrom
NMD-1605-secrets
Sep 2, 2026
Merged

Juanadelacuesta merged 7 commits into
mainfrom
NMD-1605-secrets

Conversation

@Juanadelacuesta

@Juanadelacuesta Juanadelacuesta commented Aug 31, 2026 •

Copy link
Copy Markdown
Member

This PR addresses 3 bugs:

  • On mac, to keep compatibility, directories like /vars and /tmp are actually symlinks to /private/vars and /private/tmp, If the data directory of the client is in one of this redirected directories , when using nomad alloc fs <alloc id> . the alloc path resolves to /private/var/<data-dir>/..., but the command path does not resolve the symlink, resulting on the requested path being /var/<data-dir>/... which escapes /private/var/<data-dir>/... and the command fails.

  • When a symlink is created from the local task directory into the secrets directory, the security checks are bypassed, and the contents of the secrets directory are visible using nomad alloc fs.

  • The check for unauthorised access to the the secrets and private directories is done by a simple string prefix search and it is not actually preventing any access.

This PR updates the escaping path verification methods to use the os package, which uses both lexical check and resolves any possible symlink.

Description

Testing & Reproduction steps

Links

Contributor Checklist

  • Changelog Entry If this PR changes user-facing behavior, please generate and add a
    changelog entry using the make cl command.
  • Testing Please add tests to cover any new functionality or to demonstrate bug fixes and
    ensure regressions will be caught.
  • Documentation If the change impacts user-facing functionality such as the CLI, API, UI,
    and job configuration, please update the Nomad product documentation, which is stored in the
    web-unified-docs repo. Refer to the web-unified-docs contributor guide for docs guidelines.
    Please also consider whether the change requires notes within the upgrade
    guide
    . If you would like help with the docs, tag the nomad-docs team in this PR.
  • LLM Usage If an LLM was used to generate any code, please ensure and confirm you have read
    and followed the AI usage guide.

Reviewer Checklist

  • Backport Labels Please add the correct backport labels as described by the internal
    backporting document.
  • Commit Type Ensure the correct merge method is selected which should be "squash and merge"
    in the majority of situations. The main exceptions are long-lived feature branches or merges where
    history should be preserved.
  • Enterprise PRs If this is an enterprise only PR, please add any required changelog entry
    within the public repository.
  • If a change needs to be reverted, we will roll out an update to the code within 7 days.

Changes to Security Controls

Are there any changes to security controls (access controls, encryption, logging) in this pull request? If so, explain.

@Juanadelacuesta Juanadelacuesta changed the title func: update alloc dir functions to use os.Root to detect escaping pa… client: add extra checks to nomad alloc fs Sep 1, 2026
@Juanadelacuesta Juanadelacuesta added backport/ent/1.10.x+ent backport to 1.10.x+ent release line backport/ent/1.11.x+ent backport to 1.11.x+ent release line backport/2.0.x backport to 2.0.x release line backport/ent/2.0.x+ent backport to 2.0.x+ent release line and removed backport/ent/2.0.x+ent backport to 2.0.x+ent release line labels Sep 1, 2026
@Juanadelacuesta
Juanadelacuesta marked this pull request as ready for review September 1, 2026 11:44
@Juanadelacuesta
Juanadelacuesta requested review from a team as code owners September 1, 2026 11:44

@tgross tgross left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left a few comments but otherwise LGTM. We should link to the Jira somewhere in the PR description and/or commit message too.

Comment thread client/allocdir/alloc_dir_test.go Outdated
}{
{
name: "empty path",
base: allocDir,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like all the test cases have the same base field value, so we can probably drop that and just use allocDir directly. (Although we're also shadowing the allocDir variable in the test case scope by assigning AllocDir to it)

Comment thread client/allocdir/alloc_dir_test.go Outdated
Comment on lines +433 to +443
// Check it does not access the secrets or private directories
for _, taskDir := range a.TaskDirs {

if err := escapingfs.ChildEscapesParentDir(taskDir.SecretsDir, requestedPath); err == nil {
return "", fmt.Errorf("Reading secret file prohibited: %s", path)
}

if err := escapingfs.ChildEscapesParentDir(taskDir.PrivateDir, requestedPath); err == nil {
return "", fmt.Errorf("Reading secret file prohibited: %s", path)
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original code does a case-insensitive prefix check. Does ChildEscapesParentDir handle that correctly on macOS, which has the awful case-preserving-but-insensitive file system?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requestedPath is partially attacker-controlled and we resolve it to an absolute file path. So an attacker could give us a path to directory like https://go.dev/play/p/sj2v20RAdeD that the OS would treat as inside the secrets dir for purposes of reading the file. So my question is whether ChildEscapesParentDir protects us even if they've done so. I don't have a case-preserving-but-case-insensitive file system to test with.

Comment thread client/allocdir/alloc_dir.go Outdated
Comment on lines +414 to +415
a.mu.RLock()
defer a.mu.RUnlock()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this mutex only needs to guard the TaskDirs field, which has the mutex because we can add task dirs as part of task start later on.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are reading the TaskDirs when checking for access to secrets and private

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but that's in the same block as the loop over TaskDirs on line 424 so we could move this down if we want. (Not really required but we do a syscall in ChildEscapesParentDir so it might be nice not to hold a mutex over the syscall)

Comment thread .changelog/28468.txt Outdated
tgross
tgross previously approved these changes Sep 2, 2026

@tgross tgross left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed offline smoke-testing the behavior of case-insensitive file systems (macOS) but other than that, nothing in my comments is a blocker here so LGTM

Juanadelacuesta and others added 2 commits September 2, 2026 15:44
Co-authored-by: Tim Gross <tim@0x74696d.com>
Co-authored-by: Tim Gross <tim@0x74696d.com>

@tgross tgross left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Juanadelacuesta
Juanadelacuesta merged commit 9ec5a81 into main Sep 2, 2026
56 of 57 checks passed
@Juanadelacuesta
Juanadelacuesta deleted the NMD-1605-secrets branch September 2, 2026 15:59
schmichael pushed a commit to vercel/nomad that referenced this pull request Sep 25, 2026
* func: update alloc dir functions to use os.Root to detect escaping paths and reads into secrets or private directories

* fix: update the tests to make the sanitizePath function a method of AllocDir

* fix: update the error messages

* style: add changelog

* Apply suggestion from @tgross

Co-authored-by: Tim Gross <tim@0x74696d.com>

* Apply suggestion from @tgross

Co-authored-by: Tim Gross <tim@0x74696d.com>

* fix: always use paths in lower case to avoid escapes on OSs that are case sensitive

---------

Co-authored-by: Tim Gross <tim@0x74696d.com>

This branch was successfully deployed

1 active deployment
Preview — 7aefa7ee Deployed Sep 2, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport/ent/1.10.x+ent backport to 1.10.x+ent release line backport/ent/1.11.x+ent backport to 1.11.x+ent release line backport/2.0.x backport to 2.0.x release line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants