Signed-off-by: John Stephens <johnstep@docker.com>
| ... | ... |
@@ -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 |
} |