-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Make changes to /etc/passwd on disk for non-read only #6991
Conversation
[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 |
There was a problem hiding this 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.
libpod/container_internal_linux.go
Outdated
} | ||
if err == nil { | ||
return "", nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could these two if
s be refactored into one?
if err != User.ErrNoPasswdEntries {
return "", err
}
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
b0beef7
to
131b6a7
Compare
Nice work @mheon, I was thinking of doing this myself. Glad you did it. |
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. |
Should work for both - this happens after we've already generated both entries for |
@owtaylor FYI |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
131b6a7
to
a804ef4
Compare
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). |
a804ef4
to
325acd2
Compare
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) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
6632218
to
beab905
Compare
Finally green |
@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]>
beab905
to
bae6853
Compare
LGTM |
LGTM |
/lgtm |
- 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]>
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 bypodman commit
. However, it's a lot better than breaking important system tools.Fixes #6953