From f65eafa6ba2fec491185ccf29a515c9a96b9852a Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 19 Dec 2018 18:06:12 +0100 Subject: libpod: always store the conmon pid file we need this information to later be able to join the conmon process. Signed-off-by: Giuseppe Scrivano --- libpod/runtime_ctr.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index ba8eaacbe..c108febd7 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/containers/libpod/pkg/rootless" "github.com/containers/storage" "github.com/containers/storage/pkg/stringid" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -154,6 +155,10 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options .. } }() + if rootless.IsRootless() && ctr.config.ConmonPidFile == "" { + ctr.config.ConmonPidFile = filepath.Join(ctr.state.RunDir, "conmon.pid") + } + // Go through the volume mounts and check for named volumes // If the named volme already exists continue, otherwise create // the storage for the named volume. -- cgit v1.2.3-54-g00ecf From f2e96b0934a6d1b3aac8a1d931e790c6226dd4e0 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 19 Dec 2018 18:07:03 +0100 Subject: rootless: add function to join user and mount namespace Add the possibility to join directly the user and mount namespace without looking up the parent of the user namespace. We need this in order to be able the conmon process, as the mount namespace is kept alive only there. Signed-off-by: Giuseppe Scrivano --- pkg/rootless/rootless_linux.c | 9 ++++++++- pkg/rootless/rootless_linux.go | 39 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 9eb16c1a5..1d28ff68d 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -99,7 +99,7 @@ get_cmd_line_args (pid_t pid) } int -reexec_userns_join (int userns) +reexec_userns_join (int userns, int mountns) { pid_t ppid = getpid (); char uid[16]; @@ -131,6 +131,13 @@ reexec_userns_join (int userns) } close (userns); + if (mountns >= 0 && setns (mountns, 0) < 0) + { + fprintf (stderr, "cannot setns: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } + close (userns); + if (syscall_setresgid (0, 0, 0) < 0) { fprintf (stderr, "cannot setresgid: %s\n", strerror (errno)); diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 07002da3f..9a192c0fa 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -25,7 +25,7 @@ import ( /* extern int reexec_in_user_namespace(int ready); extern int reexec_in_user_namespace_wait(int pid); -extern int reexec_userns_join(int userns); +extern int reexec_userns_join(int userns, int mountns); */ import "C" @@ -112,7 +112,40 @@ func JoinNS(pid uint) (bool, int, error) { } defer userNS.Close() - pidC := C.reexec_userns_join(C.int(userNS.Fd())) + pidC := C.reexec_userns_join(C.int(userNS.Fd()), -1) + if int(pidC) < 0 { + return false, -1, errors.Errorf("cannot re-exec process") + } + + ret := C.reexec_in_user_namespace_wait(pidC) + if ret < 0 { + return false, -1, errors.New("error waiting for the re-exec process") + } + + return true, int(ret), nil +} + +// JoinDirectUserAndMountNS re-exec podman in a new userNS and join the user and mount +// namespace of the specified PID without looking up its parent. Useful to join directly +// the conmon process. +func JoinDirectUserAndMountNS(pid uint) (bool, int, error) { + if os.Geteuid() == 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != "" { + return false, -1, nil + } + + userNS, err := os.Open(fmt.Sprintf("/proc/%d/ns/user", pid)) + if err != nil { + return false, -1, err + } + defer userNS.Close() + + mountNS, err := os.Open(fmt.Sprintf("/proc/%d/ns/mnt", pid)) + if err != nil { + return false, -1, err + } + defer userNS.Close() + + pidC := C.reexec_userns_join(C.int(userNS.Fd()), C.int(mountNS.Fd())) if int(pidC) < 0 { return false, -1, errors.Errorf("cannot re-exec process") } @@ -138,7 +171,7 @@ func JoinNSPath(path string) (bool, int, error) { } defer userNS.Close() - pidC := C.reexec_userns_join(C.int(userNS.Fd())) + pidC := C.reexec_userns_join(C.int(userNS.Fd()), -1) if int(pidC) < 0 { return false, -1, errors.Errorf("cannot re-exec process") } -- cgit v1.2.3-54-g00ecf From d389ac45e5b7208bbfb776d6724223f3bffb3e1b Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 19 Dec 2018 18:08:15 +0100 Subject: export: fix usage with rootless containers Fix usage of export when rootless containers are used without vfs. We join the conmon process namespaces as the container is running in a different one. There can be a problem if the user specify a different path for the conmon process, and then the file is deleted. In this case podman won't be able to find the conmon process to join. Closes: https://github.com/containers/libpod/issues/2027 Signed-off-by: Giuseppe Scrivano --- cmd/podman/export.go | 39 +++++++++++++++++++++++++++++++++++++++ cmd/podman/main.go | 1 + 2 files changed, 40 insertions(+) diff --git a/cmd/podman/export.go b/cmd/podman/export.go index 667b8d012..c0e63bd2a 100644 --- a/cmd/podman/export.go +++ b/cmd/podman/export.go @@ -1,9 +1,13 @@ package main import ( + "io/ioutil" "os" + "strconv" "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/urfave/cli" @@ -35,6 +39,9 @@ func exportCmd(c *cli.Context) error { if err := validateFlags(c, exportFlags); err != nil { return err } + if os.Geteuid() != 0 { + rootless.SetSkipStorageSetup(true) + } runtime, err := libpodruntime.GetRuntime(c) if err != nil { @@ -66,5 +73,37 @@ func exportCmd(c *cli.Context) error { return errors.Wrapf(err, "error looking up container %q", args[0]) } + if os.Geteuid() != 0 { + state, err := ctr.State() + if err != nil { + return errors.Wrapf(err, "cannot read container state %q", ctr.ID()) + } + if state == libpod.ContainerStateRunning || state == libpod.ContainerStatePaused { + data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile) + if err != nil { + return errors.Wrapf(err, "cannot read conmon PID file %q", ctr.Config().ConmonPidFile) + } + conmonPid, err := strconv.Atoi(string(data)) + if err != nil { + return errors.Wrapf(err, "cannot parse PID %q", data) + } + became, ret, err := rootless.JoinDirectUserAndMountNS(uint(conmonPid)) + if err != nil { + return err + } + if became { + os.Exit(ret) + } + } else { + became, ret, err := rootless.BecomeRootInUserNS() + if err != nil { + return err + } + if became { + os.Exit(ret) + } + } + } + return ctr.Export(output) } diff --git a/cmd/podman/main.go b/cmd/podman/main.go index f47a75761..7ef22a93b 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -30,6 +30,7 @@ var cmdsNotRequiringRootless = map[string]bool{ "version": true, "create": true, "exec": true, + "export": true, // `info` must be executed in an user namespace. // If this change, please also update libpod.refreshRootless() "login": true, -- cgit v1.2.3-54-g00ecf From 0ed55f75abac1311b61a17079ae9314ec5459bfd Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 19 Dec 2018 18:19:43 +0100 Subject: test: add test for rootless export Signed-off-by: Giuseppe Scrivano --- test/e2e/rootless_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/e2e/rootless_test.go b/test/e2e/rootless_test.go index 037af9688..5a7f0359a 100644 --- a/test/e2e/rootless_test.go +++ b/test/e2e/rootless_test.go @@ -7,6 +7,7 @@ import ( "os/exec" "path/filepath" "runtime" + "strings" "syscall" . "github.com/containers/libpod/test/utils" @@ -245,6 +246,22 @@ var _ = Describe("Podman rootless", func() { cmd.WaitWithDefaultTimeout() Expect(cmd.ExitCode()).To(Equal(0)) Expect(cmd.LineInOutputContains("hello")).To(BeTrue()) + + cmd = rootlessTest.PodmanAsUser([]string{"ps", "-l", "-q"}, 1000, 1000, env) + cmd.WaitWithDefaultTimeout() + Expect(cmd.ExitCode()).To(Equal(0)) + cid := cmd.OutputToString() + + cmd = rootlessTest.PodmanAsUser([]string{"exec", "-l", "sh", "-c", "echo SeCreTMessage > /file"}, 1000, 1000, env) + cmd.WaitWithDefaultTimeout() + Expect(cmd.ExitCode()).To(Equal(0)) + + path := filepath.Join(home, "export.tar") + cmd = rootlessTest.PodmanAsUser([]string{"export", "-o", path, cid}, 1000, 1000, env) + cmd.WaitWithDefaultTimeout() + content, err := ioutil.ReadFile(path) + Expect(err).To(BeNil()) + Expect(strings.Contains(string(content), "SeCreTMessage")).To(BeTrue()) } runInRootlessContext(f) } -- cgit v1.2.3-54-g00ecf