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