From d34b1e7ad51a8e3211a3ce82931a4e75369b69f8 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 31 Aug 2020 14:06:49 -0400 Subject: Ensure rootless containers without a passwd can start We want to modify /etc/passwd to add an entry for the user in question, but at the same time we don't want to require the container provide a /etc/passwd (a container with a single, statically linked binary and nothing else is perfectly fine and should be allowed, for example). We could create the passwd file if it does not exist, but if the container doesn't provide one, it's probably better not to make one at all. Gate changes to /etc/passwd behind a stat() of the file in the container returning cleanly. Fixes #7515 Signed-off-by: Matthew Heon --- libpod/container_internal_linux.go | 11 +++++++++++ test/e2e/run_passwd_test.go | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index b790da445..1abfd2f6f 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -1534,6 +1534,17 @@ func (c *Container) generatePasswd() (string, error) { if _, err := os.Stat(passwdPath); err == nil { return passwdPath, nil } + // Check if container has a /etc/passwd - if it doesn't do nothing. + passwdPath, err := securejoin.SecureJoin(c.state.Mountpoint, "/etc/passwd") + if err != nil { + return "", errors.Wrapf(err, "error creating path to container %s /etc/passwd", c.ID()) + } + if _, err := os.Stat(passwdPath); err != nil { + if os.IsNotExist(err) { + return "", nil + } + return "", errors.Wrapf(err, "unable to access container %s /etc/passwd", c.ID()) + } pwd := "" if c.config.User != "" { entry, err := c.generateUserPasswdEntry() diff --git a/test/e2e/run_passwd_test.go b/test/e2e/run_passwd_test.go index 6caf8a094..4ef4156a4 100644 --- a/test/e2e/run_passwd_test.go +++ b/test/e2e/run_passwd_test.go @@ -60,4 +60,17 @@ var _ = Describe("Podman run passwd", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.LineInOutputContains("passwd")).To(BeTrue()) }) + + It("podman can run container without /etc/passwd", func() { + SkipIfRemote() + dockerfile := `FROM alpine +RUN rm -f /etc/passwd /etc/shadow /etc/group +USER 1000` + imgName := "testimg" + podmanTest.BuildImage(dockerfile, imgName, "false") + session := podmanTest.Podman([]string{"run", "--rm", imgName, "ls", "/etc/"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Not(ContainSubstring("passwd"))) + }) }) -- cgit v1.2.3-54-g00ecf