From 6d1eecf7cf10fe736d86479dbb0ac0377fc89488 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 19 Sep 2018 14:09:46 +0200 Subject: create, rootless: join the userns of ns:PATH Signed-off-by: Giuseppe Scrivano Closes: #1507 Approved by: rhatdan --- pkg/rootless/rootless_linux.go | 70 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 9 deletions(-) (limited to 'pkg/rootless/rootless_linux.go') diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 92020cf1c..a9a914a62 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -111,6 +111,32 @@ func JoinNS(pid uint) (bool, int, error) { return true, int(ret), nil } +// 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) { + if os.Geteuid() == 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != "" { + return false, -1, nil + } + + userNS, err := getUserNSForPath(path) + if err != nil { + return false, -1, err + } + defer userNS.Close() + + pidC := C.reexec_userns_join(C.int(userNS.Fd())) + 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 +} + // 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 @@ -229,6 +255,15 @@ func readUserNsFd(fd uintptr) (string, error) { return readUserNs(filepath.Join("/proc/self/fd", fmt.Sprintf("%d", fd))) } +func getOwner(fd uintptr) (uintptr, error) { + const nsGetUserns = 0xb701 + ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(nsGetUserns), 0) + if errno != 0 { + return 0, errno + } + return (uintptr)(unsafe.Pointer(ret)), nil +} + func getParentUserNs(fd uintptr) (uintptr, error) { const nsGetParent = 0xb702 ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(nsGetParent), 0) @@ -238,7 +273,31 @@ func getParentUserNs(fd uintptr) (uintptr, error) { return (uintptr)(unsafe.Pointer(ret)), nil } -// getUserNSForPid returns an open FD for the first direct child user namespace that created the process +func getUserNSForPath(path string) (*os.File, error) { + u, err := os.Open(path) + if err != nil { + return nil, errors.Wrapf(err, "cannot open %s", path) + } + defer u.Close() + fd, err := getOwner(u.Fd()) + if err != nil { + return nil, err + } + + return getUserNSFirstChild(fd) +} + +func getUserNSForPid(pid uint) (*os.File, error) { + path := filepath.Join("/proc", fmt.Sprintf("%d", pid), "ns/user") + u, err := os.Open(path) + if err != nil { + return nil, errors.Wrapf(err, "cannot open %s", path) + } + + return getUserNSFirstChild(u.Fd()) +} + +// getUserNSFirstChild returns an open FD for the first direct child user namespace that created the process // Each container creates a new user namespace where the runtime runs. The current process in the container // might have created new user namespaces that are child of the initial namespace we created. // This function finds the initial namespace created for the container that is a child of the current namespace. @@ -250,19 +309,12 @@ func getParentUserNs(fd uintptr) (uintptr, error) { // b // / // NS READ USING THE PID -> c -func getUserNSForPid(pid uint) (*os.File, error) { +func getUserNSFirstChild(fd uintptr) (*os.File, error) { currentNS, err := readUserNs("/proc/self/ns/user") if err != nil { return nil, err } - path := filepath.Join("/proc", fmt.Sprintf("%d", pid), "ns/user") - u, err := os.Open(path) - if err != nil { - return nil, errors.Wrapf(err, "cannot open %s", path) - } - - fd := u.Fd() ns, err := readUserNsFd(fd) if err != nil { return nil, errors.Wrapf(err, "cannot read user namespace") -- cgit v1.2.3-54-g00ecf