Browse code

do not force target type for secret references

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

use secret store interface instead of embedded secret data into container

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

Evan Hazlett authored on 2016/11/16 00:04:36
Showing 13 changed files
1 1
deleted file mode 100644
... ...
@@ -1,14 +0,0 @@
1
-package container
2
-
3
-import "os"
4
-
5
-// ContainerSecret represents a secret in a container.  This gets realized
6
-// in the container tmpfs
7
-type ContainerSecret struct {
8
-	Name   string
9
-	Target string
10
-	Data   []byte
11
-	UID    string
12
-	GID    string
13
-	Mode   os.FileMode
14
-}
... ...
@@ -27,7 +27,7 @@ type SecretReferenceFileTarget struct {
27 27
 
28 28
 // SecretReference is a reference to a secret in swarm
29 29
 type SecretReference struct {
30
+	File       *SecretReferenceFileTarget
30 31
 	SecretID   string
31 32
 	SecretName string
32
-	Target     *SecretReferenceFileTarget
33 33
 }
... ...
@@ -17,19 +17,19 @@ func parseSecrets(client client.APIClient, requestedSecrets []*types.SecretReque
17 17
 	ctx := context.Background()
18 18
 
19 19
 	for _, secret := range requestedSecrets {
20
+		if _, exists := secretRefs[secret.Target]; exists {
21
+			return nil, fmt.Errorf("duplicate secret target for %s not allowed", secret.Source)
22
+		}
20 23
 		secretRef := &swarmtypes.SecretReference{
21
-			SecretName: secret.Source,
22
-			Target: &swarmtypes.SecretReferenceFileTarget{
24
+			File: &swarmtypes.SecretReferenceFileTarget{
23 25
 				Name: secret.Target,
24 26
 				UID:  secret.UID,
25 27
 				GID:  secret.GID,
26 28
 				Mode: secret.Mode,
27 29
 			},
30
+			SecretName: secret.Source,
28 31
 		}
29 32
 
30
-		if _, exists := secretRefs[secret.Target]; exists {
31
-			return nil, fmt.Errorf("duplicate secret target for %s not allowed", secret.Source)
32
-		}
33 33
 		secretRefs[secret.Target] = secretRef
34 34
 	}
35 35
 
... ...
@@ -19,6 +19,7 @@ import (
19 19
 	containertypes "github.com/docker/docker/api/types/container"
20 20
 	mounttypes "github.com/docker/docker/api/types/mount"
21 21
 	networktypes "github.com/docker/docker/api/types/network"
22
+	swarmtypes "github.com/docker/docker/api/types/swarm"
22 23
 	"github.com/docker/docker/container/stream"
23 24
 	"github.com/docker/docker/daemon/exec"
24 25
 	"github.com/docker/docker/daemon/logger"
... ...
@@ -41,6 +42,7 @@ import (
41 41
 	"github.com/docker/libnetwork/netlabel"
42 42
 	"github.com/docker/libnetwork/options"
43 43
 	"github.com/docker/libnetwork/types"
44
+	agentexec "github.com/docker/swarmkit/agent/exec"
44 45
 	"github.com/opencontainers/runc/libcontainer/label"
45 46
 )
46 47
 
... ...
@@ -90,9 +92,10 @@ type CommonContainer struct {
90 90
 	HasBeenStartedBefore   bool
91 91
 	HasBeenManuallyStopped bool // used for unless-stopped restart policy
92 92
 	MountPoints            map[string]*volume.MountPoint
93
-	HostConfig             *containertypes.HostConfig        `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable
94
-	ExecCommands           *exec.Store                       `json:"-"`
95
-	Secrets                []*containertypes.ContainerSecret `json:"-"` // do not serialize
93
+	HostConfig             *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable
94
+	ExecCommands           *exec.Store                `json:"-"`
95
+	SecretStore            agentexec.SecretGetter     `json:"-"`
96
+	SecretReferences       []*swarmtypes.SecretReference
96 97
 	// logDriver for closing
97 98
 	LogDriver      logger.Logger  `json:"-"`
98 99
 	LogCopier      *logger.Copier `json:"-"`
... ...
@@ -258,7 +258,7 @@ func (container *Container) IpcMounts() []Mount {
258 258
 
259 259
 // SecretMount returns the mount for the secret path
260 260
 func (container *Container) SecretMount() *Mount {
261
-	if len(container.Secrets) > 0 {
261
+	if len(container.SecretReferences) > 0 {
262 262
 		return &Mount{
263 263
 			Source:      container.SecretMountPath(),
264 264
 			Destination: containerSecretMountPath,
... ...
@@ -82,18 +82,22 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
82 82
 func secretReferencesToGRPC(sr []*types.SecretReference) []*swarmapi.SecretReference {
83 83
 	refs := make([]*swarmapi.SecretReference, 0, len(sr))
84 84
 	for _, s := range sr {
85
-		refs = append(refs, &swarmapi.SecretReference{
85
+		ref := &swarmapi.SecretReference{
86 86
 			SecretID:   s.SecretID,
87 87
 			SecretName: s.SecretName,
88
-			Target: &swarmapi.SecretReference_File{
88
+		}
89
+		if s.File != nil {
90
+			ref.Target = &swarmapi.SecretReference_File{
89 91
 				File: &swarmapi.SecretReference_FileTarget{
90
-					Name: s.Target.Name,
91
-					UID:  s.Target.UID,
92
-					GID:  s.Target.GID,
93
-					Mode: s.Target.Mode,
92
+					Name: s.File.Name,
93
+					UID:  s.File.UID,
94
+					GID:  s.File.GID,
95
+					Mode: s.File.Mode,
94 96
 				},
95
-			},
96
-		})
97
+			}
98
+		}
99
+
100
+		refs = append(refs, ref)
97 101
 	}
98 102
 
99 103
 	return refs
... ...
@@ -108,14 +112,14 @@ func secretReferencesFromGRPC(sr []*swarmapi.SecretReference) []*types.SecretRef
108 108
 			continue
109 109
 		}
110 110
 		refs = append(refs, &types.SecretReference{
111
-			SecretID:   s.SecretID,
112
-			SecretName: s.SecretName,
113
-			Target: &types.SecretReferenceFileTarget{
111
+			File: &types.SecretReferenceFileTarget{
114 112
 				Name: target.Name,
115 113
 				UID:  target.UID,
116 114
 				GID:  target.GID,
117 115
 				Mode: target.Mode,
118 116
 			},
117
+			SecretID:   s.SecretID,
118
+			SecretName: s.SecretName,
119 119
 		})
120 120
 	}
121 121
 
... ...
@@ -39,3 +39,27 @@ func SecretSpecToGRPC(s swarmtypes.SecretSpec) swarmapi.SecretSpec {
39 39
 		Data: s.Data,
40 40
 	}
41 41
 }
42
+
43
+func SecretReferencesFromGRPC(s []*swarmapi.SecretReference) []*swarmtypes.SecretReference {
44
+	refs := []*swarmtypes.SecretReference{}
45
+
46
+	for _, r := range s {
47
+		ref := &swarmtypes.SecretReference{
48
+			SecretID:   r.SecretID,
49
+			SecretName: r.SecretName,
50
+		}
51
+
52
+		if t, ok := r.Target.(*swarmapi.SecretReference_File); ok {
53
+			ref.File = &swarmtypes.SecretReferenceFileTarget{
54
+				Name: t.File.Name,
55
+				UID:  t.File.UID,
56
+				GID:  t.File.GID,
57
+				Mode: t.File.Mode,
58
+			}
59
+		}
60
+
61
+		refs = append(refs, ref)
62
+	}
63
+
64
+	return refs
65
+}
... ...
@@ -11,11 +11,13 @@ import (
11 11
 	"github.com/docker/docker/api/types/events"
12 12
 	"github.com/docker/docker/api/types/filters"
13 13
 	"github.com/docker/docker/api/types/network"
14
+	swarmtypes "github.com/docker/docker/api/types/swarm"
14 15
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
15 16
 	"github.com/docker/docker/reference"
16 17
 	"github.com/docker/libnetwork"
17 18
 	"github.com/docker/libnetwork/cluster"
18 19
 	networktypes "github.com/docker/libnetwork/types"
20
+	"github.com/docker/swarmkit/agent/exec"
19 21
 	"golang.org/x/net/context"
20 22
 )
21 23
 
... ...
@@ -38,7 +40,8 @@ type Backend interface {
38 38
 	ContainerWaitWithContext(ctx context.Context, name string) error
39 39
 	ContainerRm(name string, config *types.ContainerRmConfig) error
40 40
 	ContainerKill(name string, sig uint64) error
41
-	SetContainerSecrets(name string, secrets []*container.ContainerSecret) error
41
+	SetContainerSecretStore(name string, store exec.SecretGetter) error
42
+	SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error
42 43
 	SystemInfo() (*types.Info, error)
43 44
 	VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error)
44 45
 	Containers(config *types.ContainerListOptions) ([]*types.Container, error)
... ...
@@ -16,6 +16,7 @@ import (
16 16
 	containertypes "github.com/docker/docker/api/types/container"
17 17
 	"github.com/docker/docker/api/types/events"
18 18
 	"github.com/docker/docker/api/types/versions"
19
+	"github.com/docker/docker/daemon/cluster/convert"
19 20
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
20 21
 	"github.com/docker/docker/reference"
21 22
 	"github.com/docker/libnetwork"
... ...
@@ -237,33 +238,14 @@ func (c *containerAdapter) create(ctx context.Context) error {
237 237
 	if container == nil {
238 238
 		return fmt.Errorf("unable to get container from task spec")
239 239
 	}
240
-	secrets := make([]*containertypes.ContainerSecret, 0, len(container.Secrets))
241
-	for _, s := range container.Secrets {
242
-		sec := c.secrets.Get(s.SecretID)
243
-		if sec == nil {
244
-			logrus.Warnf("unable to get secret %s from provider", s.SecretID)
245
-			continue
246
-		}
247
-
248
-		name := sec.Spec.Annotations.Name
249
-		target := s.GetFile()
250
-		if target == nil {
251
-			logrus.Warnf("secret target was not a file: secret=%s", s.SecretID)
252
-			continue
253
-		}
254 240
 
255
-		secrets = append(secrets, &containertypes.ContainerSecret{
256
-			Name:   name,
257
-			Target: target.Name,
258
-			Data:   sec.Spec.Data,
259
-			UID:    target.UID,
260
-			GID:    target.GID,
261
-			Mode:   target.Mode,
262
-		})
241
+	// configure secrets
242
+	if err := c.backend.SetContainerSecretStore(cr.ID, c.secrets); err != nil {
243
+		return err
263 244
 	}
264 245
 
265
-	// configure secrets
266
-	if err := c.backend.SetContainerSecrets(cr.ID, secrets); err != nil {
246
+	refs := convert.SecretReferencesFromGRPC(container.Secrets)
247
+	if err := c.backend.SetContainerSecretReferences(cr.ID, refs); err != nil {
267 248
 		return err
268 249
 	}
269 250
 
... ...
@@ -145,7 +145,7 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
145 145
 }
146 146
 
147 147
 func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
148
-	if len(c.Secrets) == 0 {
148
+	if len(c.SecretReferences) == 0 {
149 149
 		return nil
150 150
 	}
151 151
 
... ...
@@ -174,8 +174,17 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
174 174
 		return errors.Wrap(err, "unable to setup secret mount")
175 175
 	}
176 176
 
177
-	for _, s := range c.Secrets {
178
-		targetPath := filepath.Clean(s.Target)
177
+	for _, s := range c.SecretReferences {
178
+		if c.SecretStore == nil {
179
+			return fmt.Errorf("secret store is not initialized")
180
+		}
181
+
182
+		// TODO (ehazlett): use type switch when more are supported
183
+		if s.File == nil {
184
+			return fmt.Errorf("secret target type is not a file target")
185
+		}
186
+
187
+		targetPath := filepath.Clean(s.File.Name)
179 188
 		// ensure that the target is a filename only; no paths allowed
180 189
 		if targetPath != filepath.Base(targetPath) {
181 190
 			return fmt.Errorf("error creating secret: secret must not be a path")
... ...
@@ -187,18 +196,22 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
187 187
 		}
188 188
 
189 189
 		logrus.WithFields(logrus.Fields{
190
-			"name": s.Name,
190
+			"name": s.File.Name,
191 191
 			"path": fPath,
192 192
 		}).Debug("injecting secret")
193
-		if err := ioutil.WriteFile(fPath, s.Data, s.Mode); err != nil {
193
+		secret := c.SecretStore.Get(s.SecretID)
194
+		if secret == nil {
195
+			return fmt.Errorf("unable to get secret from secret store")
196
+		}
197
+		if err := ioutil.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil {
194 198
 			return errors.Wrap(err, "error injecting secret")
195 199
 		}
196 200
 
197
-		uid, err := strconv.Atoi(s.UID)
201
+		uid, err := strconv.Atoi(s.File.UID)
198 202
 		if err != nil {
199 203
 			return err
200 204
 		}
201
-		gid, err := strconv.Atoi(s.GID)
205
+		gid, err := strconv.Atoi(s.File.GID)
202 206
 		if err != nil {
203 207
 			return err
204 208
 		}
... ...
@@ -2,12 +2,25 @@ package daemon
2 2
 
3 3
 import (
4 4
 	"github.com/Sirupsen/logrus"
5
-	containertypes "github.com/docker/docker/api/types/container"
5
+	swarmtypes "github.com/docker/docker/api/types/swarm"
6
+	"github.com/docker/swarmkit/agent/exec"
6 7
 )
7 8
 
8
-// SetContainerSecrets sets the container secrets needed
9
-func (daemon *Daemon) SetContainerSecrets(name string, secrets []*containertypes.ContainerSecret) error {
10
-	if !secretsSupported() && len(secrets) > 0 {
9
+// SetContainerSecretStore sets the secret store backend for the container
10
+func (daemon *Daemon) SetContainerSecretStore(name string, store exec.SecretGetter) error {
11
+	c, err := daemon.GetContainer(name)
12
+	if err != nil {
13
+		return err
14
+	}
15
+
16
+	c.SecretStore = store
17
+
18
+	return nil
19
+}
20
+
21
+// SetContainerSecretReferences sets the container secret references needed
22
+func (daemon *Daemon) SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error {
23
+	if !secretsSupported() && len(refs) > 0 {
11 24
 		logrus.Warn("secrets are not supported on this platform")
12 25
 		return nil
13 26
 	}
... ...
@@ -17,7 +30,7 @@ func (daemon *Daemon) SetContainerSecrets(name string, secrets []*containertypes
17 17
 		return err
18 18
 	}
19 19
 
20
-	c.Secrets = secrets
20
+	c.SecretReferences = refs
21 21
 
22 22
 	return nil
23 23
 }
... ...
@@ -69,10 +69,10 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretSimple(c *check.C) {
69 69
 	c.Assert(refs, checker.HasLen, 1)
70 70
 
71 71
 	c.Assert(refs[0].SecretName, checker.Equals, testName)
72
-	c.Assert(refs[0].Target, checker.Not(checker.IsNil))
73
-	c.Assert(refs[0].Target.Name, checker.Equals, testName)
74
-	c.Assert(refs[0].Target.UID, checker.Equals, "0")
75
-	c.Assert(refs[0].Target.GID, checker.Equals, "0")
72
+	c.Assert(refs[0].File, checker.Not(checker.IsNil))
73
+	c.Assert(refs[0].File.Name, checker.Equals, testName)
74
+	c.Assert(refs[0].File.UID, checker.Equals, "0")
75
+	c.Assert(refs[0].File.GID, checker.Equals, "0")
76 76
 }
77 77
 
78 78
 func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTarget(c *check.C) {
... ...
@@ -100,6 +100,6 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTarget(c *check.C) {
100 100
 	c.Assert(refs, checker.HasLen, 1)
101 101
 
102 102
 	c.Assert(refs[0].SecretName, checker.Equals, testName)
103
-	c.Assert(refs[0].Target, checker.Not(checker.IsNil))
104
-	c.Assert(refs[0].Target.Name, checker.Equals, testTarget)
103
+	c.Assert(refs[0].File, checker.Not(checker.IsNil))
104
+	c.Assert(refs[0].File.Name, checker.Equals, testTarget)
105 105
 }
... ...
@@ -115,8 +115,8 @@ func (s *DockerSwarmSuite) TestServiceUpdateSecrets(c *check.C) {
115 115
 	c.Assert(refs, checker.HasLen, 1)
116 116
 
117 117
 	c.Assert(refs[0].SecretName, checker.Equals, testName)
118
-	c.Assert(refs[0].Target, checker.Not(checker.IsNil))
119
-	c.Assert(refs[0].Target.Name, checker.Equals, testTarget)
118
+	c.Assert(refs[0].File, checker.Not(checker.IsNil))
119
+	c.Assert(refs[0].File.Name, checker.Equals, testTarget)
120 120
 
121 121
 	// remove
122 122
 	out, err = d.cmdRetryOutOfSequence("service", "update", "test", "--secret-rm", testName)