summaryrefslogtreecommitdiff
path: root/libpod/container_internal_linux.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/container_internal_linux.go')
-rw-r--r--libpod/container_internal_linux.go136
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
+}