Store configs that contain secrets on tmpfs

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2017-06-26 18:46:30 -07:00 committed by Brian Goff
parent cdd2e6efdb
commit cd3d0486a6
6 changed files with 131 additions and 44 deletions

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

@ -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