diff options
author | Paul Holzinger <pholzing@redhat.com> | 2022-04-29 14:41:33 +0200 |
---|---|---|
committer | Matthew Heon <matthew.heon@pm.me> | 2022-05-03 13:45:29 -0400 |
commit | 1cdf18a86b092caf5d23ddf605b23c9c143f270f (patch) | |
tree | 484ad5d68db30297b3196f414f74438c5556641f | |
parent | bbb10bb52d0acd089bf26339dbb62e4b1e1b4d59 (diff) | |
download | podman-1cdf18a86b092caf5d23ddf605b23c9c143f270f.tar.gz podman-1cdf18a86b092caf5d23ddf605b23c9c143f270f.tar.bz2 podman-1cdf18a86b092caf5d23ddf605b23c9c143f270f.zip |
fix incorrect permissions for /etc/resolv.conf in userns
The files /etc/hosts, /etc/hostname and /etc/resolv.conf should always
be owned by the root user in the container. This worked correct for
/etc/hostname and /etc/hosts but not for /etc/resolv.conf.
A container run with --userns keep-id would have the reolv.conf file
owned by the current container user which is wrong.
Consolidate some common code in a new helper function to make the code more
cleaner.
Signed-off-by: Paul Holzinger <pholzing@redhat.com>
-rw-r--r-- | libpod/container_internal_linux.go | 49 | ||||
-rw-r--r-- | test/system/500-networking.bats | 15 |
2 files changed, 36 insertions, 28 deletions
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 3c88cc75f..2eaf56c0a 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -2123,15 +2123,9 @@ func (c *Container) makeBindMounts() error { } } else { if !c.config.UseImageResolvConf { - newResolv, err := c.generateResolvConf() - if err != nil { + if err := c.generateResolvConf(); err != nil { return errors.Wrapf(err, "error creating resolv.conf for container %s", c.ID()) } - err = c.mountIntoRootDirs("/etc/resolv.conf", newResolv) - - if err != nil { - return errors.Wrapf(err, "error assigning mounts to container %s", c.ID()) - } } if !c.config.UseImageHosts { @@ -2288,7 +2282,7 @@ rootless=%d } // generateResolvConf generates a containers resolv.conf -func (c *Container) generateResolvConf() (string, error) { +func (c *Container) generateResolvConf() error { var ( nameservers []string networkNameServers []string @@ -2304,7 +2298,7 @@ func (c *Container) generateResolvConf() (string, error) { if err == nil { resolvConf = definedPath } else if !os.IsNotExist(err) { - return "", err + return err } } break @@ -2314,7 +2308,7 @@ func (c *Container) generateResolvConf() (string, error) { contents, err := ioutil.ReadFile(resolvConf) // resolv.conf doesn't have to exists if err != nil && !os.IsNotExist(err) { - return "", err + return err } ns := resolvconf.GetNameservers(contents) @@ -2324,7 +2318,7 @@ func (c *Container) generateResolvConf() (string, error) { resolvedContents, err := ioutil.ReadFile("/run/systemd/resolve/resolv.conf") if err != nil { if !os.IsNotExist(err) { - return "", errors.Wrapf(err, "detected that systemd-resolved is in use, but could not locate real resolv.conf") + return errors.Wrapf(err, "detected that systemd-resolved is in use, but could not locate real resolv.conf") } } else { contents = resolvedContents @@ -2347,21 +2341,21 @@ func (c *Container) generateResolvConf() (string, error) { ipv6, err := c.checkForIPv6(netStatus) if err != nil { - return "", err + return err } // Ensure that the container's /etc/resolv.conf is compatible with its // network configuration. resolv, err := resolvconf.FilterResolvDNS(contents, ipv6, c.config.CreateNetNS) if err != nil { - return "", errors.Wrapf(err, "error parsing host resolv.conf") + return errors.Wrapf(err, "error parsing host resolv.conf") } dns := make([]net.IP, 0, len(c.runtime.config.Containers.DNSServers)+len(c.config.DNSServer)) for _, i := range c.runtime.config.Containers.DNSServers { result := net.ParseIP(i) if result == nil { - return "", errors.Wrapf(define.ErrInvalidArg, "invalid IP address %s", i) + return errors.Wrapf(define.ErrInvalidArg, "invalid IP address %s", i) } dns = append(dns, result) } @@ -2412,20 +2406,15 @@ func (c *Container) generateResolvConf() (string, error) { destPath := filepath.Join(c.state.RunDir, "resolv.conf") if err := os.Remove(destPath); err != nil && !os.IsNotExist(err) { - return "", errors.Wrapf(err, "container %s", c.ID()) + return errors.Wrapf(err, "container %s", c.ID()) } // Build resolv.conf if _, err = resolvconf.Build(destPath, nameservers, search, options); err != nil { - return "", errors.Wrapf(err, "error building resolv.conf for container %s", c.ID()) + return errors.Wrapf(err, "error building resolv.conf for container %s", c.ID()) } - // Relabel resolv.conf for the container - if err := c.relabel(destPath, c.config.MountLabel, true); err != nil { - return "", err - } - - return destPath, nil + return c.bindMountRootFile(destPath, "/etc/resolv.conf") } // Check if a container uses IPv6. @@ -2600,17 +2589,21 @@ func (c *Container) createHosts() error { return err } - if err := os.Chown(targetFile, c.RootUID(), c.RootGID()); err != nil { + return c.bindMountRootFile(targetFile, config.DefaultHostsFile) +} + +// bindMountRootFile will chown and relabel the source file to make it usable in the container. +// It will also add the path to the container bind mount map. +// source is the path on the host, dest is the path in the container. +func (c *Container) bindMountRootFile(source, dest string) error { + if err := os.Chown(source, c.RootUID(), c.RootGID()); err != nil { return err } - if err := label.Relabel(targetFile, c.MountLabel(), false); err != nil { + if err := label.Relabel(source, c.MountLabel(), false); err != nil { return err } - if err = c.mountIntoRootDirs(config.DefaultHostsFile, targetFile); err != nil { - return err - } - return nil + return c.mountIntoRootDirs(dest, source) } // generateGroupEntry generates an entry or entries into /etc/group as diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 01571d176..c7007741b 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -723,4 +723,19 @@ EOF is "${#lines[@]}" "5" "expect 5 host entries in /etc/hosts" } +@test "podman run /etc/* permissions" { + userns="--userns=keep-id" + if ! is_rootless; then + userns="--uidmap=0:1111111:65536 --gidmap=0:1111111:65536" + fi + # check with and without userns + for userns in "" "$userns"; do + # check the /etc/hosts /etc/hostname /etc/resolv.conf are owned by root + run_podman run $userns --rm $IMAGE stat -c %u:%g /etc/hosts /etc/resolv.conf /etc/hostname + is "${lines[0]}" "0\:0" "/etc/hosts owned by root" + is "${lines[1]}" "0\:0" "/etc/resolv.conf owned by root" + is "${lines[2]}" "0\:0" "/etc/hosts owned by root" + done +} + # vim: filetype=sh |