From 25b647d635c28e2a2ea123522b60f032a5dc19ce Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Tue, 27 Oct 2020 18:43:13 +0100 Subject: [PATCH] Don't rely on XDG_RUNTIME_DIR when running as root This is one more step towards enabling toolbox(1) to be run as root. When invoked as 'sudo toolbox' there's no XDG_RUNTIME_DIR available for the root user. Neither the environment variable nor the directory are present. XDG_RUNTIME_DIR is used for two reasons. First, to place the 'lock' file to synchronize Podman migrations and the initialization stamp file to synchronize the container's entry point with the user-facing 'enter' command running on the host. Second, it's used to propagate things like the user D-Bus, Pipewire and Wayland sockets. The first use-case is important for toolbox(1) itself to work. When running as root, XDG_RUNTIME_DIR is replaced with /run/toolbox for this purpose. The second use-case is mostly ignored because sudo(8) doesn't create a full-fledged user session. Graphical applications can still work by connecting to a X11 server over the local abstract socket or the file system socket in /tmp/.X11-unix. https://github.com/containers/toolbox/issues/267 --- src/cmd/create.go | 30 ++++++++++++++++++++------ src/cmd/initContainer.go | 26 ++++++++--------------- src/cmd/root.go | 7 +++--- src/cmd/run.go | 7 ++++-- src/pkg/utils/utils.go | 46 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 29 deletions(-) diff --git a/src/cmd/create.go b/src/cmd/create.go index de5524e..bbdc474 100644 --- a/src/cmd/create.go +++ b/src/cmd/create.go @@ -191,9 +191,23 @@ func createContainer(container, image, release string, showCommandToEnter bool) toolboxPathEnvArg := "TOOLBOX_PATH=" + toolboxPath toolboxPathMountArg := toolboxPath + ":/usr/bin/toolbox:ro" - xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") - xdgRuntimeDirEnvArg := "XDG_RUNTIME_DIR=" + xdgRuntimeDir - xdgRuntimeDirMountArg := xdgRuntimeDir + ":" + xdgRuntimeDir + var runtimeDirectory string + var xdgRuntimeDirEnv []string + + if currentUser.Uid == "0" { + runtimeDirectory, err = utils.GetRuntimeDirectory(currentUser) + if err != nil { + return err + } + } else { + xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") + xdgRuntimeDirEnvArg := "XDG_RUNTIME_DIR=" + xdgRuntimeDir + xdgRuntimeDirEnv = []string{"--env", xdgRuntimeDirEnvArg} + + runtimeDirectory = xdgRuntimeDir + } + + runtimeDirectoryMountArg := runtimeDirectory + ":" + runtimeDirectory logrus.Debug("Checking if 'podman create' supports '--mount type=devpts'") @@ -337,12 +351,16 @@ func createContainer(container, image, release string, showCommandToEnter bool) "create", "--dns", "none", "--env", toolboxPathEnvArg, - "--env", xdgRuntimeDirEnvArg, + } + + createArgs = append(createArgs, xdgRuntimeDirEnv...) + + createArgs = append(createArgs, []string{ "--hostname", "toolbox", "--ipc", "host", "--label", "com.github.containers.toolbox=true", "--label", "com.github.debarshiray.toolbox=true", - } + }...) createArgs = append(createArgs, devPtsMount...) @@ -370,7 +388,7 @@ func createContainer(container, image, release string, showCommandToEnter bool) "--volume", homeDirMountArg, "--volume", toolboxPathMountArg, "--volume", usrMountArg, - "--volume", xdgRuntimeDirMountArg, + "--volume", runtimeDirectoryMountArg, }...) createArgs = append(createArgs, kcmSocketMount...) diff --git a/src/cmd/initContainer.go b/src/cmd/initContainer.go index f66f094..f0e5033 100644 --- a/src/cmd/initContainer.go +++ b/src/cmd/initContainer.go @@ -23,6 +23,7 @@ import ( "os" "os/user" "path/filepath" + "strconv" "strings" "time" @@ -127,15 +128,7 @@ func initContainer(cmd *cobra.Command, args []string) error { return errors.New(errMsg) } - runtimeDirectory := os.Getenv("XDG_RUNTIME_DIR") - if runtimeDirectory == "" { - logrus.Debug("XDG_RUNTIME_DIR is unset") - - runtimeDirectory = fmt.Sprintf("/run/user/%d", initContainerFlags.uid) - os.Setenv("XDG_RUNTIME_DIR", runtimeDirectory) - - logrus.Debugf("XDG_RUNTIME_DIR set to %s", runtimeDirectory) - } + utils.EnsureXdgRuntimeDirIsSet(initContainerFlags.uid) logrus.Debug("Creating /run/.toolboxenv") @@ -285,16 +278,15 @@ func initContainer(cmd *cobra.Command, args []string) error { logrus.Debug("Finished initializing container") - toolboxRuntimeDirectory := runtimeDirectory + "/toolbox" - logrus.Debugf("Creating runtime directory %s", toolboxRuntimeDirectory) - - if err := os.MkdirAll(toolboxRuntimeDirectory, 0700); err != nil { - return fmt.Errorf("failed to create runtime directory %s", toolboxRuntimeDirectory) + uidString := strconv.Itoa(initContainerFlags.uid) + targetUser, err := user.LookupId(uidString) + if err != nil { + return fmt.Errorf("failed to lookup user ID %s: %w", uidString, err) } - if err := os.Chown(toolboxRuntimeDirectory, initContainerFlags.uid, initContainerFlags.uid); err != nil { - return fmt.Errorf("failed to change ownership of the runtime directory %s", - toolboxRuntimeDirectory) + toolboxRuntimeDirectory, err := utils.GetRuntimeDirectory(targetUser) + if err != nil { + return err } pid := os.Getpid() diff --git a/src/cmd/root.go b/src/cmd/root.go index 1ffce41..41b9251 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -226,10 +226,9 @@ func migrate() error { return fmt.Errorf("failed to create configuration directory") } - runtimeDirectory := os.Getenv("XDG_RUNTIME_DIR") - toolboxRuntimeDirectory := runtimeDirectory + "/toolbox" - if err := os.MkdirAll(toolboxRuntimeDirectory, 0700); err != nil { - return fmt.Errorf("failed to create runtime directory %s", toolboxRuntimeDirectory) + toolboxRuntimeDirectory, err := utils.GetRuntimeDirectory(currentUser) + if err != nil { + return err } migrateLock := toolboxRuntimeDirectory + "/migrate.lock" diff --git a/src/cmd/run.go b/src/cmd/run.go index 796d4b2..a1ac0a8 100644 --- a/src/cmd/run.go +++ b/src/cmd/run.go @@ -238,8 +238,11 @@ func runCommand(container string, logrus.Debugf("Waiting for container %s to finish initializing", container) - runtimeDirectory := os.Getenv("XDG_RUNTIME_DIR") - toolboxRuntimeDirectory := runtimeDirectory + "/toolbox" + toolboxRuntimeDirectory, err := utils.GetRuntimeDirectory(currentUser) + if err != nil { + return err + } + initializedStamp := fmt.Sprintf("%s/container-initialized-%d", toolboxRuntimeDirectory, entryPointPID) logrus.Debugf("Checking if initialization stamp %s exists", initializedStamp) diff --git a/src/pkg/utils/utils.go b/src/pkg/utils/utils.go index 5455298..0575e80 100644 --- a/src/pkg/utils/utils.go +++ b/src/pkg/utils/utils.go @@ -22,6 +22,7 @@ import ( "os" "os/exec" "os/user" + "path" "path/filepath" "regexp" "sort" @@ -174,6 +175,17 @@ func CreateErrorInvalidRelease(executableBase string) error { return errors.New(errMsg) } +func EnsureXdgRuntimeDirIsSet(uid int) { + if xdgRuntimeDir, ok := os.LookupEnv("XDG_RUNTIME_DIR"); !ok { + logrus.Debug("XDG_RUNTIME_DIR is unset") + + xdgRuntimeDir = fmt.Sprintf("/run/user/%d", uid) + os.Setenv("XDG_RUNTIME_DIR", xdgRuntimeDir) + + logrus.Debugf("XDG_RUNTIME_DIR set to %s", xdgRuntimeDir) + } +} + func ForwardToHost() (int, error) { envOptions := GetEnvOptionsForPreservedVariables() toolboxPath := os.Getenv("TOOLBOX_PATH") @@ -336,6 +348,40 @@ func GetMountOptions(target string) (string, error) { return mountOptions, nil } +func GetRuntimeDirectory(targetUser *user.User) (string, error) { + uid, err := strconv.Atoi(targetUser.Uid) + if err != nil { + return "", fmt.Errorf("failed to convert user ID to integer: %w", err) + } + + var runtimeDirectory string + + if uid == 0 { + runtimeDirectory = "/run" + } else { + runtimeDirectory = os.Getenv("XDG_RUNTIME_DIR") + } + + toolboxRuntimeDirectory := path.Join(runtimeDirectory, "toolbox") + logrus.Debugf("Creating runtime directory %s", toolboxRuntimeDirectory) + + if err := os.MkdirAll(toolboxRuntimeDirectory, 0700); err != nil { + wrapped_err := fmt.Errorf("failed to create runtime directory %s: %w", + toolboxRuntimeDirectory, + err) + return "", wrapped_err + } + + if err := os.Chown(toolboxRuntimeDirectory, uid, uid); err != nil { + wrapped_err := fmt.Errorf("failed to change ownership of the runtime directory %s: %w", + toolboxRuntimeDirectory, + err) + return "", wrapped_err + } + + return toolboxRuntimeDirectory, nil +} + // HumanDuration accepts a Unix time value and converts it into a human readable // string. //