diff options
author | Matthew Heon <matthew.heon@pm.me> | 2020-07-15 15:25:12 -0400 |
---|---|---|
committer | Valentin Rothberg <rothberg@redhat.com> | 2020-08-11 15:06:37 +0200 |
commit | ca00601b14f2253e5b9f89280d39c5d498efc9f3 (patch) | |
tree | c056c435a15d9a8d261d75c76253384dfc33c75f /libpod/container_internal_linux.go | |
parent | 3dfd8630a51a37734ad8c51162c4d004b8ffffb2 (diff) | |
download | podman-ca00601b14f2253e5b9f89280d39c5d498efc9f3.tar.gz podman-ca00601b14f2253e5b9f89280d39c5d498efc9f3.tar.bz2 podman-ca00601b14f2253e5b9f89280d39c5d498efc9f3.zip |
Make changes to /etc/passwd on disk for non-read only
Bind-mounting /etc/passwd into the container is problematic
becuase of how system utilities like `useradd` work. They want
to make a copy and then rename to try to prevent breakage; this
is, unfortunately, impossible when the file they want to rename
is a bind mount. The current behavior is fine for read-only
containers, though, because we expect useradd to fail in those
cases.
Instead of bind-mounting, we can edit /etc/passwd in the
container's rootfs. This is kind of gross, because the change
will show up in `podman diff` and similar tools, and will be
included in images made by `podman commit`. However, it's a lot
better than breaking important system tools.
Fixes #6953
Signed-off-by: Matthew Heon <matthew.heon@pm.me>
Diffstat (limited to 'libpod/container_internal_linux.go')
-rw-r--r-- | libpod/container_internal_linux.go | 48 |
1 files changed, 43 insertions, 5 deletions
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index f86903fd1..d72e3ad47 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -1428,11 +1428,26 @@ func (c *Container) generateCurrentUserPasswdEntry() (string, error) { if uid == 0 { return "", nil } + u, err := user.LookupId(strconv.Itoa(rootless.GetRootlessUID())) if err != nil { return "", errors.Wrapf(err, "failed to get current user") } - return fmt.Sprintf("%s:x:%s:%s:%s:%s:/bin/sh\n", u.Username, u.Uid, u.Gid, u.Username, c.WorkingDir()), nil + + // Lookup the user to see if it exists in the container image. + _, err = lookup.GetUser(c.state.Mountpoint, u.Username) + if err != User.ErrNoPasswdEntries { + return "", err + } + + // If the user's actual home directory exists, or was mounted in - use + // that. + homeDir := c.WorkingDir() + if MountExists(c.config.Spec.Mounts, u.HomeDir) { + homeDir = u.HomeDir + } + + return fmt.Sprintf("%s:x:%s:%s:%s:%s:/bin/sh\n", u.Username, u.Uid, u.Gid, u.Username, homeDir), nil } // generateUserPasswdEntry generates an /etc/passwd entry for the container user @@ -1458,12 +1473,9 @@ func (c *Container) generateUserPasswdEntry() (string, error) { // Lookup the user to see if it exists in the container image _, err = lookup.GetUser(c.state.Mountpoint, userspec) - if err != nil && err != User.ErrNoPasswdEntries { + if err != User.ErrNoPasswdEntries { return "", err } - if err == nil { - return "", nil - } if groupspec != "" { ugid, err := strconv.ParseUint(groupspec, 10, 32) @@ -1504,6 +1516,32 @@ func (c *Container) generatePasswd() (string, error) { if pwd == "" { return "", nil } + + // If we are *not* read-only - edit /etc/passwd in the container. + // This is *gross* (shows up in changes to the container, will be + // committed to images based on the container) but it actually allows us + // to add users to the container (a bind mount breaks useradd). + // We should never get here twice, because generateUserPasswdEntry will + // not return anything if the user already exists in /etc/passwd. + if !c.IsReadOnly() { + containerPasswd, err := securejoin.SecureJoin(c.state.Mountpoint, "/etc/passwd") + if err != nil { + return "", errors.Wrapf(err, "error looking up location of container %s /etc/passwd", c.ID()) + } + + f, err := os.OpenFile(containerPasswd, os.O_APPEND|os.O_WRONLY, 0600) + if err != nil { + return "", errors.Wrapf(err, "error opening container %s /etc/passwd", c.ID()) + } + defer f.Close() + + if _, err := f.WriteString(pwd); err != nil { + return "", errors.Wrapf(err, "unable to append to container %s /etc/passwd", c.ID()) + } + + return "", nil + } + originPasswdFile := filepath.Join(c.state.Mountpoint, "/etc/passwd") orig, err := ioutil.ReadFile(originPasswdFile) if err != nil && !os.IsNotExist(err) { |