github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/test/e2e/run_userns_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/user"
     8  	"strings"
     9  
    10  	. "github.com/hanks177/podman/v4/test/utils"
    11  	. "github.com/onsi/ginkgo"
    12  	. "github.com/onsi/gomega"
    13  	. "github.com/onsi/gomega/gexec"
    14  )
    15  
    16  var _ = Describe("Podman UserNS support", func() {
    17  	var (
    18  		tempdir    string
    19  		err        error
    20  		podmanTest *PodmanTestIntegration
    21  	)
    22  
    23  	BeforeEach(func() {
    24  		if os.Getenv("SKIP_USERNS") != "" {
    25  			Skip("Skip userns tests.")
    26  		}
    27  		if _, err := os.Stat("/proc/self/uid_map"); err != nil {
    28  			Skip("User namespaces not supported.")
    29  		}
    30  		tempdir, err = CreateTempDirInTempDir()
    31  		if err != nil {
    32  			os.Exit(1)
    33  		}
    34  		podmanTest = PodmanTestCreate(tempdir)
    35  		podmanTest.Setup()
    36  	})
    37  
    38  	AfterEach(func() {
    39  		podmanTest.Cleanup()
    40  		f := CurrentGinkgoTestDescription()
    41  		processTestResult(f)
    42  
    43  	})
    44  
    45  	It("podman uidmapping and gidmapping", func() {
    46  		session := podmanTest.Podman([]string{"run", "--uidmap=0:100:5000", "--gidmap=0:200:5000", "alpine", "echo", "hello"})
    47  		session.WaitWithDefaultTimeout()
    48  		Expect(session).Should(Exit(0))
    49  		Expect(session.OutputToString()).To(ContainSubstring("hello"))
    50  	})
    51  
    52  	// It essentially repeats the test above but with the `-it` short option
    53  	// that broke execution at:
    54  	//     https://github.com/containers/podman/pull/1066#issuecomment-403562116
    55  	// To avoid a potential future regression, use this as a test.
    56  	It("podman uidmapping and gidmapping with short-opts", func() {
    57  		session := podmanTest.Podman([]string{"run", "--uidmap=0:1:5000", "--gidmap=0:200:5000", "-it", "alpine", "echo", "hello"})
    58  		session.WaitWithDefaultTimeout()
    59  		Expect(session).Should(Exit(0))
    60  		Expect(session.OutputToString()).To(ContainSubstring("hello"))
    61  	})
    62  
    63  	It("podman uidmapping and gidmapping with a volume", func() {
    64  		session := podmanTest.Podman([]string{"run", "--uidmap=0:1:500", "--gidmap=0:200:5000", "-v", "my-foo-volume:/foo:Z", "alpine", "echo", "hello"})
    65  		session.WaitWithDefaultTimeout()
    66  		Expect(session).Should(Exit(0))
    67  		Expect(session.OutputToString()).To(ContainSubstring("hello"))
    68  	})
    69  
    70  	It("podman uidmapping and gidmapping --net=host", func() {
    71  		session := podmanTest.Podman([]string{"run", "--net=host", "--uidmap=0:1:5000", "--gidmap=0:200:5000", "alpine", "echo", "hello"})
    72  		session.WaitWithDefaultTimeout()
    73  		Expect(session).Should(Exit(0))
    74  		Expect(session.OutputToString()).To(ContainSubstring("hello"))
    75  	})
    76  
    77  	It("podman --userns=keep-id", func() {
    78  		session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-u"})
    79  		session.WaitWithDefaultTimeout()
    80  		if os.Geteuid() == 0 {
    81  			Expect(session).Should(Exit(125))
    82  			return
    83  		}
    84  
    85  		Expect(session).Should(Exit(0))
    86  		uid := fmt.Sprintf("%d", os.Geteuid())
    87  		Expect(session.OutputToString()).To(ContainSubstring(uid))
    88  	})
    89  
    90  	It("podman --userns=keep-id check passwd", func() {
    91  		SkipIfNotRootless("keep-id only works in rootless mode")
    92  		session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-un"})
    93  		session.WaitWithDefaultTimeout()
    94  		Expect(session).Should(Exit(0))
    95  		u, err := user.Current()
    96  		Expect(err).To(BeNil())
    97  		Expect(session.OutputToString()).To(ContainSubstring(u.Name))
    98  	})
    99  
   100  	It("podman --userns=keep-id root owns /usr", func() {
   101  		SkipIfNotRootless("keep-id only works in rootless mode")
   102  		session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "stat", "-c%u", "/usr"})
   103  		session.WaitWithDefaultTimeout()
   104  		Expect(session).Should(Exit(0))
   105  		Expect(session.OutputToString()).To(Equal("0"))
   106  	})
   107  
   108  	It("podman --userns=keep-id --user root:root", func() {
   109  		SkipIfNotRootless("keep-id only works in rootless mode")
   110  		session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", "alpine", "id", "-u"})
   111  		session.WaitWithDefaultTimeout()
   112  		Expect(session).Should(Exit(0))
   113  		Expect(session.OutputToString()).To(Equal("0"))
   114  	})
   115  
   116  	It("podman run --userns=keep-id can add users", func() {
   117  		SkipIfNotRootless("keep-id only works in rootless mode")
   118  		userName := os.Getenv("USER")
   119  		if userName == "" {
   120  			Skip("Can't complete test if no username available")
   121  		}
   122  
   123  		ctrName := "ctr-name"
   124  		session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", "-d", "--stop-signal", "9", "--name", ctrName, fedoraMinimal, "sleep", "600"})
   125  		session.WaitWithDefaultTimeout()
   126  		Expect(session).Should(Exit(0))
   127  
   128  		exec1 := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "cat", "/etc/passwd"})
   129  		exec1.WaitWithDefaultTimeout()
   130  		Expect(exec1).Should(Exit(0))
   131  		Expect(exec1.OutputToString()).To(ContainSubstring(userName))
   132  
   133  		exec2 := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "useradd", "testuser"})
   134  		exec2.WaitWithDefaultTimeout()
   135  		Expect(exec2).Should(Exit(0))
   136  	})
   137  
   138  	It("podman --userns=auto", func() {
   139  		u, err := user.Current()
   140  		Expect(err).To(BeNil())
   141  		name := u.Name
   142  		if name == "root" {
   143  			name = "containers"
   144  		}
   145  
   146  		content, err := ioutil.ReadFile("/etc/subuid")
   147  		if err != nil {
   148  			Skip("cannot read /etc/subuid")
   149  		}
   150  		if !strings.Contains(string(content), name) {
   151  			Skip("cannot find mappings for the current user")
   152  		}
   153  
   154  		m := make(map[string]string)
   155  		for i := 0; i < 5; i++ {
   156  			session := podmanTest.Podman([]string{"run", "--userns=auto", "alpine", "cat", "/proc/self/uid_map"})
   157  			session.WaitWithDefaultTimeout()
   158  			Expect(session).Should(Exit(0))
   159  			l := session.OutputToString()
   160  			Expect(l).To(ContainSubstring("1024"))
   161  			m[l] = l
   162  		}
   163  		// check for no duplicates
   164  		Expect(m).To(HaveLen(5))
   165  	})
   166  
   167  	It("podman --userns=auto:size=%d", func() {
   168  		u, err := user.Current()
   169  		Expect(err).To(BeNil())
   170  
   171  		name := u.Name
   172  		if name == "root" {
   173  			name = "containers"
   174  		}
   175  
   176  		content, err := ioutil.ReadFile("/etc/subuid")
   177  		if err != nil {
   178  			Skip("cannot read /etc/subuid")
   179  		}
   180  		if !strings.Contains(string(content), name) {
   181  			Skip("cannot find mappings for the current user")
   182  		}
   183  
   184  		session := podmanTest.Podman([]string{"run", "--userns=auto:size=500", "alpine", "cat", "/proc/self/uid_map"})
   185  		session.WaitWithDefaultTimeout()
   186  		Expect(session).Should(Exit(0))
   187  		Expect(session.OutputToString()).To(ContainSubstring("500"))
   188  
   189  		session = podmanTest.Podman([]string{"run", "--userns=auto:size=3000", "alpine", "cat", "/proc/self/uid_map"})
   190  		session.WaitWithDefaultTimeout()
   191  		Expect(session).Should(Exit(0))
   192  		Expect(session.OutputToString()).To(ContainSubstring("3000"))
   193  
   194  		session = podmanTest.Podman([]string{"run", "--userns=auto", "--user=2000:3000", "alpine", "cat", "/proc/self/uid_map"})
   195  		session.WaitWithDefaultTimeout()
   196  		Expect(session).Should(Exit(0))
   197  		Expect(session.OutputToString()).To(ContainSubstring("3001"))
   198  
   199  		session = podmanTest.Podman([]string{"run", "--userns=auto", "--user=4000:1000", "alpine", "cat", "/proc/self/uid_map"})
   200  		session.WaitWithDefaultTimeout()
   201  		Expect(session).Should(Exit(0))
   202  		Expect(session.OutputToString()).To(ContainSubstring("4001"))
   203  	})
   204  
   205  	It("podman --userns=auto:uidmapping=", func() {
   206  		u, err := user.Current()
   207  		Expect(err).To(BeNil())
   208  
   209  		name := u.Name
   210  		if name == "root" {
   211  			name = "containers"
   212  		}
   213  
   214  		content, err := ioutil.ReadFile("/etc/subuid")
   215  		if err != nil {
   216  			Skip("cannot read /etc/subuid")
   217  		}
   218  		if !strings.Contains(string(content), name) {
   219  			Skip("cannot find mappings for the current user")
   220  		}
   221  
   222  		session := podmanTest.Podman([]string{"run", "--userns=auto:uidmapping=0:0:1", "alpine", "cat", "/proc/self/uid_map"})
   223  		session.WaitWithDefaultTimeout()
   224  		Expect(session).Should(Exit(0))
   225  		output := session.OutputToString()
   226  		Expect(output).To(MatchRegexp("\\s0\\s0\\s1"))
   227  
   228  		session = podmanTest.Podman([]string{"run", "--userns=auto:size=8192,uidmapping=0:0:1", "alpine", "cat", "/proc/self/uid_map"})
   229  		session.WaitWithDefaultTimeout()
   230  		Expect(session).Should(Exit(0))
   231  		Expect(session.OutputToString()).To(ContainSubstring("8191"))
   232  	})
   233  
   234  	It("podman --userns=auto:gidmapping=", func() {
   235  		u, err := user.Current()
   236  		Expect(err).To(BeNil())
   237  
   238  		name := u.Name
   239  		if name == "root" {
   240  			name = "containers"
   241  		}
   242  
   243  		content, err := ioutil.ReadFile("/etc/subuid")
   244  		if err != nil {
   245  			Skip("cannot read /etc/subuid")
   246  		}
   247  		if !strings.Contains(string(content), name) {
   248  			Skip("cannot find mappings for the current user")
   249  		}
   250  
   251  		session := podmanTest.Podman([]string{"run", "--userns=auto:gidmapping=0:0:1", "alpine", "cat", "/proc/self/gid_map"})
   252  		session.WaitWithDefaultTimeout()
   253  		Expect(session).Should(Exit(0))
   254  		output := session.OutputToString()
   255  		Expect(output).To(MatchRegexp("\\s0\\s0\\s1"))
   256  
   257  		session = podmanTest.Podman([]string{"run", "--userns=auto:size=8192,gidmapping=0:0:1", "alpine", "cat", "/proc/self/gid_map"})
   258  		session.WaitWithDefaultTimeout()
   259  		Expect(session).Should(Exit(0))
   260  		Expect(session.OutputToString()).To(ContainSubstring("8191"))
   261  	})
   262  
   263  	It("podman --userns=container:CTR", func() {
   264  		ctrName := "userns-ctr"
   265  		session := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:0:1", "--uidmap=1:1:4998", "--name", ctrName, "alpine", "top"})
   266  		session.WaitWithDefaultTimeout()
   267  		Expect(session).Should(Exit(0))
   268  
   269  		// runc has an issue and we also need to join the IPC namespace.
   270  		session = podmanTest.Podman([]string{"run", "--rm", "--userns=container:" + ctrName, "--ipc=container:" + ctrName, "alpine", "cat", "/proc/self/uid_map"})
   271  		session.WaitWithDefaultTimeout()
   272  		Expect(session).Should(Exit(0))
   273  
   274  		Expect(session.OutputToString()).To(ContainSubstring("4998"))
   275  
   276  		session = podmanTest.Podman([]string{"run", "--rm", "--userns=container:" + ctrName, "--net=container:" + ctrName, "alpine", "cat", "/proc/self/uid_map"})
   277  		session.WaitWithDefaultTimeout()
   278  		Expect(session).Should(Exit(0))
   279  
   280  		Expect(session.OutputToString()).To(ContainSubstring("4998"))
   281  	})
   282  
   283  	It("podman --user with volume", func() {
   284  		tests := []struct {
   285  			uid, gid, arg, vol string
   286  		}{
   287  			{"0", "0", "0:0", "vol-0"},
   288  			{"1000", "0", "1000", "vol-1"},
   289  			{"1000", "1000", "1000:1000", "vol-2"},
   290  		}
   291  
   292  		for _, tt := range tests {
   293  			session := podmanTest.Podman([]string{"run", "-d", "--user", tt.arg, "--mount", "type=volume,src=" + tt.vol + ",dst=/home/user", "alpine", "top"})
   294  			session.WaitWithDefaultTimeout()
   295  			Expect(session).Should(Exit(0))
   296  
   297  			inspectUID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .UID }}", tt.vol})
   298  			inspectUID.WaitWithDefaultTimeout()
   299  			Expect(inspectUID).Should(Exit(0))
   300  			Expect(inspectUID.OutputToString()).To(Equal(tt.uid))
   301  
   302  			// Make sure we're defaulting to 0.
   303  			inspectGID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .GID }}", tt.vol})
   304  			inspectGID.WaitWithDefaultTimeout()
   305  			Expect(inspectGID).Should(Exit(0))
   306  			Expect(inspectGID.OutputToString()).To(Equal(tt.gid))
   307  		}
   308  
   309  	})
   310  	It("podman PODMAN_USERNS", func() {
   311  		SkipIfNotRootless("keep-id only works in rootless mode")
   312  
   313  		podmanUserns, podmanUserusSet := os.LookupEnv("PODMAN_USERNS")
   314  		os.Setenv("PODMAN_USERNS", "keep-id")
   315  		defer func() {
   316  			if podmanUserusSet {
   317  				os.Setenv("PODMAN_USERNS", podmanUserns)
   318  			} else {
   319  				os.Unsetenv("PODMAN_USERNS")
   320  			}
   321  		}()
   322  		if IsRemote() {
   323  			podmanTest.RestartRemoteService()
   324  		}
   325  
   326  		result := podmanTest.Podman([]string{"create", ALPINE, "true"})
   327  		result.WaitWithDefaultTimeout()
   328  		Expect(result).Should(Exit(0))
   329  
   330  		inspect := podmanTest.Podman([]string{"inspect", "--format", "{{ .HostConfig.IDMappings }}", result.OutputToString()})
   331  		inspect.WaitWithDefaultTimeout()
   332  		Expect(inspect.OutputToString()).To(Not(Equal("<nil>")))
   333  
   334  		if IsRemote() {
   335  			podmanTest.RestartRemoteService()
   336  		}
   337  	})
   338  })