Browse code

support custom paths for secrets

This adds support to specify custom container paths for secrets.

Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>

Evan Hazlett authored on 2017/04/12 02:34:19
Showing 9 changed files
... ...
@@ -948,3 +948,20 @@ func (container *Container) InitializeStdio(iop libcontainerd.IOPipe) error {
948 948
 
949 949
 	return nil
950 950
 }
951
+
952
+// SecretMountPath returns the path of the secret mount for the container
953
+func (container *Container) SecretMountPath() string {
954
+	return filepath.Join(container.Root, "secrets")
955
+}
956
+
957
+func (container *Container) getLocalSecretPath(r *swarmtypes.SecretReference) string {
958
+	return filepath.Join(container.SecretMountPath(), filepath.Base(r.File.Name))
959
+}
960
+
961
+func getSecretTargetPath(r *swarmtypes.SecretReference) string {
962
+	if filepath.IsAbs(r.File.Name) {
963
+		return r.File.Name
964
+	}
965
+
966
+	return filepath.Join(containerSecretMountPath, r.File.Name)
967
+}
... ...
@@ -12,8 +12,8 @@ func detachMounted(path string) error {
12 12
 	return unix.Unmount(path, 0)
13 13
 }
14 14
 
15
-// SecretMount returns the mount for the secret path
16
-func (container *Container) SecretMount() *Mount {
15
+// SecretMounts returns the mounts for the secret path
16
+func (container *Container) SecretMounts() []Mount {
17 17
 	return nil
18 18
 }
19 19
 
... ...
@@ -1,9 +1,11 @@
1 1
 package container
2 2
 
3 3
 import (
4
+	"path/filepath"
4 5
 	"testing"
5 6
 
6 7
 	"github.com/docker/docker/api/types/container"
8
+	swarmtypes "github.com/docker/docker/api/types/swarm"
7 9
 	"github.com/docker/docker/pkg/signal"
8 10
 )
9 11
 
... ...
@@ -58,3 +60,17 @@ func TestContainerStopTimeout(t *testing.T) {
58 58
 		t.Fatalf("Expected 15, got %v", s)
59 59
 	}
60 60
 }
61
+
62
+func TestContainerSecretReferenceDestTarget(t *testing.T) {
63
+	ref := &swarmtypes.SecretReference{
64
+		File: &swarmtypes.SecretReferenceFileTarget{
65
+			Name: "app",
66
+		},
67
+	}
68
+
69
+	d := getSecretTargetPath(ref)
70
+	expected := filepath.Join(containerSecretMountPath, "app")
71
+	if d != expected {
72
+		t.Fatalf("expected secret dest %q; received %q", expected, d)
73
+	}
74
+}
... ...
@@ -163,11 +163,6 @@ func (container *Container) NetworkMounts() []Mount {
163 163
 	return mounts
164 164
 }
165 165
 
166
-// SecretMountPath returns the path of the secret mount for the container
167
-func (container *Container) SecretMountPath() string {
168
-	return filepath.Join(container.Root, "secrets")
169
-}
170
-
171 166
 // CopyImagePathContent copies files in destination to the volume.
172 167
 func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error {
173 168
 	rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, destination), container.BaseFS)
... ...
@@ -253,17 +248,21 @@ func (container *Container) IpcMounts() []Mount {
253 253
 	return mounts
254 254
 }
255 255
 
256
-// SecretMount returns the mount for the secret path
257
-func (container *Container) SecretMount() *Mount {
258
-	if len(container.SecretReferences) > 0 {
259
-		return &Mount{
260
-			Source:      container.SecretMountPath(),
261
-			Destination: containerSecretMountPath,
256
+// SecretMounts returns the mount for the secret path
257
+func (container *Container) SecretMounts() []Mount {
258
+	var mounts []Mount
259
+	for _, r := range container.SecretReferences {
260
+		// secrets are created in the SecretMountPath at a single level
261
+		// i.e. /var/run/secrets/foo
262
+		srcPath := container.getLocalSecretPath(r)
263
+		mounts = append(mounts, Mount{
264
+			Source:      srcPath,
265
+			Destination: getSecretTargetPath(r),
262 266
 			Writable:    false,
263
-		}
267
+		})
264 268
 	}
265 269
 
266
-	return nil
270
+	return mounts
267 271
 }
268 272
 
269 273
 // UnmountSecrets unmounts the local tmpfs for secrets
... ...
@@ -10,6 +10,10 @@ import (
10 10
 	containertypes "github.com/docker/docker/api/types/container"
11 11
 )
12 12
 
13
+const (
14
+	containerSecretMountPath = `C:\ProgramData\Docker\secrets`
15
+)
16
+
13 17
 // Container holds fields specific to the Windows implementation. See
14 18
 // CommonContainer for standard fields common to all containers.
15 19
 type Container struct {
... ...
@@ -43,8 +47,8 @@ func (container *Container) IpcMounts() []Mount {
43 43
 	return nil
44 44
 }
45 45
 
46
-// SecretMount returns the mount for the secret path
47
-func (container *Container) SecretMount() *Mount {
46
+// SecretMounts returns the mount for the secret path
47
+func (container *Container) SecretMounts() []Mount {
48 48
 	return nil
49 49
 }
50 50
 
... ...
@@ -177,13 +177,9 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
177 177
 			return fmt.Errorf("secret target type is not a file target")
178 178
 		}
179 179
 
180
-		targetPath := filepath.Clean(s.File.Name)
181
-		// ensure that the target is a filename only; no paths allowed
182
-		if targetPath != filepath.Base(targetPath) {
183
-			return fmt.Errorf("error creating secret: secret must not be a path")
184
-		}
185
-
186
-		fPath := filepath.Join(localMountPath, targetPath)
180
+		// secrets are created in the SecretMountPath at a single level
181
+		// i.e. /var/run/secrets/foo
182
+		fPath := filepath.Join(localMountPath, filepath.Base(s.File.Name))
187 183
 		if err := idtools.MkdirAllAs(filepath.Dir(fPath), 0700, rootUID, rootGID); err != nil {
188 184
 			return errors.Wrap(err, "error creating secret mount path")
189 185
 		}
... ...
@@ -750,8 +750,8 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
750 750
 	}
751 751
 	ms = append(ms, tmpfsMounts...)
752 752
 
753
-	if m := c.SecretMount(); m != nil {
754
-		ms = append(ms, *m)
753
+	if m := c.SecretMounts(); m != nil {
754
+		ms = append(ms, m...)
755 755
 	}
756 756
 
757 757
 	sort.Sort(mounts(ms))
... ...
@@ -90,35 +90,48 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretSimple(c *check.C) {
90 90
 	c.Assert(refs[0].File.Name, checker.Equals, testName)
91 91
 	c.Assert(refs[0].File.UID, checker.Equals, "0")
92 92
 	c.Assert(refs[0].File.GID, checker.Equals, "0")
93
-}
94
-
95
-func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTarget(c *check.C) {
96
-	d := s.AddDaemon(c, true, true)
97 93
 
98
-	serviceName := "test-service-secret"
99
-	testName := "test_secret"
100
-	id := d.CreateSecret(c, swarm.SecretSpec{
101
-		Annotations: swarm.Annotations{
102
-			Name: testName,
103
-		},
104
-		Data: []byte("TESTINGDATA"),
105
-	})
106
-	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
107
-	testTarget := "testing"
108
-
109
-	out, err := d.Cmd("service", "create", "--name", serviceName, "--secret", fmt.Sprintf("source=%s,target=%s", testName, testTarget), "busybox", "top")
94
+	out, err = d.Cmd("service", "rm", serviceName)
110 95
 	c.Assert(err, checker.IsNil, check.Commentf(out))
96
+	d.DeleteSecret(c, testName)
97
+}
111 98
 
112
-	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
113
-	c.Assert(err, checker.IsNil)
114
-
115
-	var refs []swarm.SecretReference
116
-	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
117
-	c.Assert(refs, checker.HasLen, 1)
99
+func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTargetPaths(c *check.C) {
100
+	d := s.AddDaemon(c, true, true)
118 101
 
119
-	c.Assert(refs[0].SecretName, checker.Equals, testName)
120
-	c.Assert(refs[0].File, checker.Not(checker.IsNil))
121
-	c.Assert(refs[0].File.Name, checker.Equals, testTarget)
102
+	testPaths := map[string]string{
103
+		"app":         "/etc/secret",
104
+		"test_secret": "test_secret",
105
+	}
106
+	for testName, testTarget := range testPaths {
107
+		serviceName := "svc-" + testName
108
+		id := d.CreateSecret(c, swarm.SecretSpec{
109
+			Annotations: swarm.Annotations{
110
+				Name: testName,
111
+			},
112
+			Data: []byte("TESTINGDATA"),
113
+		})
114
+		c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
115
+
116
+		out, err := d.Cmd("service", "create", "--name", serviceName, "--secret", fmt.Sprintf("source=%s,target=%s", testName, testTarget), "busybox", "top")
117
+		c.Assert(err, checker.IsNil, check.Commentf(out))
118
+
119
+		out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
120
+		c.Assert(err, checker.IsNil)
121
+
122
+		var refs []swarm.SecretReference
123
+		c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
124
+		c.Assert(refs, checker.HasLen, 1)
125
+
126
+		c.Assert(refs[0].SecretName, checker.Equals, testName)
127
+		c.Assert(refs[0].File, checker.Not(checker.IsNil))
128
+		c.Assert(refs[0].File.Name, checker.Equals, testTarget)
129
+
130
+		out, err = d.Cmd("service", "rm", serviceName)
131
+		c.Assert(err, checker.IsNil, check.Commentf(out))
132
+
133
+		d.DeleteSecret(c, testName)
134
+	}
122 135
 }
123 136
 
124 137
 func (s *DockerSwarmSuite) TestServiceCreateMountTmpfs(c *check.C) {
... ...
@@ -4,7 +4,6 @@ import (
4 4
 	"encoding/csv"
5 5
 	"fmt"
6 6
 	"os"
7
-	"path/filepath"
8 7
 	"strconv"
9 8
 	"strings"
10 9
 
... ...
@@ -53,10 +52,6 @@ func (o *SecretOpt) Set(value string) error {
53 53
 		case "source", "src":
54 54
 			options.SecretName = value
55 55
 		case "target":
56
-			tDir, _ := filepath.Split(value)
57
-			if tDir != "" {
58
-				return fmt.Errorf("target must not be a path")
59
-			}
60 56
 			options.File.Name = value
61 57
 		case "uid":
62 58
 			options.File.UID = value