From 04a537756d9b7b526759c02b5b5d68c135b210ea Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Tue, 9 Oct 2018 07:54:37 -0400 Subject: Generate a passwd file for users not in container If someone runs podman as a user (uid) that is not defined in the container we want generate a passwd file so that getpwuid() will work inside of container. Signed-off-by: Daniel J Walsh --- libpod/container.go | 5 ++++ libpod/container_internal.go | 66 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) (limited to 'libpod') diff --git a/libpod/container.go b/libpod/container.go index 5997c0b66..4e17b1102 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -642,6 +642,11 @@ func (c *Container) Hostname() string { return c.ID()[:12] } +// WorkingDir returns the containers working dir +func (c *Container) WorkingDir() string { + return c.config.Spec.Process.Cwd +} + // State Accessors // Require locking diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 77bba9e85..ab79aa790 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -9,6 +9,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strconv" "strings" "syscall" @@ -946,6 +947,19 @@ func (c *Container) makeBindMounts() error { } c.state.BindMounts["/etc/resolv.conf"] = newResolv + newPasswd, err := c.generatePasswd() + if err != nil { + return errors.Wrapf(err, "error creating temporary passwd file for container %s", c.ID()) + } + if newPasswd != "" { + // Make /etc/passwd + if _, ok := c.state.BindMounts["/etc/passwd"]; ok { + // If it already exists, delete so we can recreate + delete(c.state.BindMounts, "/etc/passwd") + } + logrus.Debugf("adding entry to /etc/passwd for non existent default user") + c.state.BindMounts["/etc/passwd"] = newPasswd + } // Make /etc/hosts if _, ok := c.state.BindMounts["/etc/hosts"]; ok { // If it already exists, delete so we can recreate @@ -1017,6 +1031,58 @@ func (c *Container) writeStringToRundir(destFile, output string) (string, error) return filepath.Join(c.state.DestinationRunDir, destFile), nil } +// generatePasswd generates a container specific passwd file, +// iff g.config.User is a number +func (c *Container) generatePasswd() (string, error) { + var ( + groupspec string + gid uint32 + ) + if c.config.User == "" { + return "", nil + } + spec := strings.SplitN(c.config.User, ":", 2) + userspec := spec[0] + if len(spec) > 1 { + groupspec = spec[1] + } + // If a non numeric User, then don't generate passwd + uid, err := strconv.ParseUint(userspec, 10, 32) + if err != nil { + return "", nil + } + // if UID exists inside of container rootfs /etc/passwd then + // don't generate passwd + if _, _, err := chrootuser.LookupUIDInContainer(c.state.Mountpoint, uid); err == nil { + return "", nil + } + if err == nil && groupspec != "" { + if !c.state.Mounted { + return "", errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to translate group field for passwd record", c.ID()) + } + gid, err = chrootuser.GetGroup(c.state.Mountpoint, groupspec) + if err != nil { + return "", errors.Wrapf(err, "unable to get gid from %s formporary passwd file") + } + } + + originPasswdFile := filepath.Join(c.state.Mountpoint, "/etc/passwd") + orig, err := ioutil.ReadFile(originPasswdFile) + if err != nil { + return "", errors.Wrapf(err, "unable to read passwd file %s", originPasswdFile) + } + + pwd := fmt.Sprintf("%s%d:x:%d:%d:container user:%s:/bin/sh\n", orig, uid, uid, gid, c.WorkingDir()) + passwdFile, err := c.writeStringToRundir("passwd", pwd) + if err != nil { + return "", errors.Wrapf(err, "failed to create temporary passwd fileo") + } + if os.Chmod(passwdFile, 0644); err != nil { + return "", err + } + return passwdFile, nil +} + // generateResolvConf generates a containers resolv.conf func (c *Container) generateResolvConf() (string, error) { // Determine the endpoint for resolv.conf in case it is a symlink -- cgit v1.2.3-54-g00ecf