libpod: --user works with --hostuser entries

create the /etc/passwd and /etc/group files before any user/group
lookup so that the entries added dynamically are found by --user.

As a side effect, do not automatically create the group with same
value as the uid when not specified, since it is expected to run with
gid=0.

Closes: https://github.com/containers/podman/issues/25805

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
Giuseppe Scrivano
2025-04-07 11:20:04 +02:00
parent 40d7ab19f5
commit 85024a9ba7
4 changed files with 23 additions and 23 deletions

View File

@ -195,15 +195,15 @@ func (c *Container) generateSpec(ctx context.Context) (s *spec.Spec, cleanupFunc
cleanupFunc() cleanupFunc()
} }
}() }()
if err := c.makeBindMounts(); err != nil {
return nil, nil, err
}
overrides := c.getUserOverrides() overrides := c.getUserOverrides()
execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.config.User, overrides) execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.config.User, overrides)
if err != nil { if err != nil {
if slices.Contains(c.config.HostUsers, c.config.User) { return nil, nil, err
execUser, err = lookupHostUser(c.config.User)
}
if err != nil {
return nil, nil, err
}
} }
// NewFromSpec() is deprecated according to its comment // NewFromSpec() is deprecated according to its comment
@ -236,10 +236,6 @@ func (c *Container) generateSpec(ctx context.Context) (s *spec.Spec, cleanupFunc
g.SetProcessApparmorProfile(updatedProfile) g.SetProcessApparmorProfile(updatedProfile)
} }
if err := c.makeBindMounts(); err != nil {
return nil, nil, err
}
if err := c.mountNotifySocket(g); err != nil { if err := c.mountNotifySocket(g); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -2434,7 +2430,7 @@ func (c *Container) generateGroupEntry() (string, error) {
// Things we *can't* handle: adding the user we added in // Things we *can't* handle: adding the user we added in
// generatePasswdEntry to any *existing* groups. // generatePasswdEntry to any *existing* groups.
addedGID := 0 addedGID := -1
if c.config.AddCurrentUserPasswdEntry { if c.config.AddCurrentUserPasswdEntry {
entry, gid, err := c.generateCurrentUserGroupEntry() entry, gid, err := c.generateCurrentUserGroupEntry()
if err != nil { if err != nil {
@ -2503,7 +2499,7 @@ func (c *Container) generateUserGroupEntry(addedGID int) (string, error) {
} }
splitUser := strings.SplitN(c.config.User, ":", 2) splitUser := strings.SplitN(c.config.User, ":", 2)
group := splitUser[0] group := "0"
if len(splitUser) > 1 { if len(splitUser) > 1 {
group = splitUser[1] group = splitUser[1]
} }
@ -2513,7 +2509,7 @@ func (c *Container) generateUserGroupEntry(addedGID int) (string, error) {
return "", nil //nolint: nilerr return "", nil //nolint: nilerr
} }
if addedGID != 0 && addedGID == int(gid) { if addedGID != -1 && addedGID == int(gid) {
return "", nil return "", nil
} }

View File

@ -47,16 +47,16 @@ func TestGenerateUserGroupEntry(t *testing.T) {
Mountpoint: "/does/not/exist/tmp/", Mountpoint: "/does/not/exist/tmp/",
}, },
} }
group, err := c.generateUserGroupEntry(0) group, err := c.generateUserGroupEntry(-1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assert.Equal(t, group, "456789:x:456789:123456\n") assert.Equal(t, group, "456789:x:456789:123456\n")
c.config.User = "567890" c.config.User = "567890"
group, err = c.generateUserGroupEntry(0) group, err = c.generateUserGroupEntry(-1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assert.Equal(t, group, "567890:x:567890:567890\n") assert.Equal(t, group, "0:x:0:567890\n")
} }

View File

@ -90,13 +90,6 @@ USER 1000`, ALPINE)
Expect(session.OutputToString()).To(ContainSubstring("/etc/group")) Expect(session.OutputToString()).To(ContainSubstring("/etc/group"))
}) })
It("podman run numeric user not specified in container modifies group", func() {
session := podmanTest.Podman([]string{"run", "--read-only", "-u", "20001", BB, "mount"})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
Expect(session.OutputToString()).To(ContainSubstring("/etc/group"))
})
It("podman run numeric group from image and no group file", func() { It("podman run numeric group from image and no group file", func() {
dockerfile := fmt.Sprintf(`FROM %s dockerfile := fmt.Sprintf(`FROM %s
RUN rm -f /etc/passwd /etc/shadow /etc/group RUN rm -f /etc/passwd /etc/shadow /etc/group

View File

@ -906,6 +906,17 @@ EOF
fi fi
user=$(id -u) user=$(id -u)
userspec=$(id -un):$(id -g)
run_podman run --hostuser=$user --user $userspec --rm $IMAGE sh -c 'echo $(id -un):$(id -g)'
is "$output" "$userspec"
run_podman run --hostuser=$user --user $userspec --group-entry="$(id -gn):x:$(id -g):" --rm $IMAGE sh -c 'echo $(id -un):$(id -gn)'
is "$output" "$(id -un):$(id -gn)"
run_podman 126 run --hostuser=$user --user "$(id -un):$(id -gn)" --rm $IMAGE sh -c 'echo $(id -un):$(id -gn)'
is "$output" "Error:.* no matching entries in group file"
run_podman run --hostuser=$user --rm $IMAGE grep $user /etc/passwd run_podman run --hostuser=$user --rm $IMAGE grep $user /etc/passwd
run_podman run --hostuser=$user --user $user --rm $IMAGE grep $user /etc/passwd run_podman run --hostuser=$user --user $user --rm $IMAGE grep $user /etc/passwd
user=bogus user=bogus