diff --git a/container/container.go b/container/container.go index 938d66f889..32389e3804 100644 --- a/container/container.go +++ b/container/container.go @@ -68,6 +68,13 @@ type ExitStatus struct { ExitedAt time.Time } +// ConfigReference wraps swarmtypes.ConfigReference to add a Sensitive flag. +type ConfigReference struct { + *swarmtypes.ConfigReference + // Sensitive is set if this config should not be written to disk. + Sensitive bool +} + // Container holds the structure defining a container object. type Container struct { StreamConfig *stream.Config @@ -99,7 +106,7 @@ type Container struct { ExecCommands *exec.Store `json:"-"` DependencyStore agentexec.DependencyGetter `json:"-"` SecretReferences []*swarmtypes.SecretReference - ConfigReferences []*swarmtypes.ConfigReference + ConfigReferences []*ConfigReference // logDriver for closing LogDriver logger.Logger `json:"-"` LogCopier *logger.Copier `json:"-"` @@ -1064,6 +1071,16 @@ func (container *Container) ConfigFilePath(configRef swarmtypes.ConfigReference) return filepath.Join(configs, configRef.ConfigID), nil } +// SensitiveConfigFilePath returns the path to the location of a config mounted +// as a secret. +func (container *Container) SensitiveConfigFilePath(configRef swarmtypes.ConfigReference) (string, error) { + secretMountPath, err := container.SecretMountPath() + if err != nil { + return "", err + } + return filepath.Join(secretMountPath, configRef.ConfigID+"c"), nil +} + // CreateDaemonEnvironment creates a new environment variable slice for this container. func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string) []string { // Setup environment diff --git a/container/container_unix.go b/container/container_unix.go index 6f4d91b919..ec90921acc 100644 --- a/container/container_unix.go +++ b/container/container_unix.go @@ -233,6 +233,20 @@ func (container *Container) SecretMounts() ([]Mount, error) { Writable: false, }) } + for _, r := range container.ConfigReferences { + if !r.Sensitive || r.File == nil { + continue + } + fPath, err := container.SensitiveConfigFilePath(*r.ConfigReference) + if err != nil { + return nil, err + } + mounts = append(mounts, Mount{ + Source: fPath, + Destination: r.File.Name, + Writable: false, + }) + } return mounts, nil } @@ -257,10 +271,10 @@ func (container *Container) UnmountSecrets() error { func (container *Container) ConfigMounts() ([]Mount, error) { var mounts []Mount for _, configRef := range container.ConfigReferences { - if configRef.File == nil { + if configRef.Sensitive || configRef.File == nil { continue } - src, err := container.ConfigFilePath(*configRef) + src, err := container.ConfigFilePath(*configRef.ConfigReference) if err != nil { return nil, err } diff --git a/daemon/configs.go b/daemon/configs.go index f85c51db2e..c19b7ff3fd 100644 --- a/daemon/configs.go +++ b/daemon/configs.go @@ -2,6 +2,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( swarmtypes "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/container" "github.com/sirupsen/logrus" ) @@ -17,7 +18,9 @@ func (daemon *Daemon) SetContainerConfigReferences(name string, refs []*swarmtyp return err } - c.ConfigReferences = refs + for _, ref := range refs { + c.ConfigReferences = append(c.ConfigReferences, &container.ConfigReference{ConfigReference: ref}) + } return nil } diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index abd47c807f..1523ac067c 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -19,6 +19,7 @@ import ( "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/runconfig" "github.com/docker/libnetwork" + "github.com/docker/swarmkit/template" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -160,44 +161,23 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error { return nil } -func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { +func (daemon *Daemon) setupSecretDir(c *container.Container, hasSecretDir *bool) (setupErr error) { if len(c.SecretReferences) == 0 { return nil } - localMountPath, err := c.SecretMountPath() - if err != nil { - return errors.Wrap(err, "error getting secrets mount dir") - } - logrus.Debugf("secrets: setting up secret dir: %s", localMountPath) - - // retrieve possible remapped range start for root UID, GID - rootIDs := daemon.idMappings.RootPair() - // create tmpfs - if err := idtools.MkdirAllAndChown(localMountPath, 0700, rootIDs); err != nil { - return errors.Wrap(err, "error creating secret local mount path") - } - - defer func() { - if setupErr != nil { - // cleanup - _ = detachMounted(localMountPath) - - if err := os.RemoveAll(localMountPath); err != nil { - logrus.Errorf("error cleaning up secret mount: %s", err) - } - } - }() - - tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID) - if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil { - return errors.Wrap(err, "unable to setup secret mount") + if !*hasSecretDir { + daemon.createSecretDir(c) + *hasSecretDir = true } if c.DependencyStore == nil { return fmt.Errorf("secret store is not initialized") } + // retrieve possible remapped range start for root UID, GID + rootIDs := daemon.idMappings.RootPair() + for _, s := range c.SecretReferences { // TODO (ehazlett): use type switch when more are supported if s.File == nil { @@ -244,8 +224,42 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { } } + return nil +} + +func (daemon *Daemon) createSecretDir(c *container.Container) error { + localMountPath, err := c.SecretMountPath() + if err != nil { + return err + } + logrus.Debugf("secrets: setting up secret dir: %s", localMountPath) + + // retrieve possible remapped range start for root UID, GID + rootIDs := daemon.idMappings.RootPair() + // create tmpfs + if err := idtools.MkdirAllAndChown(localMountPath, 0700, rootIDs); err != nil { + return errors.Wrap(err, "error creating secret local mount path") + } + + tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID) + if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil { + return errors.Wrap(err, "unable to setup secret mount") + } + + return nil +} + +func (daemon *Daemon) remountSecretDir(c *container.Container) error { + localMountPath, err := c.SecretMountPath() + if err != nil { + return err + } + label.Relabel(localMountPath, c.MountLabel, false) + rootIDs := daemon.idMappings.RootPair() + tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID) + // remount secrets ro if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "remount,ro,"+tmpfsOwnership); err != nil { return errors.Wrap(err, "unable to remount secret dir as readonly") @@ -254,7 +268,20 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { return nil } -func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) { +func (daemon *Daemon) cleanupSecretDir(c *container.Container) { + localMountPath, err := c.SecretMountPath() + if err != nil { + logrus.WithError(err).WithField("container", c.ID).Errorf("error getting secrets mounth path for cleanup") + } + + detachMounted(localMountPath) + + if err := os.RemoveAll(localMountPath); err != nil { + logrus.Errorf("error cleaning up secret mount: %s", err) + } +} + +func (daemon *Daemon) setupConfigDir(c *container.Container, hasSecretDir *bool) (setupErr error) { if len(c.ConfigReferences) == 0 { return nil } @@ -291,22 +318,35 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) { continue } - fPath, err := c.ConfigFilePath(*configRef) + getter := c.DependencyStore.Configs().(template.TemplatedConfigGetter) + config, sensitive, err := getter.GetAndFlagSecretData(configRef.ConfigID) if err != nil { - return err + return errors.Wrap(err, "unable to get config from config store") + } + + var fPath string + if sensitive { + configRef.Sensitive = true + fPath, err = c.SensitiveConfigFilePath(*configRef.ConfigReference) + if !*hasSecretDir { + daemon.createSecretDir(c) + *hasSecretDir = true + } + } else { + fPath, err = c.ConfigFilePath(*configRef.ConfigReference) + } + if err != nil { + return errors.Wrap(err, "error getting config file path") } log := logrus.WithFields(logrus.Fields{"name": configRef.File.Name, "path": fPath}) + log.Debug("injecting config") + if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil { return errors.Wrap(err, "error creating config path") } - log.Debug("injecting config") - config, err := c.DependencyStore.Configs().Get(configRef.ConfigID) - if err != nil { - return errors.Wrap(err, "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") } diff --git a/daemon/container_operations_windows.go b/daemon/container_operations_windows.go index e3914f9410..fe9795b8ba 100644 --- a/daemon/container_operations_windows.go +++ b/daemon/container_operations_windows.go @@ -51,7 +51,7 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) { continue } - fPath, err := c.ConfigFilePath(*configRef) + fPath, err := c.ConfigFilePath(*configRef.ConfigReference) if err != nil { return err } diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index 2a19dd5db8..e0d93d5ca4 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -760,7 +760,7 @@ func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container) return nil } -func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { +func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, err error) { s := oci.DefaultSpec() if err := daemon.populateCommonSpec(&s, c); err != nil { return nil, err @@ -842,14 +842,27 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { return nil, err } - if err := daemon.setupSecretDir(c); err != nil { + var hasSecretDir bool + defer func() { + if hasSecretDir && err != nil { + daemon.cleanupSecretDir(c) + } + }() + + if err := daemon.setupSecretDir(c, &hasSecretDir); err != nil { return nil, err } - if err := daemon.setupConfigDir(c); err != nil { + if err := daemon.setupConfigDir(c, &hasSecretDir); err != nil { return nil, err } + if hasSecretDir { + if err := daemon.remountSecretDir(c); err != nil { + return nil, err + } + } + ms, err := daemon.setupMounts(c) if err != nil { return nil, err