diff --git a/container/container_windows.go b/container/container_windows.go index a328c0c4b5..bffd9520e2 100644 --- a/container/container_windows.go +++ b/container/container_windows.go @@ -14,6 +14,7 @@ import ( const ( containerSecretMountPath = `C:\ProgramData\Docker\secrets` containerInternalSecretMountPath = `C:\ProgramData\Docker\internal\secrets` + containerInternalConfigsDirPath = `C:\ProgramData\Docker\internal\configs` ) // Container holds fields specific to the Windows implementation. See @@ -91,6 +92,43 @@ func (container *Container) UnmountSecrets() error { return os.RemoveAll(container.SecretMountPath()) } +// CreateConfigSymlinks creates symlinks to files in the config mount. +func (container *Container) CreateConfigSymlinks() error { + for _, configRef := range container.ConfigReferences { + if configRef.File == nil { + continue + } + resolvedPath, _, err := container.ResolvePath(configRef.File.Name) + if err != nil { + return err + } + if err := system.MkdirAll(filepath.Dir(resolvedPath), 0); err != nil { + return err + } + if err := os.Symlink(filepath.Join(containerInternalConfigsDirPath, configRef.ConfigID), resolvedPath); err != nil { + return err + } + } + + return nil +} + +// ConfigMounts returns the mount for configs. +// All configs are stored in a single mount on Windows. Target symlinks are +// created for each config, pointing to the files in this mount. +func (container *Container) ConfigMounts() []Mount { + var mounts []Mount + if len(container.ConfigReferences) > 0 { + mounts = append(mounts, Mount{ + Source: container.ConfigsDirPath(), + Destination: containerInternalConfigsDirPath, + Writable: false, + }) + } + + return mounts +} + // DetachAndUnmount unmounts all volumes. // On Windows it only delegates to `UnmountVolumes` since there is nothing to // force unmount. diff --git a/daemon/configs_unsupported.go b/daemon/configs_unsupported.go index 93c56d3e60..1a7cbc9dc7 100644 --- a/daemon/configs_unsupported.go +++ b/daemon/configs_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux +// +build !linux,!windows package daemon diff --git a/daemon/configs_windows.go b/daemon/configs_windows.go new file mode 100644 index 0000000000..7cb2e9c438 --- /dev/null +++ b/daemon/configs_windows.go @@ -0,0 +1,7 @@ +// +build windows + +package daemon + +func configsSupported() bool { + return true +} diff --git a/daemon/container_operations_windows.go b/daemon/container_operations_windows.go index cd000bdccd..058089e7ca 100644 --- a/daemon/container_operations_windows.go +++ b/daemon/container_operations_windows.go @@ -16,6 +16,55 @@ func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]s return nil, nil } +func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) { + if len(c.ConfigReferences) == 0 { + return nil + } + + localPath := c.ConfigsDirPath() + logrus.Debugf("configs: setting up config dir: %s", localPath) + + // create local config root + if err := system.MkdirAllWithACL(localPath, 0); err != nil { + return errors.Wrap(err, "error creating config dir") + } + + defer func() { + if setupErr != nil { + if err := os.RemoveAll(localPath); err != nil { + logrus.Errorf("error cleaning up config dir: %s", err) + } + } + }() + + if c.DependencyStore == nil { + return fmt.Errorf("config store is not initialized") + } + + for _, configRef := range c.ConfigReferences { + // TODO (ehazlett): use type switch when more are supported + if configRef.File == nil { + logrus.Error("config target type is not a file target") + continue + } + + fPath := c.ConfigFilePath(*configRef) + + log := logrus.WithFields(logrus.Fields{"name": configRef.File.Name, "path": fPath}) + + log.Debug("injecting config") + config := c.DependencyStore.Configs().Get(configRef.ConfigID) + if config == nil { + return fmt.Errorf("unable to get config from config store") + } + if err := ioutil.WriteFile(fPath, config.Spec.Data, configRef.File.Mode); err != nil { + return errors.Wrap(err, "error injecting config") + } + } + + return nil +} + // getSize returns real size & virtual size func (daemon *Daemon) getSize(containerID string) (int64, int64) { // TODO Windows diff --git a/daemon/oci_windows.go b/daemon/oci_windows.go index 9c196c45ad..0d1d08d263 100644 --- a/daemon/oci_windows.go +++ b/daemon/oci_windows.go @@ -29,6 +29,10 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { return nil, err } + if err := daemon.setupConfigDir(c); err != nil { + return nil, err + } + // In s.Mounts mounts, err := daemon.setupMounts(c) if err != nil { @@ -44,24 +48,25 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { isHyperV = c.HostConfig.Isolation.IsHyperV() } - // If the container has not been started, and has secrets, create symlinks - // to each secret. If it has been started before, the symlinks should have - // already been created. Also, it is important to not mount a Hyper-V - // container that has been started before, to protect the host from the - // container; for example, from malicious mutation of NTFS data structures. - if !c.HasBeenStartedBefore && len(c.SecretReferences) > 0 { + // If the container has not been started, and has configs or secrets + // secrets, create symlinks to each confing and secret. If it has been + // started before, the symlinks should have already been created. Also, it + // is important to not mount a Hyper-V container that has been started + // before, to protect the host from the container; for example, from + // malicious mutation of NTFS data structures. + if !c.HasBeenStartedBefore && (len(c.SecretReferences) > 0 || len(c.ConfigReferences) > 0) { // The container file system is mounted before this function is called, // except for Hyper-V containers, so mount it here in that case. if isHyperV { if err := daemon.Mount(c); err != nil { return nil, err } + defer daemon.Unmount(c) } - err := c.CreateSecretSymlinks() - if isHyperV { - daemon.Unmount(c) + if err := c.CreateSecretSymlinks(); err != nil { + return nil, err } - if err != nil { + if err := c.CreateConfigSymlinks(); err != nil { return nil, err } } @@ -70,6 +75,10 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { mounts = append(mounts, m...) } + if m := c.ConfigMounts(); m != nil { + mounts = append(mounts, m...) + } + for _, mount := range mounts { m := specs.Mount{ Source: mount.Source,