summaryrefslogtreecommitdiff
path: root/pkg/rootless
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/rootless')
-rw-r--r--pkg/rootless/rootless.go9
-rw-r--r--pkg/rootless/rootless_linux.c6
-rw-r--r--pkg/rootless/rootless_linux.go108
-rw-r--r--pkg/rootless/rootless_unsupported.go35
4 files changed, 92 insertions, 66 deletions
diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go
new file mode 100644
index 000000000..a531e43ce
--- /dev/null
+++ b/pkg/rootless/rootless.go
@@ -0,0 +1,9 @@
+package rootless
+
+// Opts allows to customize how re-execing to a rootless process is done
+type Opts struct {
+ // Argument overrides the arguments on the command line
+ // for the re-execed process. The process in the namespace
+ // must use rootless.Argument() to read its value.
+ Argument string
+}
diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c
index dfbc7fe33..ff39e9e77 100644
--- a/pkg/rootless/rootless_linux.c
+++ b/pkg/rootless/rootless_linux.c
@@ -32,7 +32,11 @@ syscall_setresgid (gid_t rgid, gid_t egid, gid_t sgid)
static int
syscall_clone (unsigned long flags, void *child_stack)
{
+#if defined(__s390__) || defined(__CRIS__)
+ return (int) syscall (__NR_clone, child_stack, flags);
+#else
return (int) syscall (__NR_clone, flags, child_stack);
+#endif
}
static char **
@@ -273,6 +277,8 @@ reexec_in_user_namespace (int ready)
_exit (EXIT_FAILURE);
}
close (ready);
+ if (b != '1')
+ _exit (EXIT_FAILURE);
if (syscall_setresgid (0, 0, 0) < 0)
{
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index 98692707f..baceebee3 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -11,7 +11,6 @@ import (
"os/user"
"runtime"
"strconv"
- "strings"
"sync"
"syscall"
"unsafe"
@@ -61,6 +60,11 @@ func SkipStorageSetup() bool {
return skipStorageSetup
}
+// Argument returns the argument that was set for the rootless session.
+func Argument() string {
+ return os.Getenv("_LIBPOD_ROOTLESS_ARG")
+}
+
// GetRootlessUID returns the UID of the user in the parent userNS
func GetRootlessUID() int {
uidEnv := os.Getenv("_LIBPOD_ROOTLESS_UID")
@@ -68,7 +72,7 @@ func GetRootlessUID() int {
u, _ := strconv.Atoi(uidEnv)
return u
}
- return os.Getuid()
+ return os.Geteuid()
}
func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) error {
@@ -102,7 +106,7 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap)
// JoinNS re-exec podman in a new userNS and join the user namespace of the specified
// PID.
-func JoinNS(pid uint) (bool, int, error) {
+func JoinNS(pid uint, preserveFDs int) (bool, int, error) {
if os.Geteuid() == 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != "" {
return false, -1, nil
}
@@ -117,6 +121,13 @@ func JoinNS(pid uint) (bool, int, error) {
if int(pidC) < 0 {
return false, -1, errors.Errorf("cannot re-exec process")
}
+ if preserveFDs > 0 {
+ for fd := 3; fd < 3+preserveFDs; fd++ {
+ // These fds were passed down to the runtime. Close them
+ // and not interfere
+ os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd)).Close()
+ }
+ }
ret := C.reexec_in_user_namespace_wait(pidC)
if ret < 0 {
@@ -128,8 +139,16 @@ func JoinNS(pid uint) (bool, int, error) {
// 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.
+// the conmon process. It is a convenience function for JoinDirectUserAndMountNSWithOpts
+// with a default configuration.
func JoinDirectUserAndMountNS(pid uint) (bool, int, error) {
+ return JoinDirectUserAndMountNSWithOpts(pid, nil)
+}
+
+// JoinDirectUserAndMountNSWithOpts 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 JoinDirectUserAndMountNSWithOpts(pid uint, opts *Opts) (bool, int, error) {
if os.Geteuid() == 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != "" {
return false, -1, nil
}
@@ -146,6 +165,12 @@ func JoinDirectUserAndMountNS(pid uint) (bool, int, error) {
}
defer userNS.Close()
+ if opts != nil && opts.Argument != "" {
+ if err := os.Setenv("_LIBPOD_ROOTLESS_ARG", opts.Argument); err != nil {
+ return false, -1, err
+ }
+ }
+
pidC := C.reexec_userns_join(C.int(userNS.Fd()), C.int(mountNS.Fd()))
if int(pidC) < 0 {
return false, -1, errors.Errorf("cannot re-exec process")
@@ -185,27 +210,19 @@ func JoinNSPath(path string) (bool, int, error) {
return true, int(ret), nil
}
-const defaultMinimumMappings = 65536
-
-func getMinimumIDs(p string) int {
- content, err := ioutil.ReadFile(p)
- if err != nil {
- logrus.Debugf("error reading data from %q, use a default value of %d", p, defaultMinimumMappings)
- return defaultMinimumMappings
- }
- ret, err := strconv.Atoi(strings.TrimSuffix(string(content), "\n"))
- if err != nil {
- logrus.Debugf("error reading data from %q, use a default value of %d", p, defaultMinimumMappings)
- return defaultMinimumMappings
- }
- return ret + 1
-}
-
// 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.
+// process. It is a convenience function for BecomeRootInUserNSWithOpts with a default configuration.
func BecomeRootInUserNS() (bool, int, error) {
+ return BecomeRootInUserNSWithOpts(nil)
+}
+
+// BecomeRootInUserNSWithOpts re-exec podman in a new userNS. It returns whether podman was
+// re-execute 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 BecomeRootInUserNSWithOpts(opts *Opts) (bool, int, error) {
if os.Geteuid() == 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != "" {
if os.Getenv("_LIBPOD_USERNS_CONFIGURED") == "init" {
return false, 0, runInUser()
@@ -222,6 +239,13 @@ func BecomeRootInUserNS() (bool, int, error) {
}
defer r.Close()
defer w.Close()
+ defer w.Write([]byte("0"))
+
+ if opts != nil && opts.Argument != "" {
+ if err := os.Setenv("_LIBPOD_ROOTLESS_ARG", opts.Argument); err != nil {
+ return false, -1, err
+ }
+ }
pidC := C.reexec_in_user_namespace(C.int(r.Fd()))
pid := int(pidC)
@@ -229,47 +253,18 @@ func BecomeRootInUserNS() (bool, int, error) {
return false, -1, errors.Errorf("cannot re-exec process")
}
- allowSingleIDMapping := os.Getenv("PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS") != ""
-
var uids, gids []idtools.IDMap
username := os.Getenv("USER")
if username == "" {
user, err := user.LookupId(fmt.Sprintf("%d", os.Getuid()))
- if err != nil && !allowSingleIDMapping {
- if os.IsNotExist(err) {
- return false, 0, errors.Wrapf(err, "/etc/subuid or /etc/subgid does not exist, see subuid/subgid man pages for information on these files")
- }
- return false, 0, errors.Wrapf(err, "could not find user by UID nor USER env was set")
- }
if err == nil {
username = user.Username
}
}
mappings, err := idtools.NewIDMappings(username, username)
- if !allowSingleIDMapping {
- if err != nil {
- return false, -1, err
- }
-
- availableGIDs, availableUIDs := 0, 0
- for _, i := range mappings.UIDs() {
- availableUIDs += i.Size
- }
-
- minUIDs := getMinimumIDs("/proc/sys/kernel/overflowuid")
- if availableUIDs < minUIDs {
- return false, 0, fmt.Errorf("not enough UIDs available for the user, at least %d are needed", minUIDs)
- }
-
- for _, i := range mappings.GIDs() {
- availableGIDs += i.Size
- }
- minGIDs := getMinimumIDs("/proc/sys/kernel/overflowgid")
- if availableGIDs < minGIDs {
- return false, 0, fmt.Errorf("not enough GIDs available for the user, at least %d are needed", minGIDs)
- }
- }
- if err == nil {
+ if err != nil {
+ logrus.Warnf("cannot find mappings for user %s: %v", username, err)
+ } else {
uids = mappings.UIDs()
gids = mappings.GIDs()
}
@@ -277,12 +272,10 @@ func BecomeRootInUserNS() (bool, int, error) {
uidsMapped := false
if mappings != nil && uids != nil {
err := tryMappingTool("newuidmap", pid, os.Getuid(), uids)
- if !allowSingleIDMapping && err != nil {
- return false, 0, err
- }
uidsMapped = err == nil
}
if !uidsMapped {
+ logrus.Warnf("using rootless single mapping into the namespace. This might break some images. Check /etc/subuid and /etc/subgid for adding subids")
setgroups := fmt.Sprintf("/proc/%d/setgroups", pid)
err = ioutil.WriteFile(setgroups, []byte("deny\n"), 0666)
if err != nil {
@@ -299,9 +292,6 @@ func BecomeRootInUserNS() (bool, int, error) {
gidsMapped := false
if mappings != nil && gids != nil {
err := tryMappingTool("newgidmap", pid, os.Getgid(), gids)
- if !allowSingleIDMapping && err != nil {
- return false, 0, err
- }
gidsMapped = err == nil
}
if !gidsMapped {
diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go
index 1823c023e..e01d7855c 100644
--- a/pkg/rootless/rootless_unsupported.go
+++ b/pkg/rootless/rootless_unsupported.go
@@ -11,10 +11,18 @@ func IsRootless() bool {
return false
}
+// 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. It is a convenience function for BecomeRootInUserNSWithOpts with a default configuration.
+func BecomeRootInUserNS() (bool, int, error) {
+ return false, -1, errors.New("this function is not supported on this os")
+}
+
// BecomeRootInUserNS is a stub function that always returns false and an
// error on unsupported OS's
-func BecomeRootInUserNS() (bool, int, error) {
- return false, -1, errors.New("this function is not supported on this os1")
+func BecomeRootInUserNSWithOpts(opts *Opts) (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
@@ -33,19 +41,32 @@ func SkipStorageSetup() bool {
// JoinNS re-exec podman in a new userNS and join the user namespace of the specified
// PID.
-func JoinNS(pid uint) (bool, int, error) {
- return false, -1, errors.New("this function is not supported on this os2")
+func JoinNS(pid uint, preserveFDs int) (bool, int, error) {
+ return false, -1, errors.New("this function is not supported on this os")
}
// JoinNSPath re-exec podman in a new userNS and join the owner user namespace of the
// specified path.
func JoinNSPath(path string) (bool, int, error) {
- return false, -1, errors.New("this function is not supported on this os3")
+ return false, -1, errors.New("this function is not supported on this os")
+}
+
+// JoinDirectUserAndMountNSWithOpts 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 JoinDirectUserAndMountNSWithOpts(pid uint, opts *Opts) (bool, int, error) {
+ return false, -1, errors.New("this function is not supported on this os")
}
// 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.
+// the conmon process. It is a convenience function for JoinDirectUserAndMountNSWithOpts
+// with a default configuration.
func JoinDirectUserAndMountNS(pid uint) (bool, int, error) {
- return false, -1, errors.New("this function is not supported on this os4")
+ return false, -1, errors.New("this function is not supported on this os")
+}
+
+// Argument returns the argument that was set for the rootless session.
+func Argument() string {
+ return ""
}