diff options
-rw-r--r-- | cmd/podman/libpodruntime/runtime.go | 15 | ||||
-rw-r--r-- | cmd/podman/main.go | 4 | ||||
-rw-r--r-- | libpod/oci.go | 21 | ||||
-rw-r--r-- | libpod/runtime.go | 52 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.c | 6 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.go | 31 | ||||
-rw-r--r-- | pkg/rootless/rootless_unsupported.go | 4 |
7 files changed, 85 insertions, 48 deletions
diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index b48667653..098864810 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -6,6 +6,7 @@ import ( "path/filepath" "github.com/containers/storage" + "github.com/pkg/errors" "github.com/projectatomic/libpod/libpod" "github.com/projectatomic/libpod/pkg/rootless" "github.com/urfave/cli" @@ -23,7 +24,11 @@ func GetRuntime(c *cli.Context) (*libpod.Runtime, error) { func GetRootlessStorageOpts() (storage.StoreOptions, error) { var opts storage.StoreOptions - opts.RunRoot = filepath.Join(libpod.GetRootlessRuntimeDir(), "run") + rootlessRuntime, err := libpod.GetRootlessRuntimeDir() + if err != nil { + return opts, err + } + opts.RunRoot = filepath.Join(rootlessRuntime, "run") dataDir := os.Getenv("XDG_DATA_HOME") if dataDir == "" { @@ -31,7 +36,13 @@ func GetRootlessStorageOpts() (storage.StoreOptions, error) { if home == "" { return opts, fmt.Errorf("neither XDG_DATA_HOME nor HOME was set non-empty") } - dataDir = filepath.Join(home, ".local", "share") + // runc doesn't like symlinks in the rootfs path, and at least + // on CoreOS /home is a symlink to /var/home, so resolve any symlink. + resolvedHome, err := filepath.EvalSymlinks(home) + if err != nil { + return opts, errors.Wrapf(err, "cannot resolve %s", home) + } + dataDir = filepath.Join(resolvedHome, ".local", "share") } opts.GraphRoot = filepath.Join(dataDir, "containers", "storage") opts.GraphDriverName = "vfs" diff --git a/cmd/podman/main.go b/cmd/podman/main.go index a83dc5fb4..3dbf196c2 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -29,13 +29,13 @@ func main() { debug := false cpuProfile := false - became, err := rootless.BecomeRootInUserNS() + became, ret, err := rootless.BecomeRootInUserNS() if err != nil { logrus.Errorf(err.Error()) os.Exit(1) } if became { - os.Exit(0) + os.Exit(ret) } if reexec.Init() { diff --git a/libpod/oci.go b/libpod/oci.go index 612935aed..c0478b3b6 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -180,6 +180,11 @@ func waitPidsStop(pids []int, timeout time.Duration) error { func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string) (err error) { var stderrBuf bytes.Buffer + runtimeDir, err := GetRootlessRuntimeDir() + if err != nil { + return err + } + parentPipe, childPipe, err := newPipe() if err != nil { return errors.Wrapf(err, "error creating socket pair") @@ -253,7 +258,7 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string) (er // 0, 1 and 2 are stdin, stdout and stderr cmd.Env = append(r.conmonEnv, fmt.Sprintf("_OCI_SYNCPIPE=%d", 3)) cmd.Env = append(cmd.Env, fmt.Sprintf("_OCI_STARTPIPE=%d", 4)) - cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", GetRootlessRuntimeDir())) + cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)) if notify, ok := os.LookupEnv("NOTIFY_SOCKET"); ok { cmd.Env = append(cmd.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notify)) } @@ -362,11 +367,16 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string) (er func (r *OCIRuntime) updateContainerStatus(ctr *Container) error { state := new(spec.State) + runtimeDir, err := GetRootlessRuntimeDir() + if err != nil { + return err + } + // Store old state so we know if we were already stopped oldState := ctr.state.State cmd := exec.Command(r.path, "state", ctr.ID()) - cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", GetRootlessRuntimeDir())) + cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)) out, err := cmd.CombinedOutput() if err != nil { @@ -556,6 +566,11 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty return nil, errors.Wrapf(ErrEmptyID, "must provide a session ID for exec") } + runtimeDir, err := GetRootlessRuntimeDir() + if err != nil { + return nil, err + } + args := []string{} // TODO - should we maintain separate logpaths for exec sessions? @@ -597,7 +612,7 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty execCmd.Stdout = os.Stdout execCmd.Stderr = os.Stderr execCmd.Stdin = os.Stdin - execCmd.Env = append(execCmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", GetRootlessRuntimeDir())) + execCmd.Env = append(execCmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)) return execCmd, nil } diff --git a/libpod/runtime.go b/libpod/runtime.go index 9ba6acb78..a551c9134 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -1,7 +1,6 @@ package libpod import ( - "bytes" "fmt" "io/ioutil" "os" @@ -170,7 +169,7 @@ var ( CgroupManager: CgroupfsCgroupsManager, HooksDir: hooks.DefaultDir, StaticDir: filepath.Join(storage.DefaultStoreOptions.GraphRoot, "libpod"), - TmpDir: getDefaultTmpDir(), + TmpDir: "", MaxLogSize: -1, NoPivotRoot: false, CNIConfigDir: "/etc/cni/net.d/", @@ -179,7 +178,7 @@ var ( ) // GetRootlessRuntimeDir returns the runtime directory when running as non root -func GetRootlessRuntimeDir() string { +func GetRootlessRuntimeDir() (string, error) { runtimeDir := os.Getenv("XDG_RUNTIME_DIR") uid := fmt.Sprintf("%d", rootless.GetRootlessUID()) if runtimeDir == "" { @@ -199,18 +198,29 @@ func GetRootlessRuntimeDir() string { } } if runtimeDir == "" { - runtimeDir = filepath.Join(os.Getenv("HOME"), "rundir") + home := os.Getenv("HOME") + if home == "" { + return "", fmt.Errorf("neither XDG_RUNTIME_DIR nor HOME was set non-empty") + } + resolvedHome, err := filepath.EvalSymlinks(home) + if err != nil { + return "", errors.Wrapf(err, "cannot resolve %s", home) + } + runtimeDir = filepath.Join(resolvedHome, "rundir") } - return runtimeDir + return runtimeDir, nil } -func getDefaultTmpDir() string { +func getDefaultTmpDir() (string, error) { if !rootless.IsRootless() { - return "/var/run/libpod" + return "/var/run/libpod", nil } - rootlessRuntimeDir := GetRootlessRuntimeDir() - return filepath.Join(rootlessRuntimeDir, "libpod", "tmp") + rootlessRuntimeDir, err := GetRootlessRuntimeDir() + if err != nil { + return "", err + } + return filepath.Join(rootlessRuntimeDir, "libpod", "tmp"), nil } // NewRuntime creates a new container runtime @@ -220,7 +230,12 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { runtime.config = new(RuntimeConfig) // Copy the default configuration + tmpDir, err := getDefaultTmpDir() + if err != nil { + return nil, err + } deepcopier.Copy(defaultRuntimeConfig).To(runtime.config) + runtime.config.TmpDir = tmpDir configPath := ConfigPath foundConfig := true @@ -230,9 +245,14 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { foundConfig = false } + runtimeDir, err := GetRootlessRuntimeDir() + if err != nil { + return nil, err + } + // containers/image uses XDG_RUNTIME_DIR to locate the auth file. // So make sure the env variable is set. - err = os.Setenv("XDG_RUNTIME_DIR", GetRootlessRuntimeDir()) + err = os.Setenv("XDG_RUNTIME_DIR", runtimeDir) if err != nil { return nil, errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") } @@ -675,18 +695,6 @@ func (r *Runtime) generateName() (string, error) { // The code should never reach here. } -// SaveDefaultConfig saves a copy of the default config at the given path -func SaveDefaultConfig(path string) error { - var w bytes.Buffer - e := toml.NewEncoder(&w) - - if err := e.Encode(&defaultRuntimeConfig); err != nil { - return err - } - - return ioutil.WriteFile(path, w.Bytes(), 0644) -} - // ImageRuntime returns the imageruntime for image resolution func (r *Runtime) ImageRuntime() *image.Runtime { return r.imageRuntime diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index f2684f75f..d08c6c875 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -105,16 +105,16 @@ reexec_in_user_namespace(int ready) ret = read (ready, &b, 1) < 0; while (ret < 0 && errno == EINTR); if (ret < 0) - _exit (1); + _exit (EXIT_FAILURE); close (ready); if (setresgid (0, 0, 0) < 0 || setresuid (0, 0, 0) < 0) - _exit (1); + _exit (EXIT_FAILURE); execvp (argv[0], argv); - _exit (1); + _exit (EXIT_FAILURE); } int diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 55a55bbc6..6089ad73a 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -67,14 +67,16 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) return cmd.Run() } -// BecomeRootInUserNS re-exec podman in a new userNS -func BecomeRootInUserNS() (bool, error) { - +// BecomeRootInUserNS re-exec podman in a new userNS. It returns whether podman was re-executed +// into a new user namespace and the return code from the re-executed podman process. +// If podman was re-executed the caller needs to propagate the error code returned by the child +// process. +func BecomeRootInUserNS() (bool, int, error) { if os.Getuid() == 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != "" { if os.Getenv("_LIBPOD_USERNS_CONFIGURED") == "init" { - return false, runInUser() + return false, 0, runInUser() } - return false, nil + return false, 0, nil } runtime.LockOSThread() @@ -82,7 +84,7 @@ func BecomeRootInUserNS() (bool, error) { r, w, err := os.Pipe() if err != nil { - return false, err + return false, -1, err } defer r.Close() defer w.Close() @@ -90,13 +92,13 @@ func BecomeRootInUserNS() (bool, error) { pidC := C.reexec_in_user_namespace(C.int(r.Fd())) pid := int(pidC) if pid < 0 { - return false, errors.Errorf("cannot re-exec process") + return false, -1, errors.Errorf("cannot re-exec process") } setgroups := fmt.Sprintf("/proc/%d/setgroups", pid) err = ioutil.WriteFile(setgroups, []byte("deny\n"), 0666) if err != nil { - return false, errors.Wrapf(err, "cannot write setgroups file") + return false, -1, errors.Wrapf(err, "cannot write setgroups file") } var uids, gids []idtools.IDMap @@ -115,7 +117,7 @@ func BecomeRootInUserNS() (bool, error) { uidMap := fmt.Sprintf("/proc/%d/uid_map", pid) err = ioutil.WriteFile(uidMap, []byte(fmt.Sprintf("%d %d 1\n", 0, os.Getuid())), 0666) if err != nil { - return false, errors.Wrapf(err, "cannot write uid_map") + return false, -1, errors.Wrapf(err, "cannot write uid_map") } } @@ -127,13 +129,13 @@ func BecomeRootInUserNS() (bool, error) { gidMap := fmt.Sprintf("/proc/%d/gid_map", pid) err = ioutil.WriteFile(gidMap, []byte(fmt.Sprintf("%d %d 1\n", 0, os.Getgid())), 0666) if err != nil { - return false, errors.Wrapf(err, "cannot write gid_map") + return false, -1, errors.Wrapf(err, "cannot write gid_map") } } _, err = w.Write([]byte("1")) if err != nil { - return false, errors.Wrapf(err, "write to sync pipe") + return false, -1, errors.Wrapf(err, "write to sync pipe") } c := make(chan os.Signal, 1) @@ -150,9 +152,10 @@ func BecomeRootInUserNS() (bool, error) { } }() - if C.reexec_in_user_namespace_wait(pidC) < 0 { - return false, errors.Wrapf(err, "error waiting for the re-exec process") + ret := C.reexec_in_user_namespace_wait(pidC) + if ret < 0 { + return false, -1, errors.Wrapf(err, "error waiting for the re-exec process") } - return true, nil + return true, int(ret), nil } diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go index a6efa73f5..b1f075045 100644 --- a/pkg/rootless/rootless_unsupported.go +++ b/pkg/rootless/rootless_unsupported.go @@ -13,8 +13,8 @@ func IsRootless() bool { // BecomeRootInUserNS is a stub function that always returns false and an // error on unsupported OS's -func BecomeRootInUserNS() (bool, error) { - return false, errors.New("this function is not supported on this os") +func BecomeRootInUserNS() (bool, int, error) { + return false, -1, errors.New("this function is not supported on this os") } // GetRootlessUID returns the UID of the user in the parent userNS |