Skip to content

Commit 4bae547

Browse files
committed
Secret dir materialized in alloc/task directory
1 parent 56b2b36 commit 4bae547

19 files changed

+179
-10
lines changed

client/allocdir/alloc_dir.go

+22
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ var (
4646
// regardless of driver.
4747
TaskLocal = "local"
4848

49+
// TaskSecrets is the the name of the secret directory inside each task
50+
// directory
51+
TaskSecrets = "secrets"
52+
4953
// TaskDirs is the set of directories created in each tasks directory.
5054
TaskDirs = []string{"tmp"}
5155
)
@@ -154,6 +158,14 @@ func (d *AllocDir) UnmountAll() error {
154158
}
155159
}
156160

161+
taskSecret := filepath.Join(dir, TaskSecrets)
162+
if d.pathExists(taskSecret) {
163+
if err := d.removeSecretDir(taskSecret); err != nil {
164+
mErr.Errors = append(mErr.Errors,
165+
fmt.Errorf("failed to remove the secret dir %q: %v", taskSecret, err))
166+
}
167+
}
168+
157169
// Unmount dev/ and proc/ have been mounted.
158170
d.unmountSpecialDirs(dir)
159171
}
@@ -223,6 +235,16 @@ func (d *AllocDir) Build(tasks []*structs.Task) error {
223235
return err
224236
}
225237
}
238+
239+
// Create the secret directory
240+
secret := filepath.Join(taskDir, TaskSecrets)
241+
if err := d.createSecretDir(secret); err != nil {
242+
return err
243+
}
244+
245+
if err := d.dropDirPermissions(secret); err != nil {
246+
return err
247+
}
226248
}
227249

228250
return nil

client/allocdir/alloc_dir_darwin.go

+11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package allocdir
22

33
import (
4+
"os"
45
"syscall"
56
)
67

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

18+
// createSecretDir creates the secrets dir folder at the given path
19+
func (d *AllocDir) createSecretDir(dir string) error {
20+
return os.MkdirAll(dir, 0777)
21+
}
22+
23+
// removeSecretDir removes the secrets dir folder
24+
func (d *AllocDir) removeSecretDir(dir string) error {
25+
return os.RemoveAll(dir)
26+
}
27+
1728
// MountSpecialDirs mounts the dev and proc file system on the chroot of the
1829
// task. It's a no-op on darwin.
1930
func (d *AllocDir) MountSpecialDirs(taskDir string) error {

client/allocdir/alloc_dir_freebsd.go

+11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package allocdir
22

33
import (
4+
"os"
45
"syscall"
56
)
67

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

18+
// createSecretDir creates the secrets dir folder at the given path
19+
func (d *AllocDir) createSecretDir(dir string) error {
20+
return os.MkdirAll(dir, 0777)
21+
}
22+
23+
// removeSecretDir removes the secrets dir folder
24+
func (d *AllocDir) removeSecretDir(dir string) error {
25+
return os.RemoveAll(dir)
26+
}
27+
1728
// MountSpecialDirs mounts the dev and proc file system on the chroot of the
1829
// task. It's a no-op on FreeBSD right now.
1930
func (d *AllocDir) MountSpecialDirs(taskDir string) error {

client/allocdir/alloc_dir_linux.go

+37
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@ import (
66
"path/filepath"
77
"syscall"
88

9+
"golang.org/x/sys/unix"
10+
911
"github.com/hashicorp/go-multierror"
1012
)
1113

14+
const (
15+
// secretDirTmpfsSize is the size of the tmpfs per task in MBs
16+
secretDirTmpfsSize = 1
17+
)
18+
1219
// Bind mounts the shared directory into the task directory. Must be root to
1320
// run.
1421
func (d *AllocDir) mountSharedDir(taskDir string) error {
@@ -23,6 +30,36 @@ func (d *AllocDir) unmountSharedDir(dir string) error {
2330
return syscall.Unmount(dir, 0)
2431
}
2532

33+
// createSecretDir creates the secrets dir folder at the given path using a
34+
// tmpfs
35+
func (d *AllocDir) createSecretDir(dir string) error {
36+
// Only mount the tmpfs if we are root
37+
if unix.Geteuid() == 0 {
38+
if err := os.MkdirAll(dir, 0777); err != nil {
39+
return err
40+
}
41+
42+
var flags uintptr
43+
flags = syscall.MS_NOEXEC
44+
options := fmt.Sprintf("size=%dm", secretDirTmpfsSize)
45+
err := syscall.Mount("tmpfs", dir, "tmpfs", flags, options)
46+
return os.NewSyscallError("mount", err)
47+
}
48+
49+
return os.MkdirAll(dir, 0777)
50+
}
51+
52+
// createSecretDir removes the secrets dir folder
53+
func (d *AllocDir) removeSecretDir(dir string) error {
54+
if unix.Geteuid() == 0 {
55+
if err := syscall.Unmount(dir, 0); err != nil {
56+
return err
57+
}
58+
}
59+
60+
return os.RemoveAll(dir)
61+
}
62+
2663
// MountSpecialDirs mounts the dev and proc file system from the host to the
2764
// chroot
2865
func (d *AllocDir) MountSpecialDirs(taskDir string) error {

client/allocdir/alloc_dir_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ func TestAllocDir_BuildAlloc(t *testing.T) {
7373
if _, err := os.Stat(tDir); os.IsNotExist(err) {
7474
t.Fatalf("Build(%v) didn't create TaskDir %v", tasks, tDir)
7575
}
76+
77+
if _, err := os.Stat(filepath.Join(tDir, TaskSecrets)); os.IsNotExist(err) {
78+
t.Fatalf("Build(%v) didn't create secret dir %v", tasks)
79+
}
7680
}
7781
}
7882

client/allocdir/alloc_dir_unix.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,17 @@ import (
1414
)
1515

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

20-
//Path inside container for mounted directory for local storage.
21+
// TaskLocalContainer is the path inside a container for mounted directory
22+
// for local storage.
2123
TaskLocalContainerPath = filepath.Join("/", TaskLocal)
24+
25+
// TaskSecretsContainerPath is the path inside a container for mounted
26+
// secrets directory
27+
TaskSecretsContainerPath = filepath.Join("/", TaskSecrets)
2228
)
2329

2430
func (d *AllocDir) linkOrCopy(src, dst string, perm os.FileMode) error {

client/allocdir/alloc_dir_windows.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@ import (
77
)
88

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

13-
//Path inside container for mounted directory for local storage.
14+
// TaskLocalContainer is the path inside a container for mounted directory
15+
// for local storage.
1416
TaskLocalContainerPath = filepath.Join("c:\\", TaskLocal)
17+
18+
// TaskSecretsContainerPath is the path inside a container for mounted
19+
// secrets directory
20+
TaskSecretsContainerPath = filepath.Join("c:\\", TaskSecrets)
1521
)
1622

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

32+
// createSecretDir creates the secrets dir folder at the given path
33+
func (d *AllocDir) createSecretDir(dir string) error {
34+
return os.MkdirAll(dir, 0777)
35+
}
36+
37+
// removeSecretDir removes the secrets dir folder
38+
func (d *AllocDir) removeSecretDir(dir string) error {
39+
return os.RemoveAll(dir)
40+
}
41+
2642
// The windows version does nothing currently.
2743
func (d *AllocDir) dropDirPermissions(path string) error {
2844
return nil

client/client_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,14 @@ func TestClient_SaveRestoreState(t *testing.T) {
575575
}, func(err error) {
576576
t.Fatalf("err: %v", err)
577577
})
578+
579+
// Destroy all the allocations
580+
c2.allocLock.Lock()
581+
for _, ar := range c2.allocs {
582+
ar.Destroy()
583+
<-ar.WaitCh()
584+
}
585+
c2.allocLock.Unlock()
578586
}
579587

580588
func TestClient_Init(t *testing.T) {
@@ -608,6 +616,7 @@ func TestClient_BlockedAllocations(t *testing.T) {
608616
c1 := testClient(t, func(c *config.Config) {
609617
c.RPCHandler = s1
610618
})
619+
defer c1.Shutdown()
611620

612621
// Wait for the node to be ready
613622
state := s1.State()
@@ -691,4 +700,13 @@ func TestClient_BlockedAllocations(t *testing.T) {
691700
}, func(err error) {
692701
t.Fatalf("err: %v", err)
693702
})
703+
704+
// Destroy all the allocations
705+
c1.allocLock.Lock()
706+
for _, ar := range c1.allocs {
707+
ar.Destroy()
708+
<-ar.WaitCh()
709+
}
710+
c1.allocLock.Unlock()
711+
694712
}

client/driver/docker.go

+1
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ func (d *DockerDriver) createContainer(ctx *ExecContext, task *structs.Task,
381381
// Set environment variables.
382382
d.taskEnv.SetAllocDir(allocdir.SharedAllocContainerPath)
383383
d.taskEnv.SetTaskLocalDir(allocdir.TaskLocalContainerPath)
384+
d.taskEnv.SetTaskLocalDir(allocdir.TaskSecretsContainerPath)
384385

385386
config := &docker.Config{
386387
Image: driverConfig.ImageName,

client/driver/docker_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ func dockerSetup(t *testing.T, task *structs.Task) (*docker.Client, DriverHandle
117117

118118
// This test should always pass, even if docker daemon is not available
119119
func TestDockerDriver_Fingerprint(t *testing.T) {
120-
driverCtx, _ := testDriverContexts(&structs.Task{Name: "foo", Resources: basicResources})
120+
driverCtx, execCtx := testDriverContexts(&structs.Task{Name: "foo", Resources: basicResources})
121+
defer execCtx.AllocDir.Destroy()
121122
d := NewDockerDriver(driverCtx)
122123
node := &structs.Node{
123124
Attributes: make(map[string]string),

client/driver/driver.go

+1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ func GetTaskEnv(allocDir *allocdir.AllocDir, node *structs.Node,
153153
}
154154

155155
env.SetTaskLocalDir(filepath.Join(taskdir, allocdir.TaskLocal))
156+
env.SetSecretDir(filepath.Join(taskdir, allocdir.TaskSecrets))
156157
}
157158

158159
if task.Resources != nil {

client/driver/env/env.go

+18
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ const (
2121
// removed.
2222
TaskLocalDir = "NOMAD_TASK_DIR"
2323

24+
// SecretDir is the environment variable with the path to the tasks secret
25+
// directory where it can store sensitive data.
26+
SecretDir = "NOMAD_SECRET_DIR"
27+
2428
// MemLimit is the environment variable with the tasks memory limit in MBs.
2529
MemLimit = "NOMAD_MEMORY_LIMIT"
2630

@@ -79,6 +83,7 @@ type TaskEnvironment struct {
7983
JobMeta map[string]string
8084
AllocDir string
8185
TaskDir string
86+
SecretDir string
8287
CpuLimit int
8388
MemLimit int
8489
TaskName string
@@ -153,6 +158,9 @@ func (t *TaskEnvironment) Build() *TaskEnvironment {
153158
if t.TaskDir != "" {
154159
t.TaskEnv[TaskLocalDir] = t.TaskDir
155160
}
161+
if t.SecretDir != "" {
162+
t.TaskEnv[SecretDir] = t.SecretDir
163+
}
156164

157165
// Build the resource limits
158166
if t.MemLimit != 0 {
@@ -249,6 +257,16 @@ func (t *TaskEnvironment) ClearTaskLocalDir() *TaskEnvironment {
249257
return t
250258
}
251259

260+
func (t *TaskEnvironment) SetSecretDir(dir string) *TaskEnvironment {
261+
t.SecretDir = dir
262+
return t
263+
}
264+
265+
func (t *TaskEnvironment) ClearSecretDir() *TaskEnvironment {
266+
t.SecretDir = ""
267+
return t
268+
}
269+
252270
func (t *TaskEnvironment) SetMemLimit(limit int) *TaskEnvironment {
253271
t.MemLimit = limit
254272
return t

client/driver/exec_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ func TestExecDriver_Fingerprint(t *testing.T) {
2626
Name: "foo",
2727
Resources: structs.DefaultResources(),
2828
}
29-
driverCtx, _ := testDriverContexts(task)
29+
driverCtx, execCtx := testDriverContexts(task)
30+
defer execCtx.AllocDir.Destroy()
3031
d := NewExecDriver(driverCtx)
3132
node := &structs.Node{
3233
Attributes: map[string]string{

client/driver/executor/executor_linux_test.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,23 @@ func TestExecutor_IsolationAndConstraints(t *testing.T) {
8181
t.Fatalf("file %v hasn't been removed", memLimits)
8282
}
8383

84-
expected := "/:\nalloc/\nbin/\ndev/\netc/\nlib/\nlib64/\nlocal/\nproc/\ntmp/\nusr/\n\n/etc/:\nld.so.cache\nld.so.conf\nld.so.conf.d/"
84+
expected := `/:
85+
alloc/
86+
bin/
87+
dev/
88+
etc/
89+
lib/
90+
lib64/
91+
local/
92+
proc/
93+
secrets/
94+
tmp/
95+
usr/
96+
97+
/etc/:
98+
ld.so.cache
99+
ld.so.conf
100+
ld.so.conf.d/`
85101
file := filepath.Join(ctx.AllocDir.LogDir(), "web.stdout.0")
86102
output, err := ioutil.ReadFile(file)
87103
if err != nil {

client/driver/java_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ func TestJavaDriver_Fingerprint(t *testing.T) {
3535
Name: "foo",
3636
Resources: structs.DefaultResources(),
3737
}
38-
driverCtx, _ := testDriverContexts(task)
38+
driverCtx, execCtx := testDriverContexts(task)
39+
defer execCtx.AllocDir.Destroy()
3940
d := NewJavaDriver(driverCtx)
4041
node := &structs.Node{
4142
Attributes: map[string]string{

client/driver/qemu_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ func TestQemuDriver_Fingerprint(t *testing.T) {
1919
Name: "foo",
2020
Resources: structs.DefaultResources(),
2121
}
22-
driverCtx, _ := testDriverContexts(task)
22+
driverCtx, execCtx := testDriverContexts(task)
23+
defer execCtx.AllocDir.Destroy()
2324
d := NewQemuDriver(driverCtx)
2425
node := &structs.Node{
2526
Attributes: make(map[string]string),

client/driver/raw_exec_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ func TestRawExecDriver_Fingerprint(t *testing.T) {
2121
Name: "foo",
2222
Resources: structs.DefaultResources(),
2323
}
24-
driverCtx, _ := testDriverContexts(task)
24+
driverCtx, execCtx := testDriverContexts(task)
25+
defer execCtx.AllocDir.Destroy()
2526
d := NewRawExecDriver(driverCtx)
2627
node := &structs.Node{
2728
Attributes: make(map[string]string),

client/driver/rkt_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ func TestRktTaskValidate(t *testing.T) {
376376
"dns_servers": []string{"8.8.8.8", "8.8.4.4"},
377377
"dns_search_domains": []string{"example.com", "example.org", "example.net"},
378378
},
379+
Resources: basicResources,
379380
}
380381
driverCtx, execCtx := testDriverContexts(task)
381382
defer execCtx.AllocDir.Destroy()

0 commit comments

Comments
 (0)