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

Secret dir materialized in alloc/task directory #1681

Merged
merged 2 commits into from
Sep 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions client/allocdir/alloc_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ var (
// regardless of driver.
TaskLocal = "local"

// TaskSecrets is the the name of the secret directory inside each task
// directory
TaskSecrets = "secrets"

// TaskDirs is the set of directories created in each tasks directory.
TaskDirs = []string{"tmp"}
)
Expand Down Expand Up @@ -154,6 +158,14 @@ func (d *AllocDir) UnmountAll() error {
}
}

taskSecret := filepath.Join(dir, TaskSecrets)
if d.pathExists(taskSecret) {
if err := d.removeSecretDir(taskSecret); err != nil {
mErr.Errors = append(mErr.Errors,
fmt.Errorf("failed to remove the secret dir %q: %v", taskSecret, err))
}
}

// Unmount dev/ and proc/ have been mounted.
d.unmountSpecialDirs(dir)
}
Expand Down Expand Up @@ -223,6 +235,16 @@ func (d *AllocDir) Build(tasks []*structs.Task) error {
return err
}
}

// Create the secret directory
secret := filepath.Join(taskDir, TaskSecrets)
if err := d.createSecretDir(secret); err != nil {
return err
}

if err := d.dropDirPermissions(secret); err != nil {
return err
}
}

return nil
Expand Down
11 changes: 11 additions & 0 deletions client/allocdir/alloc_dir_darwin.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package allocdir

import (
"os"
"syscall"
)

Expand All @@ -14,6 +15,16 @@ func (d *AllocDir) unmountSharedDir(dir string) error {
return syscall.Unlink(dir)
}

// createSecretDir creates the secrets dir folder at the given path
func (d *AllocDir) createSecretDir(dir string) error {
return os.MkdirAll(dir, 0777)
}

// removeSecretDir removes the secrets dir folder
func (d *AllocDir) removeSecretDir(dir string) error {
return os.RemoveAll(dir)
}

// MountSpecialDirs mounts the dev and proc file system on the chroot of the
// task. It's a no-op on darwin.
func (d *AllocDir) MountSpecialDirs(taskDir string) error {
Expand Down
11 changes: 11 additions & 0 deletions client/allocdir/alloc_dir_freebsd.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package allocdir

import (
"os"
"syscall"
)

Expand All @@ -14,6 +15,16 @@ func (d *AllocDir) unmountSharedDir(dir string) error {
return syscall.Unlink(dir)
}

// createSecretDir creates the secrets dir folder at the given path
func (d *AllocDir) createSecretDir(dir string) error {
return os.MkdirAll(dir, 0777)
}

// removeSecretDir removes the secrets dir folder
func (d *AllocDir) removeSecretDir(dir string) error {
return os.RemoveAll(dir)
}

// MountSpecialDirs mounts the dev and proc file system on the chroot of the
// task. It's a no-op on FreeBSD right now.
func (d *AllocDir) MountSpecialDirs(taskDir string) error {
Expand Down
37 changes: 37 additions & 0 deletions client/allocdir/alloc_dir_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ import (
"path/filepath"
"syscall"

"golang.org/x/sys/unix"

"github.com/hashicorp/go-multierror"
)

const (
// secretDirTmpfsSize is the size of the tmpfs per task in MBs
secretDirTmpfsSize = 1
)

// Bind mounts the shared directory into the task directory. Must be root to
// run.
func (d *AllocDir) mountSharedDir(taskDir string) error {
Expand All @@ -23,6 +30,36 @@ func (d *AllocDir) unmountSharedDir(dir string) error {
return syscall.Unmount(dir, 0)
}

// createSecretDir creates the secrets dir folder at the given path using a
// tmpfs
func (d *AllocDir) createSecretDir(dir string) error {
// Only mount the tmpfs if we are root
if unix.Geteuid() == 0 {
if err := os.MkdirAll(dir, 0777); err != nil {
return err
}

var flags uintptr
flags = syscall.MS_NOEXEC
options := fmt.Sprintf("size=%dm", secretDirTmpfsSize)
err := syscall.Mount("tmpfs", dir, "tmpfs", flags, options)
return os.NewSyscallError("mount", err)
}

return os.MkdirAll(dir, 0777)
}

// createSecretDir removes the secrets dir folder
func (d *AllocDir) removeSecretDir(dir string) error {
if unix.Geteuid() == 0 {
if err := syscall.Unmount(dir, 0); err != nil {
return os.NewSyscallError("unmount", err)
}
}

return os.RemoveAll(dir)
}

// MountSpecialDirs mounts the dev and proc file system from the host to the
// chroot
func (d *AllocDir) MountSpecialDirs(taskDir string) error {
Expand Down
4 changes: 4 additions & 0 deletions client/allocdir/alloc_dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ func TestAllocDir_BuildAlloc(t *testing.T) {
if _, err := os.Stat(tDir); os.IsNotExist(err) {
t.Fatalf("Build(%v) didn't create TaskDir %v", tasks, tDir)
}

if _, err := os.Stat(filepath.Join(tDir, TaskSecrets)); os.IsNotExist(err) {
t.Fatalf("Build(%v) didn't create secret dir %v", tasks)
}
}
}

Expand Down
10 changes: 8 additions & 2 deletions client/allocdir/alloc_dir_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ import (
)

var (
//Path inside container for mounted directory shared across tasks in a task group.
// SharedAllocContainerPath is the path inside container for mounted
// directory shared across tasks in a task group.
SharedAllocContainerPath = filepath.Join("/", SharedAllocName)

//Path inside container for mounted directory for local storage.
// TaskLocalContainer is the path inside a container for mounted directory
// for local storage.
TaskLocalContainerPath = filepath.Join("/", TaskLocal)

// TaskSecretsContainerPath is the path inside a container for mounted
// secrets directory
TaskSecretsContainerPath = filepath.Join("/", TaskSecrets)
)

func (d *AllocDir) linkOrCopy(src, dst string, perm os.FileMode) error {
Expand Down
20 changes: 18 additions & 2 deletions client/allocdir/alloc_dir_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ import (
)

var (
//Path inside container for mounted directory that is shared across tasks in a task group.
// SharedAllocContainerPath is the path inside container for mounted
// directory shared across tasks in a task group.
SharedAllocContainerPath = filepath.Join("c:\\", SharedAllocName)

//Path inside container for mounted directory for local storage.
// TaskLocalContainer is the path inside a container for mounted directory
// for local storage.
TaskLocalContainerPath = filepath.Join("c:\\", TaskLocal)

// TaskSecretsContainerPath is the path inside a container for mounted
// secrets directory
TaskSecretsContainerPath = filepath.Join("c:\\", TaskSecrets)
)

func (d *AllocDir) linkOrCopy(src, dst string, perm os.FileMode) error {
Expand All @@ -23,6 +29,16 @@ func (d *AllocDir) mountSharedDir(dir string) error {
return errors.New("Mount on Windows not supported.")
}

// createSecretDir creates the secrets dir folder at the given path
func (d *AllocDir) createSecretDir(dir string) error {
return os.MkdirAll(dir, 0777)
}

// removeSecretDir removes the secrets dir folder
func (d *AllocDir) removeSecretDir(dir string) error {
return os.RemoveAll(dir)
}

// The windows version does nothing currently.
func (d *AllocDir) dropDirPermissions(path string) error {
return nil
Expand Down
18 changes: 18 additions & 0 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,14 @@ func TestClient_SaveRestoreState(t *testing.T) {
}, func(err error) {
t.Fatalf("err: %v", err)
})

// Destroy all the allocations
c2.allocLock.Lock()
for _, ar := range c2.allocs {
ar.Destroy()
<-ar.WaitCh()
}
c2.allocLock.Unlock()
}

func TestClient_Init(t *testing.T) {
Expand Down Expand Up @@ -608,6 +616,7 @@ func TestClient_BlockedAllocations(t *testing.T) {
c1 := testClient(t, func(c *config.Config) {
c.RPCHandler = s1
})
defer c1.Shutdown()

// Wait for the node to be ready
state := s1.State()
Expand Down Expand Up @@ -691,4 +700,13 @@ func TestClient_BlockedAllocations(t *testing.T) {
}, func(err error) {
t.Fatalf("err: %v", err)
})

// Destroy all the allocations
c1.allocLock.Lock()
for _, ar := range c1.allocs {
ar.Destroy()
<-ar.WaitCh()
}
c1.allocLock.Unlock()

}
1 change: 1 addition & 0 deletions client/driver/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task,
// Set environment variables.
d.taskEnv.SetAllocDir(allocdir.SharedAllocContainerPath)
d.taskEnv.SetTaskLocalDir(allocdir.TaskLocalContainerPath)
d.taskEnv.SetTaskLocalDir(allocdir.TaskSecretsContainerPath)

config := &docker.Config{
Image: driverConfig.ImageName,
Expand Down
3 changes: 2 additions & 1 deletion client/driver/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ func dockerSetup(t *testing.T, task *structs.Task) (*docker.Client, DriverHandle

// This test should always pass, even if docker daemon is not available
func TestDockerDriver_Fingerprint(t *testing.T) {
driverCtx, _ := testDriverContexts(&structs.Task{Name: "foo", Resources: basicResources})
driverCtx, execCtx := testDriverContexts(&structs.Task{Name: "foo", Resources: basicResources})
defer execCtx.AllocDir.Destroy()
d := NewDockerDriver(driverCtx)
node := &structs.Node{
Attributes: make(map[string]string),
Expand Down
1 change: 1 addition & 0 deletions client/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ func GetTaskEnv(allocDir *allocdir.AllocDir, node *structs.Node,
}

env.SetTaskLocalDir(filepath.Join(taskdir, allocdir.TaskLocal))
env.SetSecretDir(filepath.Join(taskdir, allocdir.TaskSecrets))
}

if task.Resources != nil {
Expand Down
18 changes: 18 additions & 0 deletions client/driver/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ const (
// removed.
TaskLocalDir = "NOMAD_TASK_DIR"

// SecretDir is the environment variable with the path to the tasks secret
// directory where it can store sensitive data.
SecretDir = "NOMAD_SECRET_DIR"

// MemLimit is the environment variable with the tasks memory limit in MBs.
MemLimit = "NOMAD_MEMORY_LIMIT"

Expand Down Expand Up @@ -79,6 +83,7 @@ type TaskEnvironment struct {
JobMeta map[string]string
AllocDir string
TaskDir string
SecretDir string
CpuLimit int
MemLimit int
TaskName string
Expand Down Expand Up @@ -153,6 +158,9 @@ func (t *TaskEnvironment) Build() *TaskEnvironment {
if t.TaskDir != "" {
t.TaskEnv[TaskLocalDir] = t.TaskDir
}
if t.SecretDir != "" {
t.TaskEnv[SecretDir] = t.SecretDir
}

// Build the resource limits
if t.MemLimit != 0 {
Expand Down Expand Up @@ -249,6 +257,16 @@ func (t *TaskEnvironment) ClearTaskLocalDir() *TaskEnvironment {
return t
}

func (t *TaskEnvironment) SetSecretDir(dir string) *TaskEnvironment {
t.SecretDir = dir
return t
}

func (t *TaskEnvironment) ClearSecretDir() *TaskEnvironment {
Copy link
Member

Choose a reason for hiding this comment

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

Why have setters for public fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some do more

func (t *TaskEnvironment) AppendHostEnvvars(filter []string) *TaskEnvironment {

t.SecretDir = ""
return t
}

func (t *TaskEnvironment) SetMemLimit(limit int) *TaskEnvironment {
t.MemLimit = limit
return t
Expand Down
3 changes: 2 additions & 1 deletion client/driver/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ func TestExecDriver_Fingerprint(t *testing.T) {
Name: "foo",
Resources: structs.DefaultResources(),
}
driverCtx, _ := testDriverContexts(task)
driverCtx, execCtx := testDriverContexts(task)
defer execCtx.AllocDir.Destroy()
d := NewExecDriver(driverCtx)
node := &structs.Node{
Attributes: map[string]string{
Expand Down
18 changes: 17 additions & 1 deletion client/driver/executor/executor_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,23 @@ func TestExecutor_IsolationAndConstraints(t *testing.T) {
t.Fatalf("file %v hasn't been removed", memLimits)
}

expected := "/:\nalloc/\nbin/\ndev/\netc/\nlib/\nlib64/\nlocal/\nproc/\ntmp/\nusr/\n\n/etc/:\nld.so.cache\nld.so.conf\nld.so.conf.d/"
expected := `/:
alloc/
bin/
dev/
etc/
lib/
lib64/
local/
proc/
secrets/
tmp/
usr/
/etc/:
ld.so.cache
ld.so.conf
ld.so.conf.d/`
file := filepath.Join(ctx.AllocDir.LogDir(), "web.stdout.0")
output, err := ioutil.ReadFile(file)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion client/driver/java_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ func TestJavaDriver_Fingerprint(t *testing.T) {
Name: "foo",
Resources: structs.DefaultResources(),
}
driverCtx, _ := testDriverContexts(task)
driverCtx, execCtx := testDriverContexts(task)
defer execCtx.AllocDir.Destroy()
d := NewJavaDriver(driverCtx)
node := &structs.Node{
Attributes: map[string]string{
Expand Down
3 changes: 2 additions & 1 deletion client/driver/qemu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ func TestQemuDriver_Fingerprint(t *testing.T) {
Name: "foo",
Resources: structs.DefaultResources(),
}
driverCtx, _ := testDriverContexts(task)
driverCtx, execCtx := testDriverContexts(task)
defer execCtx.AllocDir.Destroy()
d := NewQemuDriver(driverCtx)
node := &structs.Node{
Attributes: make(map[string]string),
Expand Down
3 changes: 2 additions & 1 deletion client/driver/raw_exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ func TestRawExecDriver_Fingerprint(t *testing.T) {
Name: "foo",
Resources: structs.DefaultResources(),
}
driverCtx, _ := testDriverContexts(task)
driverCtx, execCtx := testDriverContexts(task)
defer execCtx.AllocDir.Destroy()
d := NewRawExecDriver(driverCtx)
node := &structs.Node{
Attributes: make(map[string]string),
Expand Down
1 change: 1 addition & 0 deletions client/driver/rkt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ func TestRktTaskValidate(t *testing.T) {
"dns_servers": []string{"8.8.8.8", "8.8.4.4"},
"dns_search_domains": []string{"example.com", "example.org", "example.net"},
},
Resources: basicResources,
}
driverCtx, execCtx := testDriverContexts(task)
defer execCtx.AllocDir.Destroy()
Expand Down
Loading