diff options
-rw-r--r-- | cmd/podman/export.go | 39 | ||||
-rw-r--r-- | cmd/podman/main.go | 1 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 5 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.c | 9 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.go | 39 | ||||
-rw-r--r-- | test/e2e/rootless_test.go | 17 |
6 files changed, 106 insertions, 4 deletions
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, 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. 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") } 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) } |