summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@pm.me>2020-07-15 15:25:12 -0400
committerMatthew Heon <mheon@redhat.com>2020-07-23 14:27:19 -0400
commitbae6853906c388051a49b9a43776eba97e4f0523 (patch)
treee20eace33ee1448310b8c0836528031f3172e097 /libpod
parent9051546c4df40b611ca09b02ae57ae6e8fb72c94 (diff)
downloadpodman-bae6853906c388051a49b9a43776eba97e4f0523.tar.gz
podman-bae6853906c388051a49b9a43776eba97e4f0523.tar.bz2
podman-bae6853906c388051a49b9a43776eba97e4f0523.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')
-rw-r--r--libpod/container_internal_linux.go48
1 files changed, 43 insertions, 5 deletions
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 255505416..574525315 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -1458,11 +1458,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
@@ -1488,12 +1503,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)
@@ -1542,6 +1554,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) {