diff options
-rwxr-xr-x | contrib/cirrus/logformatter | 6 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 48 | ||||
-rw-r--r-- | pkg/bindings/test/containers_test.go | 1 | ||||
-rw-r--r-- | pkg/specgen/generate/ports.go | 15 | ||||
-rw-r--r-- | test/e2e/run_networking_test.go | 16 | ||||
-rw-r--r-- | test/e2e/run_passwd_test.go | 8 | ||||
-rw-r--r-- | test/e2e/run_userns_test.go | 25 |
7 files changed, 100 insertions, 19 deletions
diff --git a/contrib/cirrus/logformatter b/contrib/cirrus/logformatter index b56a829c5..f97638b6f 100755 --- a/contrib/cirrus/logformatter +++ b/contrib/cirrus/logformatter @@ -208,13 +208,13 @@ END_HTML } # Try to identify the git commit we're working with... - if ($line =~ m!libpod/define.gitCommit=([0-9a-f]+)!) { + if ($line =~ m!/define.gitCommit=([0-9a-f]+)!) { $git_commit = $1; } # ...so we can link to specific lines in source files if ($git_commit) { - # 1 12 3 34 4 5 526 6 - $line =~ s{^(.*)(\/(containers\/libpod)(\/\S+):(\d+))(.*)$} + # 1 12 3 34 4 5 526 6 + $line =~ s{^(.*)(\/(containers\/[^/]+)(\/\S+):(\d+))(.*)$} {$1<a class="codelink" href='https://github.com/$3/blob/$git_commit$4#L$5'>$2</a>$6}; } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 795611596..4cfe992ea 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -1480,11 +1480,26 @@ func (c *Container) generateCurrentUserPasswdEntry() (string, error) { if uid == 0 { return "", nil } + u, err := user.LookupId(strconv.Itoa(rootless.GetRootlessUID())) if err != nil { return "", errors.Wrapf(err, "failed to get current user") } - 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) + if err != User.ErrNoPasswdEntries { + return "", err + } + + // If the user's actual home directory exists, or was mounted in - use + // that. + homeDir := c.WorkingDir() + if MountExists(c.config.Spec.Mounts, u.HomeDir) { + homeDir = u.HomeDir + } + + return fmt.Sprintf("%s:x:%s:%s:%s:%s:/bin/sh\n", u.Username, u.Uid, u.Gid, u.Username, homeDir), nil } // generateUserPasswdEntry generates an /etc/passwd entry for the container user @@ -1510,12 +1525,9 @@ func (c *Container) generateUserPasswdEntry() (string, error) { // Lookup the user to see if it exists in the container image _, err = lookup.GetUser(c.state.Mountpoint, userspec) - if err != nil && err != User.ErrNoPasswdEntries { + if err != User.ErrNoPasswdEntries { return "", err } - if err == nil { - return "", nil - } if groupspec != "" { ugid, err := strconv.ParseUint(groupspec, 10, 32) @@ -1564,6 +1576,32 @@ func (c *Container) generatePasswd() (string, error) { if pwd == "" { return "", nil } + + // If we are *not* read-only - edit /etc/passwd in the container. + // This is *gross* (shows up in changes to the container, will be + // committed to images based on the container) but it actually allows us + // to add users to the container (a bind mount breaks useradd). + // We should never get here twice, because generateUserPasswdEntry will + // not return anything if the user already exists in /etc/passwd. + if !c.IsReadOnly() { + containerPasswd, err := securejoin.SecureJoin(c.state.Mountpoint, "/etc/passwd") + if err != nil { + return "", errors.Wrapf(err, "error looking up location of container %s /etc/passwd", c.ID()) + } + + f, err := os.OpenFile(containerPasswd, os.O_APPEND|os.O_WRONLY, 0600) + if err != nil { + return "", errors.Wrapf(err, "error opening container %s /etc/passwd", c.ID()) + } + defer f.Close() + + if _, err := f.WriteString(pwd); err != nil { + return "", errors.Wrapf(err, "unable to append to container %s /etc/passwd", c.ID()) + } + + return "", nil + } + originPasswdFile := filepath.Join(c.state.Mountpoint, "/etc/passwd") orig, err := ioutil.ReadFile(originPasswdFile) if err != nil && !os.IsNotExist(err) { diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index c1a01c280..9a188e5da 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -280,6 +280,7 @@ var _ = Describe("Podman containers ", func() { }) It("podman wait to pause|unpause condition", func() { + Skip("FIXME: https://github.com/containers/podman/issues/6518") var ( name = "top" exitCode int32 = -1 diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index 1ad7e6f4d..7dd50ac0d 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -123,19 +123,20 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, postAssignHostPort = true } } else { - testCPort := ctrPortMap[cPort] - if testCPort != 0 && testCPort != hPort { - // This is an attempt to redefine a port - return nil, nil, nil, errors.Errorf("conflicting port mappings for container port %d (protocol %s)", cPort, p) - } - ctrPortMap[cPort] = hPort - testHPort := hostPortMap[hPort] if testHPort != 0 && testHPort != cPort { return nil, nil, nil, errors.Errorf("conflicting port mappings for host port %d (protocol %s)", hPort, p) } hostPortMap[hPort] = cPort + // Mapping a container port to multiple + // host ports is allowed. + // We only store the latest of these in + // the container port map - we don't + // need to know all of them, just one. + testCPort := ctrPortMap[cPort] + ctrPortMap[cPort] = hPort + // If we have an exact duplicate, just continue if testCPort == hPort && testHPort == cPort { continue diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index bf96db197..87b74052a 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -220,6 +220,22 @@ var _ = Describe("Podman run networking", func() { Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostIP).To(Equal("")) }) + It("podman run -p 8080:8080 -p 8081:8080", func() { + name := "testctr" + session := podmanTest.Podman([]string{"create", "-t", "-p", "4000:8080", "-p", "8000:8080", "--name", name, ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + inspectOut := podmanTest.InspectContainer(name) + Expect(len(inspectOut)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports["8080/tcp"])).To(Equal(2)) + + hp1 := inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostPort + hp2 := inspectOut[0].NetworkSettings.Ports["8080/tcp"][1].HostPort + + // We can't guarantee order + Expect((hp1 == "4000" && hp2 == "8000") || (hp1 == "8000" && hp2 == "4000")).To(BeTrue()) + }) + It("podman run network expose host port 80 to container port 8000", func() { SkipIfRootless() session := podmanTest.Podman([]string{"run", "-dt", "-p", "80:8000", ALPINE, "/bin/sh"}) diff --git a/test/e2e/run_passwd_test.go b/test/e2e/run_passwd_test.go index a1414e313..8dea7d39b 100644 --- a/test/e2e/run_passwd_test.go +++ b/test/e2e/run_passwd_test.go @@ -33,27 +33,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 198217433..25f8d0d15 100644 --- a/test/e2e/run_userns_test.go +++ b/test/e2e/run_userns_test.go @@ -111,6 +111,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()) |