Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make changes to /etc/passwd on disk for non-read only #6991

Merged
merged 1 commit into from
Jul 29, 2020

Conversation

mheon
Copy link
Member

@mheon mheon commented Jul 15, 2020

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

@openshift-ci-robot
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: mheon

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci-robot openshift-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 15, 2020
Copy link
Member

@edsantiago edsantiago left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work. A few questions for you to ponder.

}
if err == nil {
return "", nil
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could these two ifs be refactored into one?

if err != User.ErrNoPasswdEntries {
    return "", err
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I find it easier to read with the err == nil case handled explicitly, but either is fine.)

}
}
if hasHomedir {
homeDir = u.HomeDir
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to set WORKINGDIR here too (if unset)?

$ ./bin/podman run -it --rm --userns=keep-id --security-opt label=disable -v /home/esm:/home/esm alpine_labels sh
/ $ pwd
/         <--- I kind of expected /home/esm
/ $ tail -1 /etc/passwd
esm:x:14904:14904:esm:/home/esm:/bin/sh     <--- it was detected
/ $ ls -la /home/esm
[as expected]                            <---- and it exists

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm reluctant to reset workdir given it could have been deliberately set by the user - I don't have a way of telling that this deep in the code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If workingdir == "" then we know it was never set.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By this point, in the spec, it's already been defaulted to / if it was not set by the user.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corner case: would it make sense to do lookup by UID instead of name? This is super-unlikely, but if the podman user has a UID already defined in the image's /etc/passwd, appending a new entry is going to be mostly a NOP: ls and ps and anything that does lookups is going to show the preexisting username.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered that, and decided it's probably not worth the potential performance hit of a duplicate lookup.

@mheon mheon force-pushed the change_passwd_ondisk branch from b0beef7 to 131b6a7 Compare July 15, 2020 20:28
@rhatdan
Copy link
Member

rhatdan commented Jul 15, 2020

Nice work @mheon, I was thinking of doing this myself. Glad you did it.

@rhatdan
Copy link
Member

rhatdan commented Jul 15, 2020

Does this work for both use cases.

if I specify a --user 1234, we add an entry for 1234 to /etc/passwd.

If you use --userns=keepid we add your entry to /etc/passwd.

@mheon
Copy link
Member Author

mheon commented Jul 15, 2020

Should work for both - this happens after we've already generated both entries for /etc/passwd and the only difference is where we write them to (a new file or the existing passwd)

@rhatdan
Copy link
Member

rhatdan commented Jul 15, 2020

@owtaylor FYI

Copy link
Member

@TomSweeneyRedHat TomSweeneyRedHat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests don't look too hip.
The changes LGTM for what you're trying to do, however, mucking around with /etc/passwd gives me the heebie-jeebies. I'll nod to @rhatdan and @mtrmac on the safeness of that.

@mheon mheon force-pushed the change_passwd_ondisk branch from 131b6a7 to a804ef4 Compare July 16, 2020 15:31
@mheon
Copy link
Member Author

mheon commented Jul 16, 2020

Added a further test and un-broke the existing passwd tests (used read-only mode to force us back to the old bind mount behavior).

@mheon mheon force-pushed the change_passwd_ondisk branch from a804ef4 to 325acd2 Compare July 16, 2020 17:12
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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(A pedantic note no-one should care about: POSIX does allow all-numeric usernames, with the username different from the UID value. In user input like the chown CLI, where the input is ambiguous, the input is defined to be interpreted as a username if it exists, in preference to UID.

Either way lookup.GetUser parsing the string and guessing whether the caller meant a name or an UID does the wrong thing, when we know the input is a name.)

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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the user does exist inside the container, but with a different UID?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we end up with duplicate entries in that case, with this one being the second of the two. I don't think that's completely disastrous, but it's definitely not ideal.

}

// If the user's actual home directory exists, or was mounted in - use
// that.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting, but somewhat out of scope.

  • Is it safe? I can easily imagine containers that assume that ~ == WORKDIR.
  • Also, is it OK to give the container this information about the outside? There is the precedent of telling the container the user name already, sure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The primary case I was trying to capture here was pet containers (Toolbox) which explicitly bind-mount in the user's /home. I think we're safe to set in that case, given the user's /home was mounted in explicitly at container creation.

Dropping the case where we set /home if it exists in the container rootfs would simplify things and prevent the leakage when the home directory was not explicitly added, so I think I'll do that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: Dropped down to just an explicit mount.

@mheon mheon force-pushed the change_passwd_ondisk branch 3 times, most recently from 6632218 to beab905 Compare July 17, 2020 18:23
@mheon
Copy link
Member Author

mheon commented Jul 17, 2020

Finally green

@mheon
Copy link
Member Author

mheon commented Jul 23, 2020

@rhatdan @vrothberg @TomSweeneyRedHat @jwhonce PTAL and merge

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 containers#6953

Signed-off-by: Matthew Heon <[email protected]>
@mheon mheon force-pushed the change_passwd_ondisk branch from beab905 to bae6853 Compare July 23, 2020 18:27
@rhatdan
Copy link
Member

rhatdan commented Jul 23, 2020

LGTM
I think we should think of some corner cases like @mtrmac Talks about, but we have those already.

@rhatdan
Copy link
Member

rhatdan commented Jul 29, 2020

@edsantiago
Copy link
Member

LGTM

@rhatdan
Copy link
Member

rhatdan commented Jul 29, 2020

/lgtm

@openshift-ci-robot openshift-ci-robot added the lgtm Indicates that a PR is ready to be merged. label Jul 29, 2020
@openshift-merge-robot openshift-merge-robot merged commit 044a7cb into containers:master Jul 29, 2020
edsantiago added a commit to edsantiago/libpod that referenced this pull request Jul 30, 2020
 - New test for containers#6991 - passwd file is writable even when
   run with --userns=keep-id

 - Enable another keep-id test, commented out due to containers#6593

 - New test for podman system df

Also, independently, removed this line:

   apt-get -y upgrade conmon

...because it's causing CI failures, probably because of the
boothole CVE, probably because the Ubuntu grub update was
rushed out. I believe it is safe to remove this, because
both Ubuntu 19 and 20 report:

   conmon is already the newest version (2.0.18~1).

Signed-off-by: Ed Santiago <[email protected]>
@github-actions github-actions bot added the locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments. label Sep 24, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

podman exec: Error: unable to find user $USER: no matching entries in passwd file
7 participants