github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/daemon/container_operations_unix.go (about)

     1  // +build linux freebsd
     2  
     3  package daemon // import "github.com/docker/docker/daemon"
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"strconv"
    12  	"time"
    13  
    14  	"github.com/docker/docker/container"
    15  	"github.com/docker/docker/daemon/links"
    16  	"github.com/docker/docker/errdefs"
    17  	"github.com/docker/docker/pkg/idtools"
    18  	"github.com/docker/docker/pkg/stringid"
    19  	"github.com/docker/docker/pkg/system"
    20  	"github.com/docker/docker/runconfig"
    21  	"github.com/docker/libnetwork"
    22  	"github.com/moby/sys/mount"
    23  	"github.com/opencontainers/selinux/go-selinux/label"
    24  	"github.com/pkg/errors"
    25  	"github.com/sirupsen/logrus"
    26  	"golang.org/x/sys/unix"
    27  )
    28  
    29  func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
    30  	var env []string
    31  	children := daemon.children(container)
    32  
    33  	bridgeSettings := container.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()]
    34  	if bridgeSettings == nil || bridgeSettings.EndpointSettings == nil {
    35  		return nil, nil
    36  	}
    37  
    38  	for linkAlias, child := range children {
    39  		if !child.IsRunning() {
    40  			return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
    41  		}
    42  
    43  		childBridgeSettings := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()]
    44  		if childBridgeSettings == nil || childBridgeSettings.EndpointSettings == nil {
    45  			return nil, fmt.Errorf("container %s not attached to default bridge network", child.ID)
    46  		}
    47  
    48  		link := links.NewLink(
    49  			bridgeSettings.IPAddress,
    50  			childBridgeSettings.IPAddress,
    51  			linkAlias,
    52  			child.Config.Env,
    53  			child.Config.ExposedPorts,
    54  		)
    55  
    56  		env = append(env, link.ToEnv()...)
    57  	}
    58  
    59  	return env, nil
    60  }
    61  
    62  func (daemon *Daemon) getIpcContainer(id string) (*container.Container, error) {
    63  	errMsg := "can't join IPC of container " + id
    64  	// Check the container exists
    65  	ctr, err := daemon.GetContainer(id)
    66  	if err != nil {
    67  		return nil, errors.Wrap(err, errMsg)
    68  	}
    69  	// Check the container is running and not restarting
    70  	if err := daemon.checkContainer(ctr, containerIsRunning, containerIsNotRestarting); err != nil {
    71  		return nil, errors.Wrap(err, errMsg)
    72  	}
    73  	// Check the container ipc is shareable
    74  	if st, err := os.Stat(ctr.ShmPath); err != nil || !st.IsDir() {
    75  		if err == nil || os.IsNotExist(err) {
    76  			return nil, errors.New(errMsg + ": non-shareable IPC (hint: use IpcMode:shareable for the donor container)")
    77  		}
    78  		// stat() failed?
    79  		return nil, errors.Wrap(err, errMsg+": unexpected error from stat "+ctr.ShmPath)
    80  	}
    81  
    82  	return ctr, nil
    83  }
    84  
    85  func (daemon *Daemon) getPidContainer(ctr *container.Container) (*container.Container, error) {
    86  	containerID := ctr.HostConfig.PidMode.Container()
    87  	ctr, err := daemon.GetContainer(containerID)
    88  	if err != nil {
    89  		return nil, errors.Wrapf(err, "cannot join PID of a non running container: %s", containerID)
    90  	}
    91  	return ctr, daemon.checkContainer(ctr, containerIsRunning, containerIsNotRestarting)
    92  }
    93  
    94  func containerIsRunning(c *container.Container) error {
    95  	if !c.IsRunning() {
    96  		return errdefs.Conflict(errors.Errorf("container %s is not running", c.ID))
    97  	}
    98  	return nil
    99  }
   100  
   101  func containerIsNotRestarting(c *container.Container) error {
   102  	if c.IsRestarting() {
   103  		return errContainerIsRestarting(c.ID)
   104  	}
   105  	return nil
   106  }
   107  
   108  func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
   109  	ipcMode := c.HostConfig.IpcMode
   110  
   111  	switch {
   112  	case ipcMode.IsContainer():
   113  		ic, err := daemon.getIpcContainer(ipcMode.Container())
   114  		if err != nil {
   115  			return err
   116  		}
   117  		c.ShmPath = ic.ShmPath
   118  
   119  	case ipcMode.IsHost():
   120  		if _, err := os.Stat("/dev/shm"); err != nil {
   121  			return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host")
   122  		}
   123  		c.ShmPath = "/dev/shm"
   124  
   125  	case ipcMode.IsPrivate(), ipcMode.IsNone():
   126  		// c.ShmPath will/should not be used, so make it empty.
   127  		// Container's /dev/shm mount comes from OCI spec.
   128  		c.ShmPath = ""
   129  
   130  	case ipcMode.IsEmpty():
   131  		// A container was created by an older version of the daemon.
   132  		// The default behavior used to be what is now called "shareable".
   133  		fallthrough
   134  
   135  	case ipcMode.IsShareable():
   136  		rootIDs := daemon.idMapping.RootPair()
   137  		if !c.HasMountFor("/dev/shm") {
   138  			shmPath, err := c.ShmResourcePath()
   139  			if err != nil {
   140  				return err
   141  			}
   142  
   143  			if err := idtools.MkdirAllAndChown(shmPath, 0700, rootIDs); err != nil {
   144  				return err
   145  			}
   146  
   147  			shmproperty := "mode=1777,size=" + strconv.FormatInt(c.HostConfig.ShmSize, 10)
   148  			if err := unix.Mount("shm", shmPath, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), label.FormatMountLabel(shmproperty, c.GetMountLabel())); err != nil {
   149  				return fmt.Errorf("mounting shm tmpfs: %s", err)
   150  			}
   151  			if err := os.Chown(shmPath, rootIDs.UID, rootIDs.GID); err != nil {
   152  				return err
   153  			}
   154  			c.ShmPath = shmPath
   155  		}
   156  
   157  	default:
   158  		return fmt.Errorf("invalid IPC mode: %v", ipcMode)
   159  	}
   160  
   161  	return nil
   162  }
   163  
   164  func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
   165  	if len(c.SecretReferences) == 0 && len(c.ConfigReferences) == 0 {
   166  		return nil
   167  	}
   168  
   169  	if err := daemon.createSecretsDir(c); err != nil {
   170  		return err
   171  	}
   172  	defer func() {
   173  		if setupErr != nil {
   174  			daemon.cleanupSecretDir(c)
   175  		}
   176  	}()
   177  
   178  	if c.DependencyStore == nil {
   179  		return fmt.Errorf("secret store is not initialized")
   180  	}
   181  
   182  	// retrieve possible remapped range start for root UID, GID
   183  	rootIDs := daemon.idMapping.RootPair()
   184  
   185  	for _, s := range c.SecretReferences {
   186  		// TODO (ehazlett): use type switch when more are supported
   187  		if s.File == nil {
   188  			logrus.Error("secret target type is not a file target")
   189  			continue
   190  		}
   191  
   192  		// secrets are created in the SecretMountPath on the host, at a
   193  		// single level
   194  		fPath, err := c.SecretFilePath(*s)
   195  		if err != nil {
   196  			return errors.Wrap(err, "error getting secret file path")
   197  		}
   198  		if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil {
   199  			return errors.Wrap(err, "error creating secret mount path")
   200  		}
   201  
   202  		logrus.WithFields(logrus.Fields{
   203  			"name": s.File.Name,
   204  			"path": fPath,
   205  		}).Debug("injecting secret")
   206  		secret, err := c.DependencyStore.Secrets().Get(s.SecretID)
   207  		if err != nil {
   208  			return errors.Wrap(err, "unable to get secret from secret store")
   209  		}
   210  		if err := ioutil.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil {
   211  			return errors.Wrap(err, "error injecting secret")
   212  		}
   213  
   214  		uid, err := strconv.Atoi(s.File.UID)
   215  		if err != nil {
   216  			return err
   217  		}
   218  		gid, err := strconv.Atoi(s.File.GID)
   219  		if err != nil {
   220  			return err
   221  		}
   222  
   223  		if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil {
   224  			return errors.Wrap(err, "error setting ownership for secret")
   225  		}
   226  		if err := os.Chmod(fPath, s.File.Mode); err != nil {
   227  			return errors.Wrap(err, "error setting file mode for secret")
   228  		}
   229  	}
   230  
   231  	for _, configRef := range c.ConfigReferences {
   232  		// TODO (ehazlett): use type switch when more are supported
   233  		if configRef.File == nil {
   234  			// Runtime configs are not mounted into the container, but they're
   235  			// a valid type of config so we should not error when we encounter
   236  			// one.
   237  			if configRef.Runtime == nil {
   238  				logrus.Error("config target type is not a file or runtime target")
   239  			}
   240  			// However, in any case, this isn't a file config, so we have no
   241  			// further work to do
   242  			continue
   243  		}
   244  
   245  		fPath, err := c.ConfigFilePath(*configRef)
   246  		if err != nil {
   247  			return errors.Wrap(err, "error getting config file path for container")
   248  		}
   249  		if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0700, rootIDs); err != nil {
   250  			return errors.Wrap(err, "error creating config mount path")
   251  		}
   252  
   253  		logrus.WithFields(logrus.Fields{
   254  			"name": configRef.File.Name,
   255  			"path": fPath,
   256  		}).Debug("injecting config")
   257  		config, err := c.DependencyStore.Configs().Get(configRef.ConfigID)
   258  		if err != nil {
   259  			return errors.Wrap(err, "unable to get config from config store")
   260  		}
   261  		if err := ioutil.WriteFile(fPath, config.Spec.Data, configRef.File.Mode); err != nil {
   262  			return errors.Wrap(err, "error injecting config")
   263  		}
   264  
   265  		uid, err := strconv.Atoi(configRef.File.UID)
   266  		if err != nil {
   267  			return err
   268  		}
   269  		gid, err := strconv.Atoi(configRef.File.GID)
   270  		if err != nil {
   271  			return err
   272  		}
   273  
   274  		if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil {
   275  			return errors.Wrap(err, "error setting ownership for config")
   276  		}
   277  		if err := os.Chmod(fPath, configRef.File.Mode); err != nil {
   278  			return errors.Wrap(err, "error setting file mode for config")
   279  		}
   280  	}
   281  
   282  	return daemon.remountSecretDir(c)
   283  }
   284  
   285  // createSecretsDir is used to create a dir suitable for storing container secrets.
   286  // In practice this is using a tmpfs mount and is used for both "configs" and "secrets"
   287  func (daemon *Daemon) createSecretsDir(c *container.Container) error {
   288  	// retrieve possible remapped range start for root UID, GID
   289  	rootIDs := daemon.idMapping.RootPair()
   290  	dir, err := c.SecretMountPath()
   291  	if err != nil {
   292  		return errors.Wrap(err, "error getting container secrets dir")
   293  	}
   294  
   295  	// create tmpfs
   296  	if err := idtools.MkdirAllAndChown(dir, 0700, rootIDs); err != nil {
   297  		return errors.Wrap(err, "error creating secret local mount path")
   298  	}
   299  
   300  	tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID)
   301  	if err := mount.Mount("tmpfs", dir, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil {
   302  		return errors.Wrap(err, "unable to setup secret mount")
   303  	}
   304  	return nil
   305  }
   306  
   307  func (daemon *Daemon) remountSecretDir(c *container.Container) error {
   308  	dir, err := c.SecretMountPath()
   309  	if err != nil {
   310  		return errors.Wrap(err, "error getting container secrets path")
   311  	}
   312  	if err := label.Relabel(dir, c.MountLabel, false); err != nil {
   313  		logrus.WithError(err).WithField("dir", dir).Warn("Error while attempting to set selinux label")
   314  	}
   315  	rootIDs := daemon.idMapping.RootPair()
   316  	tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID)
   317  
   318  	// remount secrets ro
   319  	if err := mount.Mount("tmpfs", dir, "tmpfs", "remount,ro,"+tmpfsOwnership); err != nil {
   320  		return errors.Wrap(err, "unable to remount dir as readonly")
   321  	}
   322  
   323  	return nil
   324  }
   325  
   326  func (daemon *Daemon) cleanupSecretDir(c *container.Container) {
   327  	dir, err := c.SecretMountPath()
   328  	if err != nil {
   329  		logrus.WithError(err).WithField("container", c.ID).Warn("error getting secrets mount path for container")
   330  	}
   331  	if err := mount.RecursiveUnmount(dir); err != nil {
   332  		logrus.WithField("dir", dir).WithError(err).Warn("Error while attempting to unmount dir, this may prevent removal of container.")
   333  	}
   334  	if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) {
   335  		logrus.WithField("dir", dir).WithError(err).Error("Error removing dir.")
   336  	}
   337  }
   338  
   339  func killProcessDirectly(cntr *container.Container) error {
   340  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   341  	defer cancel()
   342  
   343  	// Block until the container to stops or timeout.
   344  	status := <-cntr.Wait(ctx, container.WaitConditionNotRunning)
   345  	if status.Err() != nil {
   346  		// Ensure that we don't kill ourselves
   347  		if pid := cntr.GetPID(); pid != 0 {
   348  			logrus.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(cntr.ID))
   349  			if err := unix.Kill(pid, 9); err != nil {
   350  				if err != unix.ESRCH {
   351  					return err
   352  				}
   353  				e := errNoSuchProcess{pid, 9}
   354  				logrus.Debug(e)
   355  				return e
   356  			}
   357  
   358  			// In case there were some exceptions(e.g., state of zombie and D)
   359  			if system.IsProcessAlive(pid) {
   360  
   361  				// Since we can not kill a zombie pid, add zombie check here
   362  				isZombie, err := system.IsProcessZombie(pid)
   363  				if err != nil {
   364  					logrus.Warnf("Container %s state is invalid", stringid.TruncateID(cntr.ID))
   365  					return err
   366  				}
   367  				if isZombie {
   368  					return errdefs.System(errors.Errorf("container %s PID %d is zombie and can not be killed. Use the --init option when creating containers to run an init inside the container that forwards signals and reaps processes", stringid.TruncateID(cntr.ID), pid))
   369  				}
   370  			}
   371  		}
   372  	}
   373  	return nil
   374  }
   375  
   376  func isLinkable(child *container.Container) bool {
   377  	// A container is linkable only if it belongs to the default network
   378  	_, ok := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()]
   379  	return ok
   380  }
   381  
   382  func enableIPOnPredefinedNetwork() bool {
   383  	return false
   384  }
   385  
   386  // serviceDiscoveryOnDefaultNetwork indicates if service discovery is supported on the default network
   387  func serviceDiscoveryOnDefaultNetwork() bool {
   388  	return false
   389  }
   390  
   391  func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
   392  	var err error
   393  
   394  	if container.HostConfig.NetworkMode.IsHost() {
   395  		// Point to the host files, so that will be copied into the container running in host mode
   396  		*sboxOptions = append(*sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts"))
   397  	}
   398  
   399  	// Copy the host's resolv.conf for the container (/etc/resolv.conf or /run/systemd/resolve/resolv.conf)
   400  	*sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath(daemon.configStore.GetResolvConf()))
   401  
   402  	container.HostsPath, err = container.GetRootResourcePath("hosts")
   403  	if err != nil {
   404  		return err
   405  	}
   406  	*sboxOptions = append(*sboxOptions, libnetwork.OptionHostsPath(container.HostsPath))
   407  
   408  	container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf")
   409  	if err != nil {
   410  		return err
   411  	}
   412  	*sboxOptions = append(*sboxOptions, libnetwork.OptionResolvConfPath(container.ResolvConfPath))
   413  	return nil
   414  }
   415  
   416  func (daemon *Daemon) initializeNetworkingPaths(container *container.Container, nc *container.Container) error {
   417  	container.HostnamePath = nc.HostnamePath
   418  	container.HostsPath = nc.HostsPath
   419  	container.ResolvConfPath = nc.ResolvConfPath
   420  	return nil
   421  }
   422  
   423  func (daemon *Daemon) setupContainerMountsRoot(c *container.Container) error {
   424  	// get the root mount path so we can make it unbindable
   425  	p, err := c.MountsResourcePath("")
   426  	if err != nil {
   427  		return err
   428  	}
   429  	return idtools.MkdirAllAndChown(p, 0700, daemon.idMapping.RootPair())
   430  }