From ca00601b14f2253e5b9f89280d39c5d498efc9f3 Mon Sep 17 00:00:00 2001
From: Matthew Heon <matthew.heon@pm.me>
Date: Wed, 15 Jul 2020 15:25:12 -0400
Subject: 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>
---
 test/e2e/run_passwd_test.go |  8 ++++----
 test/e2e/run_userns_test.go | 25 +++++++++++++++++++++++++
 2 files changed, 29 insertions(+), 4 deletions(-)

(limited to 'test')

diff --git a/test/e2e/run_passwd_test.go b/test/e2e/run_passwd_test.go
index d7f3233ef..6caf8a094 100644
--- a/test/e2e/run_passwd_test.go
+++ b/test/e2e/run_passwd_test.go
@@ -35,27 +35,27 @@ var _ = Describe("Podman run passwd", func() {
 	})
 
 	It("podman run no user specified ", func() {
-		session := podmanTest.Podman([]string{"run", BB, "mount"})
+		session := podmanTest.Podman([]string{"run", "--read-only", BB, "mount"})
 		session.WaitWithDefaultTimeout()
 		Expect(session.ExitCode()).To(Equal(0))
 		Expect(session.LineInOutputContains("passwd")).To(BeFalse())
 	})
 	It("podman run user specified in container", func() {
-		session := podmanTest.Podman([]string{"run", "-u", "bin", BB, "mount"})
+		session := podmanTest.Podman([]string{"run", "--read-only", "-u", "bin", BB, "mount"})
 		session.WaitWithDefaultTimeout()
 		Expect(session.ExitCode()).To(Equal(0))
 		Expect(session.LineInOutputContains("passwd")).To(BeFalse())
 	})
 
 	It("podman run UID specified in container", func() {
-		session := podmanTest.Podman([]string{"run", "-u", "2:1", BB, "mount"})
+		session := podmanTest.Podman([]string{"run", "--read-only", "-u", "2:1", BB, "mount"})
 		session.WaitWithDefaultTimeout()
 		Expect(session.ExitCode()).To(Equal(0))
 		Expect(session.LineInOutputContains("passwd")).To(BeFalse())
 	})
 
 	It("podman run UID not specified in container", func() {
-		session := podmanTest.Podman([]string{"run", "-u", "20001:1", BB, "mount"})
+		session := podmanTest.Podman([]string{"run", "--read-only", "-u", "20001:1", BB, "mount"})
 		session.WaitWithDefaultTimeout()
 		Expect(session.ExitCode()).To(Equal(0))
 		Expect(session.LineInOutputContains("passwd")).To(BeTrue())
diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go
index 24d3e42eb..fef125e60 100644
--- a/test/e2e/run_userns_test.go
+++ b/test/e2e/run_userns_test.go
@@ -113,6 +113,31 @@ var _ = Describe("Podman UserNS support", func() {
 		Expect(session.OutputToString()).To(Equal("0"))
 	})
 
+	It("podman run --userns=keep-id can add users", func() {
+		if os.Geteuid() == 0 {
+			Skip("Test only runs without root")
+		}
+
+		userName := os.Getenv("USER")
+		if userName == "" {
+			Skip("Can't complete test if no username available")
+		}
+
+		ctrName := "ctr-name"
+		session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", "-d", "--stop-signal", "9", "--name", ctrName, fedoraMinimal, "sleep", "600"})
+		session.WaitWithDefaultTimeout()
+		Expect(session.ExitCode()).To(Equal(0))
+
+		exec1 := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "cat", "/etc/passwd"})
+		exec1.WaitWithDefaultTimeout()
+		Expect(exec1.ExitCode()).To(Equal(0))
+		Expect(exec1.OutputToString()).To(ContainSubstring(userName))
+
+		exec2 := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "useradd", "testuser"})
+		exec2.WaitWithDefaultTimeout()
+		Expect(exec2.ExitCode()).To(Equal(0))
+	})
+
 	It("podman --userns=auto", func() {
 		u, err := user.Current()
 		Expect(err).To(BeNil())
-- 
cgit v1.2.3-54-g00ecf