Signed-off-by: John Stephens <johnstep@docker.com>
John Stephens authored on 2016/12/02 01:11:15... | ... |
@@ -8,10 +8,12 @@ import ( |
8 | 8 |
"path/filepath" |
9 | 9 |
|
10 | 10 |
containertypes "github.com/docker/docker/api/types/container" |
11 |
+ "github.com/docker/docker/pkg/system" |
|
11 | 12 |
) |
12 | 13 |
|
13 | 14 |
const ( |
14 |
- containerSecretMountPath = `C:\ProgramData\Docker\secrets` |
|
15 |
+ containerSecretMountPath = `C:\ProgramData\Docker\secrets` |
|
16 |
+ containerInternalSecretMountPath = `C:\ProgramData\Docker\internal\secrets` |
|
15 | 17 |
) |
16 | 18 |
|
17 | 19 |
// Container holds fields specific to the Windows implementation. See |
... | ... |
@@ -47,14 +49,46 @@ func (container *Container) IpcMounts() []Mount { |
47 | 47 |
return nil |
48 | 48 |
} |
49 | 49 |
|
50 |
-// SecretMounts returns the mounts for the secret path |
|
51 |
-func (container *Container) SecretMounts() []Mount { |
|
50 |
+// CreateSecretSymlinks creates symlinks to files in the secret mount. |
|
51 |
+func (container *Container) CreateSecretSymlinks() error { |
|
52 |
+ for _, r := range container.SecretReferences { |
|
53 |
+ if r.File == nil { |
|
54 |
+ continue |
|
55 |
+ } |
|
56 |
+ resolvedPath, _, err := container.ResolvePath(getSecretTargetPath(r)) |
|
57 |
+ if err != nil { |
|
58 |
+ return err |
|
59 |
+ } |
|
60 |
+ if err := system.MkdirAll(filepath.Dir(resolvedPath), 0); err != nil { |
|
61 |
+ return err |
|
62 |
+ } |
|
63 |
+ if err := os.Symlink(filepath.Join(containerInternalSecretMountPath, r.SecretID), resolvedPath); err != nil { |
|
64 |
+ return err |
|
65 |
+ } |
|
66 |
+ } |
|
67 |
+ |
|
52 | 68 |
return nil |
53 | 69 |
} |
54 | 70 |
|
71 |
+// SecretMounts returns the mount for the secret path. |
|
72 |
+// All secrets are stored in a single mount on Windows. Target symlinks are |
|
73 |
+// created for each secret, pointing to the files in this mount. |
|
74 |
+func (container *Container) SecretMounts() []Mount { |
|
75 |
+ var mounts []Mount |
|
76 |
+ if len(container.SecretReferences) > 0 { |
|
77 |
+ mounts = append(mounts, Mount{ |
|
78 |
+ Source: container.SecretMountPath(), |
|
79 |
+ Destination: containerInternalSecretMountPath, |
|
80 |
+ Writable: false, |
|
81 |
+ }) |
|
82 |
+ } |
|
83 |
+ |
|
84 |
+ return mounts |
|
85 |
+} |
|
86 |
+ |
|
55 | 87 |
// UnmountSecrets unmounts the fs for secrets |
56 | 88 |
func (container *Container) UnmountSecrets() error { |
57 |
- return nil |
|
89 |
+ return os.RemoveAll(container.SecretMountPath()) |
|
58 | 90 |
} |
59 | 91 |
|
60 | 92 |
// DetachAndUnmount unmounts all volumes. |
... | ... |
@@ -1,10 +1,15 @@ |
1 |
-// +build windows |
|
2 |
- |
|
3 | 1 |
package daemon |
4 | 2 |
|
5 | 3 |
import ( |
4 |
+ "fmt" |
|
5 |
+ "io/ioutil" |
|
6 |
+ "os" |
|
7 |
+ |
|
8 |
+ "github.com/Sirupsen/logrus" |
|
6 | 9 |
"github.com/docker/docker/container" |
10 |
+ "github.com/docker/docker/pkg/system" |
|
7 | 11 |
"github.com/docker/libnetwork" |
12 |
+ "github.com/pkg/errors" |
|
8 | 13 |
) |
9 | 14 |
|
10 | 15 |
func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) { |
... | ... |
@@ -35,6 +40,57 @@ func detachMounted(path string) error { |
35 | 35 |
return nil |
36 | 36 |
} |
37 | 37 |
|
38 |
+func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { |
|
39 |
+ if len(c.SecretReferences) == 0 { |
|
40 |
+ return nil |
|
41 |
+ } |
|
42 |
+ |
|
43 |
+ localMountPath := c.SecretMountPath() |
|
44 |
+ logrus.Debugf("secrets: setting up secret dir: %s", localMountPath) |
|
45 |
+ |
|
46 |
+ // create local secret root |
|
47 |
+ if err := system.MkdirAllWithACL(localMountPath, 0); err != nil { |
|
48 |
+ return errors.Wrap(err, "error creating secret local directory") |
|
49 |
+ } |
|
50 |
+ |
|
51 |
+ defer func() { |
|
52 |
+ if setupErr != nil { |
|
53 |
+ if err := os.RemoveAll(localMountPath); err != nil { |
|
54 |
+ logrus.Errorf("error cleaning up secret mount: %s", err) |
|
55 |
+ } |
|
56 |
+ } |
|
57 |
+ }() |
|
58 |
+ |
|
59 |
+ if c.DependencyStore == nil { |
|
60 |
+ return fmt.Errorf("secret store is not initialized") |
|
61 |
+ } |
|
62 |
+ |
|
63 |
+ for _, s := range c.SecretReferences { |
|
64 |
+ // TODO (ehazlett): use type switch when more are supported |
|
65 |
+ if s.File == nil { |
|
66 |
+ logrus.Error("secret target type is not a file target") |
|
67 |
+ continue |
|
68 |
+ } |
|
69 |
+ |
|
70 |
+ // secrets are created in the SecretMountPath on the host, at a |
|
71 |
+ // single level |
|
72 |
+ fPath := c.SecretFilePath(*s) |
|
73 |
+ logrus.WithFields(logrus.Fields{ |
|
74 |
+ "name": s.File.Name, |
|
75 |
+ "path": fPath, |
|
76 |
+ }).Debug("injecting secret") |
|
77 |
+ secret := c.DependencyStore.Secrets().Get(s.SecretID) |
|
78 |
+ if secret == nil { |
|
79 |
+ return fmt.Errorf("unable to get secret from secret store") |
|
80 |
+ } |
|
81 |
+ if err := ioutil.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil { |
|
82 |
+ return errors.Wrap(err, "error injecting secret") |
|
83 |
+ } |
|
84 |
+ } |
|
85 |
+ |
|
86 |
+ return nil |
|
87 |
+} |
|
88 |
+ |
|
38 | 89 |
func killProcessDirectly(container *container.Container) error { |
39 | 90 |
return nil |
40 | 91 |
} |
... | ... |
@@ -25,11 +25,51 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { |
25 | 25 |
// In base spec |
26 | 26 |
s.Hostname = c.FullHostname() |
27 | 27 |
|
28 |
+ if err := daemon.setupSecretDir(c); err != nil { |
|
29 |
+ return nil, err |
|
30 |
+ } |
|
31 |
+ |
|
28 | 32 |
// In s.Mounts |
29 | 33 |
mounts, err := daemon.setupMounts(c) |
30 | 34 |
if err != nil { |
31 | 35 |
return nil, err |
32 | 36 |
} |
37 |
+ |
|
38 |
+ var isHyperV bool |
|
39 |
+ if c.HostConfig.Isolation.IsDefault() { |
|
40 |
+ // Container using default isolation, so take the default from the daemon configuration |
|
41 |
+ isHyperV = daemon.defaultIsolation.IsHyperV() |
|
42 |
+ } else { |
|
43 |
+ // Container may be requesting an explicit isolation mode. |
|
44 |
+ isHyperV = c.HostConfig.Isolation.IsHyperV() |
|
45 |
+ } |
|
46 |
+ |
|
47 |
+ // If the container has not been started, and has secrets, create symlinks |
|
48 |
+ // to each secret. If it has been started before, the symlinks should have |
|
49 |
+ // already been created. Also, it is important to not mount a Hyper-V |
|
50 |
+ // container that has been started before, to protect the host from the |
|
51 |
+ // container; for example, from malicious mutation of NTFS data structures. |
|
52 |
+ if !c.HasBeenStartedBefore && len(c.SecretReferences) > 0 { |
|
53 |
+ // The container file system is mounted before this function is called, |
|
54 |
+ // except for Hyper-V containers, so mount it here in that case. |
|
55 |
+ if isHyperV { |
|
56 |
+ if err := daemon.Mount(c); err != nil { |
|
57 |
+ return nil, err |
|
58 |
+ } |
|
59 |
+ } |
|
60 |
+ err := c.CreateSecretSymlinks() |
|
61 |
+ if isHyperV { |
|
62 |
+ daemon.Unmount(c) |
|
63 |
+ } |
|
64 |
+ if err != nil { |
|
65 |
+ return nil, err |
|
66 |
+ } |
|
67 |
+ } |
|
68 |
+ |
|
69 |
+ if m := c.SecretMounts(); m != nil { |
|
70 |
+ mounts = append(mounts, m...) |
|
71 |
+ } |
|
72 |
+ |
|
33 | 73 |
for _, mount := range mounts { |
34 | 74 |
m := specs.Mount{ |
35 | 75 |
Source: mount.Source, |
... | ... |
@@ -64,14 +104,6 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { |
64 | 64 |
s.Process.User.Username = c.Config.User |
65 | 65 |
|
66 | 66 |
// In spec.Root. This is not set for Hyper-V containers |
67 |
- var isHyperV bool |
|
68 |
- if c.HostConfig.Isolation.IsDefault() { |
|
69 |
- // Container using default isolation, so take the default from the daemon configuration |
|
70 |
- isHyperV = daemon.defaultIsolation.IsHyperV() |
|
71 |
- } else { |
|
72 |
- // Container may be requesting an explicit isolation mode. |
|
73 |
- isHyperV = c.HostConfig.Isolation.IsHyperV() |
|
74 |
- } |
|
75 | 67 |
if !isHyperV { |
76 | 68 |
s.Root.Path = c.BaseFS |
77 | 69 |
} |