diff options
Diffstat (limited to 'libpod/container_internal_linux.go')
-rw-r--r-- | libpod/container_internal_linux.go | 136 |
1 files changed, 111 insertions, 25 deletions
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index b074efa3a..c6c9ceb0c 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -18,14 +18,15 @@ import ( cnitypes "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" + "github.com/containers/buildah/pkg/secrets" crioAnnotations "github.com/containers/libpod/pkg/annotations" "github.com/containers/libpod/pkg/apparmor" "github.com/containers/libpod/pkg/criu" "github.com/containers/libpod/pkg/lookup" "github.com/containers/libpod/pkg/resolvconf" "github.com/containers/libpod/pkg/rootless" - "github.com/containers/libpod/pkg/secrets" "github.com/containers/storage/pkg/idtools" + "github.com/cyphar/filepath-securejoin" "github.com/opencontainers/runc/libcontainer/user" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" @@ -202,7 +203,8 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } // Check if the spec file mounts contain the label Relabel flags z or Z. // If they do, relabel the source directory and then remove the option. - for _, m := range g.Mounts() { + for i := range g.Config.Mounts { + m := &g.Config.Mounts[i] var options []string for _, o := range m.Options { switch o { @@ -218,6 +220,13 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } } m.Options = options + + // If we are using a user namespace, we will use an intermediate + // directory to bind mount volumes + if c.state.UserNSRoot != "" && strings.HasPrefix(m.Source, c.runtime.config.VolumePath) { + newSourceDir := filepath.Join(c.state.UserNSRoot, "volumes") + m.Source = strings.Replace(m.Source, c.runtime.config.VolumePath, newSourceDir, 1) + } } g.SetProcessSelinuxLabel(c.ProcessLabel()) @@ -366,6 +375,18 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { // For private volumes any root propagation value should work. rootPropagation := "" for _, m := range mounts { + // We need to remove all symlinks from tmpfs mounts. + // Runc and other runtimes may choke on them. + // Easy solution: use securejoin to do a scoped evaluation of + // the links, then trim off the mount prefix. + if m.Type == "tmpfs" { + finalPath, err := securejoin.SecureJoin(c.state.Mountpoint, m.Destination) + if err != nil { + return nil, errors.Wrapf(err, "error resolving symlinks for mount destination %s", m.Destination) + } + trimmedPath := strings.TrimPrefix(finalPath, strings.TrimSuffix(c.state.Mountpoint, "/")) + m.Destination = trimmedPath + } g.AddMount(m) for _, opt := range m.Options { switch opt { @@ -472,10 +493,19 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr return nil } -func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) (err error) { - +func (c *Container) checkpointRestoreSupported() (err error) { if !criu.CheckForCriu() { - return errors.Errorf("checkpointing a container requires at least CRIU %d", criu.MinCriuVersion) + return errors.Errorf("Checkpoint/Restore requires at least CRIU %d", criu.MinCriuVersion) + } + if !c.runtime.ociRuntime.featureCheckCheckpointing() { + return errors.Errorf("Configured runtime does not support checkpoint/restore") + } + return nil +} + +func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) (err error) { + if err := c.checkpointRestoreSupported(); err != nil { + return err } if c.state.State != ContainerStateRunning { @@ -532,8 +562,8 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (err error) { - if !criu.CheckForCriu() { - return errors.Errorf("restoring a container requires at least CRIU %d", criu.MinCriuVersion) + if err := c.checkpointRestoreSupported(); err != nil { + return err } if (c.state.State != ContainerStateConfigured) && (c.state.State != ContainerStateExited) { @@ -656,18 +686,21 @@ func (c *Container) makeBindMounts() error { if !netDisabled { // If /etc/resolv.conf and /etc/hosts exist, delete them so we - // will recreate - if path, ok := c.state.BindMounts["/etc/resolv.conf"]; ok { - if err := os.Remove(path); err != nil && !os.IsNotExist(err) { - return errors.Wrapf(err, "error removing container %s resolv.conf", c.ID()) + // will recreate. Only do this if we aren't sharing them with + // another container. + if c.config.NetNsCtr == "" { + if path, ok := c.state.BindMounts["/etc/resolv.conf"]; ok { + if err := os.Remove(path); err != nil && !os.IsNotExist(err) { + return errors.Wrapf(err, "error removing container %s resolv.conf", c.ID()) + } + delete(c.state.BindMounts, "/etc/resolv.conf") } - delete(c.state.BindMounts, "/etc/resolv.conf") - } - if path, ok := c.state.BindMounts["/etc/hosts"]; ok { - if err := os.Remove(path); err != nil && !os.IsNotExist(err) { - return errors.Wrapf(err, "error removing container %s hosts", c.ID()) + if path, ok := c.state.BindMounts["/etc/hosts"]; ok { + if err := os.Remove(path); err != nil && !os.IsNotExist(err) { + return errors.Wrapf(err, "error removing container %s hosts", c.ID()) + } + delete(c.state.BindMounts, "/etc/hosts") } - delete(c.state.BindMounts, "/etc/hosts") } if c.config.NetNsCtr != "" { @@ -689,13 +722,29 @@ func (c *Container) makeBindMounts() error { // If it doesn't, don't copy them resolvPath, exists := bindMounts["/etc/resolv.conf"] if exists { - c.state.BindMounts["/etc/resolv.conf"] = resolvPath } + + // check if dependency container has an /etc/hosts file hostsPath, exists := bindMounts["/etc/hosts"] - if exists { - c.state.BindMounts["/etc/hosts"] = hostsPath + if !exists { + return errors.Errorf("error finding hosts file of dependency container %s for container %s", depCtr.ID(), c.ID()) + } + + depCtr.lock.Lock() + // generate a hosts file for the dependency container, + // based on either its old hosts file, or the default, + // and add the relevant information from the new container (hosts and IP) + hostsPath, err = depCtr.appendHosts(hostsPath, c) + + if err != nil { + depCtr.lock.Unlock() + return errors.Wrapf(err, "error creating hosts file for container %s which depends on container %s", c.ID(), depCtr.ID()) } + depCtr.lock.Unlock() + + // finally, save it in the new container + c.state.BindMounts["/etc/hosts"] = hostsPath } else { newResolv, err := c.generateResolvConf() if err != nil { @@ -703,7 +752,7 @@ func (c *Container) makeBindMounts() error { } c.state.BindMounts["/etc/resolv.conf"] = newResolv - newHosts, err := c.generateHosts() + newHosts, err := c.generateHosts("/etc/hosts") if err != nil { return errors.Wrapf(err, "error creating hosts file for container %s", c.ID()) } @@ -807,6 +856,10 @@ func (c *Container) generateResolvConf() (string, error) { // Make a new resolv.conf nameservers := resolvconf.GetNameservers(resolv.Content) + // slirp4netns has a built in DNS server. + if c.config.NetMode.IsSlirp4netns() { + nameservers = append(nameservers, "10.0.2.3") + } if len(c.config.DNSServer) > 0 { // We store DNS servers as net.IP, so need to convert to string nameservers = []string{} @@ -845,12 +898,28 @@ func (c *Container) generateResolvConf() (string, error) { } // generateHosts creates a containers hosts file -func (c *Container) generateHosts() (string, error) { - orig, err := ioutil.ReadFile("/etc/hosts") +func (c *Container) generateHosts(path string) (string, error) { + orig, err := ioutil.ReadFile(path) if err != nil { - return "", errors.Wrapf(err, "unable to read /etc/hosts") + return "", errors.Wrapf(err, "unable to read %s", path) } hosts := string(orig) + hosts += c.getHosts() + return c.writeStringToRundir("hosts", hosts) +} + +// appendHosts appends a container's config and state pertaining to hosts to a container's +// local hosts file. netCtr is the container from which the netNS information is +// taken. +// path is the basis of the hosts file, into which netCtr's netNS information will be appended. +func (c *Container) appendHosts(path string, netCtr *Container) (string, error) { + return c.appendStringToRundir("hosts", netCtr.getHosts()) +} + +// getHosts finds the pertinent information for a container's host file in its config and state +// and returns a string in a format that can be written to the host file +func (c *Container) getHosts() string { + var hosts string if len(c.config.HostAdd) > 0 { for _, host := range c.config.HostAdd { // the host format has already been verified at this point @@ -862,7 +931,7 @@ func (c *Container) generateHosts() (string, error) { ipAddress := strings.Split(c.state.NetworkStatus[0].IPs[0].Address.String(), "/")[0] hosts += fmt.Sprintf("%s\t%s\n", ipAddress, c.Hostname()) } - return c.writeStringToRundir("hosts", hosts) + return hosts } // generatePasswd generates a container specific passwd file, @@ -921,3 +990,20 @@ func (c *Container) generatePasswd() (string, error) { } return passwdFile, nil } + +func (c *Container) copyOwnerAndPerms(source, dest string) error { + info, err := os.Stat(source) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return errors.Wrapf(err, "cannot stat `%s`", dest) + } + if err := os.Chmod(dest, info.Mode()); err != nil { + return errors.Wrapf(err, "cannot chmod `%s`", dest) + } + if err := os.Chown(dest, int(info.Sys().(*syscall.Stat_t).Uid), int(info.Sys().(*syscall.Stat_t).Gid)); err != nil { + return errors.Wrapf(err, "cannot chown `%s`", dest) + } + return nil +} |