mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Store configs that contain secrets on tmpfs
Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
parent
cdd2e6efdb
commit
cd3d0486a6
6 changed files with 131 additions and 44 deletions
|
@ -68,6 +68,13 @@ type ExitStatus struct {
|
||||||
ExitedAt time.Time
|
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.
|
// Container holds the structure defining a container object.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
StreamConfig *stream.Config
|
StreamConfig *stream.Config
|
||||||
|
@ -99,7 +106,7 @@ type Container struct {
|
||||||
ExecCommands *exec.Store `json:"-"`
|
ExecCommands *exec.Store `json:"-"`
|
||||||
DependencyStore agentexec.DependencyGetter `json:"-"`
|
DependencyStore agentexec.DependencyGetter `json:"-"`
|
||||||
SecretReferences []*swarmtypes.SecretReference
|
SecretReferences []*swarmtypes.SecretReference
|
||||||
ConfigReferences []*swarmtypes.ConfigReference
|
ConfigReferences []*ConfigReference
|
||||||
// logDriver for closing
|
// logDriver for closing
|
||||||
LogDriver logger.Logger `json:"-"`
|
LogDriver logger.Logger `json:"-"`
|
||||||
LogCopier *logger.Copier `json:"-"`
|
LogCopier *logger.Copier `json:"-"`
|
||||||
|
@ -1064,6 +1071,16 @@ func (container *Container) ConfigFilePath(configRef swarmtypes.ConfigReference)
|
||||||
return filepath.Join(configs, configRef.ConfigID), nil
|
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.
|
// CreateDaemonEnvironment creates a new environment variable slice for this container.
|
||||||
func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string) []string {
|
func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string) []string {
|
||||||
// Setup environment
|
// Setup environment
|
||||||
|
|
|
@ -233,6 +233,20 @@ func (container *Container) SecretMounts() ([]Mount, error) {
|
||||||
Writable: false,
|
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
|
return mounts, nil
|
||||||
}
|
}
|
||||||
|
@ -257,10 +271,10 @@ func (container *Container) UnmountSecrets() error {
|
||||||
func (container *Container) ConfigMounts() ([]Mount, error) {
|
func (container *Container) ConfigMounts() ([]Mount, error) {
|
||||||
var mounts []Mount
|
var mounts []Mount
|
||||||
for _, configRef := range container.ConfigReferences {
|
for _, configRef := range container.ConfigReferences {
|
||||||
if configRef.File == nil {
|
if configRef.Sensitive || configRef.File == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
src, err := container.ConfigFilePath(*configRef)
|
src, err := container.ConfigFilePath(*configRef.ConfigReference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package daemon // import "github.com/docker/docker/daemon"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/docker/docker/container"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,7 +18,9 @@ func (daemon *Daemon) SetContainerConfigReferences(name string, refs []*swarmtyp
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ConfigReferences = refs
|
for _, ref := range refs {
|
||||||
|
c.ConfigReferences = append(c.ConfigReferences, &container.ConfigReference{ConfigReference: ref})
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/libnetwork"
|
"github.com/docker/libnetwork"
|
||||||
|
"github.com/docker/swarmkit/template"
|
||||||
"github.com/opencontainers/selinux/go-selinux/label"
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -160,44 +161,23 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
|
||||||
return nil
|
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 {
|
if len(c.SecretReferences) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
localMountPath, err := c.SecretMountPath()
|
if !*hasSecretDir {
|
||||||
if err != nil {
|
daemon.createSecretDir(c)
|
||||||
return errors.Wrap(err, "error getting secrets mount dir")
|
*hasSecretDir = true
|
||||||
}
|
|
||||||
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 c.DependencyStore == nil {
|
if c.DependencyStore == nil {
|
||||||
return fmt.Errorf("secret store is not initialized")
|
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 {
|
for _, s := range c.SecretReferences {
|
||||||
// TODO (ehazlett): use type switch when more are supported
|
// TODO (ehazlett): use type switch when more are supported
|
||||||
if s.File == nil {
|
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)
|
label.Relabel(localMountPath, c.MountLabel, false)
|
||||||
|
|
||||||
|
rootIDs := daemon.idMappings.RootPair()
|
||||||
|
tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID)
|
||||||
|
|
||||||
// remount secrets ro
|
// remount secrets ro
|
||||||
if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "remount,ro,"+tmpfsOwnership); err != nil {
|
if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "remount,ro,"+tmpfsOwnership); err != nil {
|
||||||
return errors.Wrap(err, "unable to remount secret dir as readonly")
|
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
|
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 {
|
if len(c.ConfigReferences) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -291,22 +318,35 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fPath, err := c.ConfigFilePath(*configRef)
|
getter := c.DependencyStore.Configs().(template.TemplatedConfigGetter)
|
||||||
|
config, sensitive, err := getter.GetAndFlagSecretData(configRef.ConfigID)
|
||||||
if err != nil {
|
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 := 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 {
|
if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil {
|
||||||
return errors.Wrap(err, "error creating config path")
|
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 {
|
if err := ioutil.WriteFile(fPath, config.Spec.Data, configRef.File.Mode); err != nil {
|
||||||
return errors.Wrap(err, "error injecting config")
|
return errors.Wrap(err, "error injecting config")
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fPath, err := c.ConfigFilePath(*configRef)
|
fPath, err := c.ConfigFilePath(*configRef.ConfigReference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -760,7 +760,7 @@ func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container)
|
||||||
return nil
|
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()
|
s := oci.DefaultSpec()
|
||||||
if err := daemon.populateCommonSpec(&s, c); err != nil {
|
if err := daemon.populateCommonSpec(&s, c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -842,14 +842,27 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
|
||||||
return nil, err
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := daemon.setupConfigDir(c); err != nil {
|
if err := daemon.setupConfigDir(c, &hasSecretDir); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hasSecretDir {
|
||||||
|
if err := daemon.remountSecretDir(c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ms, err := daemon.setupMounts(c)
|
ms, err := daemon.setupMounts(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
Loading…
Add table
Reference in a new issue