Merge pull request #2028 from giuseppe/fix-rootless-export

rootless: fix export when using fuse-overlayfs
This commit is contained in:
OpenShift Merge Robot 2018-12-21 06:09:45 -08:00 committed by GitHub
commit faa7ff3568
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 106 additions and 4 deletions

View File

@ -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)
} }

View File

@ -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,

View File

@ -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.

View File

@ -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));

View File

@ -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")
} }

View File

@ -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)
} }