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