mirror of
https://github.com/containers/podman.git
synced 2025-12-11 01:11:30 +08:00
Merge pull request #1075 from giuseppe/rootless-no-symlinks-into-storage-path
rootless: fix usage on Fedora Silverblue/CoreOS
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
return runtimeDir
|
||||
resolvedHome, err := filepath.EvalSymlinks(home)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "cannot resolve %s", home)
|
||||
}
|
||||
runtimeDir = filepath.Join(resolvedHome, "rundir")
|
||||
}
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user