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>
| 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 |
-} |
| ... | ... |
@@ -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)
|