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 }