Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

address interpolation of artifact and template src/dest #9671

Merged
merged 6 commits into from
Jan 4, 2021

Conversation

cgbaker
Copy link
Contributor

@cgbaker cgbaker commented Dec 18, 2020

refactored #9668 for (imho) better abstraction

Resolves #9610
Resolves #9389
Resolves #6929

The main change is to use client-specific absolute paths in the environment maps for interpolating source for template and destination for template and artifact.

Because of this, it required adjusting the sandbox-escape checks to allow shared alloc dir, in addition to task working dir.

The result is that we should now support source and destination paths of the following forms:

  • well-known interpolated paths: ${NOMAD_ALLOC_DIR}, ${NOMAD_SECRETS_DIR}, ${NOMAD_TASK_DIR}
  • task-dir relative paths: local, secrets, ../alloc

The new e2e test shows the capability that we're targeting.

@cgbaker cgbaker marked this pull request as draft December 18, 2020 11:58
@cgbaker cgbaker changed the title refactor of 9668 WIP: refactor of 9668 Dec 18, 2020
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui December 18, 2020 12:42 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad December 18, 2020 12:42 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui December 18, 2020 13:16 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad December 18, 2020 13:16 Inactive
@cgbaker cgbaker force-pushed the b-9610-template-interpolation-paths-refactor branch from 0962139 to 1215201 Compare December 18, 2020 13:17
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui December 18, 2020 13:17 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad December 18, 2020 13:17 Inactive
@cgbaker cgbaker force-pushed the b-9610-template-interpolation-paths-refactor branch from 1215201 to d286454 Compare December 18, 2020 13:22
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui December 18, 2020 13:22 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad December 18, 2020 13:22 Inactive
// If the interpolated result is a relative path, it is made absolute
// wrt to the task working directory.
// The result is checked to see whether it escapes both the task working
// directory and the shared allocation directory.
Copy link
Contributor Author

@cgbaker cgbaker Dec 18, 2020

Choose a reason for hiding this comment

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

this the result of the refactoring from #9668... it is potentially excessive refactoring. this method provides the functionality that we need for template and artifact path interpolation. namely, using the client-relative environment values to interpolate paths that we're using on the client: paths for consul-template and go-getter.

Copy link
Member

Choose a reason for hiding this comment

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

Whatever we can do to make it a "no brainer" for folks trying to handle client paths safely seems like a good thing to me. So I don't feel like it's excessive to pull this out to a centralized implementation! 😀

nodeAttrs := make(map[string]string)
// buildEnv returns the environment variables and device environment
// variables with respect to the task directories passed in the arguments.
func (b *Builder) buildEnv(allocDir, localDir, secretsDir string,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

there was a lot of interpolation that happened while creating these environment variables, so i did a back-of-the-envelope dependency analysis and broke out the code for creating the env vars and the device env vars into this method.

@vercel vercel bot temporarily deployed to Preview – nomad December 18, 2020 14:01 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui December 18, 2020 14:01 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui December 18, 2020 14:11 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad December 18, 2020 14:11 Inactive
@@ -93,11 +100,10 @@ func setEnvvars(envBuilder *taskenv.Builder, fsi drivers.FSIsolation, taskDir *a
// Set the host environment variables for non-image based drivers
if fsi != drivers.FSIsolationImage {
// COMPAT(1.0) using inclusive language, blacklist is kept for backward compatibility.
denylist := conf.ReadAlternativeDefault(
filter := strings.Split(conf.ReadAlternativeDefault(
Copy link
Contributor Author

@cgbaker cgbaker Dec 18, 2020

Choose a reason for hiding this comment

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

@cgbaker cgbaker changed the title WIP: refactor of 9668 address interpolation of artifact and template src/dest Dec 18, 2020
Copy link
Member

@tgross tgross left a comment

Choose a reason for hiding this comment

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

The overall approach here LGTM. I think this will be a big improvement in terms of comprehensibility for both the operators and us developers.

// If the interpolated result is a relative path, it is made absolute
// wrt to the task working directory.
// The result is checked to see whether it escapes both the task working
// directory and the shared allocation directory.
Copy link
Member

Choose a reason for hiding this comment

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

Whatever we can do to make it a "no brainer" for folks trying to handle client paths safely seems like a good thing to me. So I don't feel like it's excessive to pull this out to a centralized implementation! 😀

// is supported as a destination for artifact and template blocks, and
// that it is properly interpolated for task drivers with varying
// filesystem isolation
func (tc *ConsulTemplateTest) TestTemplatePathInterpolation_SharedAllocDir(f *framework.F) {
Copy link
Member

Choose a reason for hiding this comment

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

This test is great 👍

e2e/consultemplate/consultemplate.go Outdated Show resolved Hide resolved
envMap, deviceEnvs := b.buildEnv(b.allocDir, b.localDir, b.secretsDir, nodeAttrs)
envMapClient, _ := b.buildEnv(b.clientAllocDir, b.clientLocalDir, b.clientSecretsDir, nodeAttrs)

return NewTaskEnv(envMap, envMapClient, deviceEnvs, nodeAttrs, b.clientTaskDir, b.clientAllocDir)
Copy link
Member

Choose a reason for hiding this comment

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

It looks like this is the only non-test caller? I'm wondering if we should make this internal to the package to force test callers to always use the full safe path?

@cgbaker
Copy link
Contributor Author

cgbaker commented Dec 18, 2020

okay, it looks like i've got a problem... there is a use case that i didn't capture in this PR which may send this back to the drawing board:
https://github.com/hashicorp/nomad/pull/9671/files#diff-f40e85b67f576552c2d39989b55c983267bf19524185cc5f60d4716be0f71359R1683-L1686

it looks like the previous behavior of always joining against taskdir meant that absolute paths in the template destination would map to the same absolute path in the container:
https://github.com/hashicorp/nomad/pull/9671/files#diff-90e77cdb4f5439161bf5a053bf2d0b74d3ac3ee65cfce482bbd8e02646a14a64L143
this is because something like ${taskDir}//etc became ${taskDir}/etc which was mounted on top of /

... except for docker, where this only worked if the absolute path was one of the bind-mounted paths:

  • /secrets/...
  • /local/...
    but not /alloc (which would go into {taskdir}/alloc which was not bind-mounted)

so, i need to figure out whether this invalidates the approach or not and deal with it. it may be as simple as handling absolute paths a little better...

Copy link
Member

@schmichael schmichael left a comment

Choose a reason for hiding this comment

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

Not sure I follow the hiccup you've hit. It's definitely awfully confusing as we have 3 "FSIsolation" modes that affect this:

  1. None (raw exec) - ClientPath == TaskPath but behavior is like Chroot (exec) in that you can write anywhere in the task's filesystem.
  2. Chroot (exec/java) - ClientPath != TaskPath. Runtime paths are like Image but you can write to anywhere in the task's filesystem like None (raw exec) above.
  3. Image (docker/podman) - ClientPath != TaskPath. Runtime paths are like Chroot but you can only write to the 3 bind-mounted paths (secrets, local, alloc).

For simplicities sake I think we should always be bind mounting <alloc-dir>/<alloc-id>/alloc to <alloc-dir>/<alloc-id>/<task>/alloc and doing all operations on that path. I'm not sure we ever need to reference the "real" .../alloc.

Image drivers like Docker and Podman should have .../alloc bind-mounted into their container filesystem as well.

I think that double bind mounting (once to create the task directory on the host, and the second time for Image based drivers) should make it so we never need to special case where the "alloc dir" is? It's the same as local and secrets.

While the ability to write anywhere on a Chroot driver's filesystem was likely an unintentional side effect, it should be considered a unique feature at this point distinct from how Image behaves. There's no point in trying to rememdy the discrepancy between the two now. In the future I hope Chroot goes away entirely in favor of Image, but obviously totally removing the Chroot behavior will take a long time.

client/taskenv/env.go Outdated Show resolved Hide resolved
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui December 22, 2020 13:39 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad December 22, 2020 13:39 Inactive
@cgbaker
Copy link
Contributor Author

cgbaker commented Dec 22, 2020

For simplicities sake I think we should always be bind mounting <alloc-dir>/<alloc-id>/alloc to <alloc-dir>/<alloc-id>/<task>/alloc and doing all operations on that path. I'm not sure we ever need to reference the "real" .../alloc.

We used to do this, but (if I'm understanding the PR correctly) we stopped because it required root for the Nomad client (which some docker-only users didn't need/want):
0785109#diff-0626141dbc5bf9ed895f1d35410b05e96fdbe3aaf1f734c2dcdad9b3cab48552R92

@vercel vercel bot temporarily deployed to Preview – nomad December 22, 2020 13:49 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad December 22, 2020 14:26 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui December 22, 2020 14:26 Inactive
@cgbaker
Copy link
Contributor Author

cgbaker commented Dec 22, 2020

rebased against master and re-enabled the disabled test from #9383
ready for review

@cgbaker cgbaker marked this pull request as ready for review December 22, 2020 14:45
Copy link
Member

@schmichael schmichael left a comment

Choose a reason for hiding this comment

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

We used to do this, but [...] we stopped because it required root for the Nomad client (which some docker-only users didn't need/want)

Argh of course, good catch. I even recognize that face on the PR 😬

An alternate fix for #2178 would have been to soft-fail on that linkDir call. I think that would make raw_exec behave more reasonably when Nomad is run as root.

I think this is a better solution all around thouhg? Distinctly rendering client paths vs task paths. Great work.

client/taskenv/env.go Outdated Show resolved Hide resolved
// If joinEscape, an interpolated path that escapes will be joined with the
// task dir.
Copy link
Member

Choose a reason for hiding this comment

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

Can you add an example usage here? Maybe it's the holidays, but I don't want to admit how long I stared at this and the relevant 2 lines of code to figure out what was going on. 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, will do. this is the hiccup that i ran up against, where previous behavior allowed for absolute destination paths for artifact and template:
https://github.com/hashicorp/nomad/pull/9671/files#diff-90e77cdb4f5439161bf5a053bf2d0b74d3ac3ee65cfce482bbd8e02646a14a64L140-L144

i'll add a documenting test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines +818 to +843
func (b *Builder) SetClientSharedAllocDir(dir string) *Builder {
b.mu.Lock()
b.clientSharedAllocDir = dir
b.mu.Unlock()
return b
}

func (b *Builder) SetClientTaskRoot(dir string) *Builder {
b.mu.Lock()
b.clientTaskRoot = dir
b.mu.Unlock()
return b
}

func (b *Builder) SetClientTaskLocalDir(dir string) *Builder {
b.mu.Lock()
b.clientTaskLocalDir = dir
b.mu.Unlock()
return b
}

func (b *Builder) SetClientTaskSecretsDir(dir string) *Builder {
b.mu.Lock()
b.clientTaskSecretsDir = dir
b.mu.Unlock()
return b
}
Copy link
Member

Choose a reason for hiding this comment

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

Since these are reqiured for Build()'s proper function it's a shame we can't enforce those dependencies through the New func or similar, but I can see how that gets awkward given the way Builder's are built up vs where FSIsolation is handled. A problem for another time perhaps.

@hashicorp hashicorp deleted a comment from wirat112535 Jan 4, 2021
@hashicorp hashicorp deleted a comment from wirat112535 Jan 4, 2021
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui January 4, 2021 17:50 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad January 4, 2021 17:50 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad January 4, 2021 18:52 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui January 4, 2021 18:52 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad January 4, 2021 21:05 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui January 4, 2021 21:05 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad January 4, 2021 22:01 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui January 4, 2021 22:01 Inactive
@cgbaker cgbaker force-pushed the b-9610-template-interpolation-paths-refactor branch from e70f08d to 196f550 Compare January 4, 2021 22:26
@vercel vercel bot temporarily deployed to Preview – nomad January 4, 2021 22:26 Inactive
@vercel vercel bot temporarily deployed to Preview – nomad-storybook-and-ui January 4, 2021 22:26 Inactive
@cgbaker cgbaker merged commit 30603ca into master Jan 4, 2021
@cgbaker cgbaker deleted the b-9610-template-interpolation-paths-refactor branch January 4, 2021 23:00
@github-actions
Copy link

github-actions bot commented Dec 5, 2022

I'm going to lock this pull request because it has been closed for 120 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 5, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
3 participants