Merge pull request #2028 from giuseppe/fix-rootless-export
rootless: fix export when using fuse-overlayfs
This commit is contained in:
commit
faa7ff3568
|
@ -1,9 +1,13 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
@ -35,6 +39,9 @@ func exportCmd(c *cli.Context) error {
|
||||||
if err := validateFlags(c, exportFlags); err != nil {
|
if err := validateFlags(c, exportFlags); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if os.Geteuid() != 0 {
|
||||||
|
rootless.SetSkipStorageSetup(true)
|
||||||
|
}
|
||||||
|
|
||||||
runtime, err := libpodruntime.GetRuntime(c)
|
runtime, err := libpodruntime.GetRuntime(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -66,5 +73,37 @@ func exportCmd(c *cli.Context) error {
|
||||||
return errors.Wrapf(err, "error looking up container %q", args[0])
|
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)
|
return ctr.Export(output)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ var cmdsNotRequiringRootless = map[string]bool{
|
||||||
"version": true,
|
"version": true,
|
||||||
"create": true,
|
"create": true,
|
||||||
"exec": true,
|
"exec": true,
|
||||||
|
"export": true,
|
||||||
// `info` must be executed in an user namespace.
|
// `info` must be executed in an user namespace.
|
||||||
// If this change, please also update libpod.refreshRootless()
|
// If this change, please also update libpod.refreshRootless()
|
||||||
"login": true,
|
"login": true,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/stringid"
|
"github.com/containers/storage/pkg/stringid"
|
||||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
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
|
// Go through the volume mounts and check for named volumes
|
||||||
// If the named volme already exists continue, otherwise create
|
// If the named volme already exists continue, otherwise create
|
||||||
// the storage for the named volume.
|
// the storage for the named volume.
|
||||||
|
|
|
@ -99,7 +99,7 @@ get_cmd_line_args (pid_t pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
reexec_userns_join (int userns)
|
reexec_userns_join (int userns, int mountns)
|
||||||
{
|
{
|
||||||
pid_t ppid = getpid ();
|
pid_t ppid = getpid ();
|
||||||
char uid[16];
|
char uid[16];
|
||||||
|
@ -131,6 +131,13 @@ reexec_userns_join (int userns)
|
||||||
}
|
}
|
||||||
close (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)
|
if (syscall_setresgid (0, 0, 0) < 0)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "cannot setresgid: %s\n", strerror (errno));
|
fprintf (stderr, "cannot setresgid: %s\n", strerror (errno));
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
/*
|
/*
|
||||||
extern int reexec_in_user_namespace(int ready);
|
extern int reexec_in_user_namespace(int ready);
|
||||||
extern int reexec_in_user_namespace_wait(int pid);
|
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"
|
import "C"
|
||||||
|
|
||||||
|
@ -112,7 +112,40 @@ func JoinNS(pid uint) (bool, int, error) {
|
||||||
}
|
}
|
||||||
defer userNS.Close()
|
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 {
|
if int(pidC) < 0 {
|
||||||
return false, -1, errors.Errorf("cannot re-exec process")
|
return false, -1, errors.Errorf("cannot re-exec process")
|
||||||
}
|
}
|
||||||
|
@ -138,7 +171,7 @@ func JoinNSPath(path string) (bool, int, error) {
|
||||||
}
|
}
|
||||||
defer userNS.Close()
|
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 {
|
if int(pidC) < 0 {
|
||||||
return false, -1, errors.Errorf("cannot re-exec process")
|
return false, -1, errors.Errorf("cannot re-exec process")
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
. "github.com/containers/libpod/test/utils"
|
. "github.com/containers/libpod/test/utils"
|
||||||
|
@ -245,6 +246,22 @@ var _ = Describe("Podman rootless", func() {
|
||||||
cmd.WaitWithDefaultTimeout()
|
cmd.WaitWithDefaultTimeout()
|
||||||
Expect(cmd.ExitCode()).To(Equal(0))
|
Expect(cmd.ExitCode()).To(Equal(0))
|
||||||
Expect(cmd.LineInOutputContains("hello")).To(BeTrue())
|
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)
|
runInRootlessContext(f)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue