summaryrefslogtreecommitdiff
path: root/pkg/rootless
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/rootless')
-rw-r--r--pkg/rootless/rootless_linux.go85
-rw-r--r--pkg/rootless/rootless_unsupported.go7
2 files changed, 92 insertions, 0 deletions
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index 26f4b0b18..904d22ee2 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -9,9 +9,11 @@ import (
"os/exec"
gosignal "os/signal"
"os/user"
+ "path/filepath"
"runtime"
"strconv"
"syscall"
+ "unsafe"
"github.com/containers/storage/pkg/idtools"
"github.com/docker/docker/pkg/signal"
@@ -186,3 +188,86 @@ func BecomeRootInUserNS() (bool, int, error) {
return true, int(ret), nil
}
+
+func readUserNs(path string) (string, error) {
+ b := make([]byte, 256)
+ _, err := syscall.Readlink(path, b)
+ if err != nil {
+ return "", err
+ }
+ return string(b), nil
+}
+
+func readUserNsFd(fd uintptr) (string, error) {
+ return readUserNs(filepath.Join("/proc/self/fd", fmt.Sprintf("%d", fd)))
+}
+
+func getParentUserNs(fd uintptr) (uintptr, error) {
+ const nsGetParent = 0xb702
+ ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(nsGetParent), 0)
+ if errno != 0 {
+ return 0, errno
+ }
+ return (uintptr)(unsafe.Pointer(ret)), nil
+}
+
+// GetUserNSForPid 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.
+//
+// current ns
+// / \
+// TARGET -> a [other containers]
+// /
+// b
+// /
+// NS READ USING THE PID -> c
+func GetUserNSForPid(pid uint) (*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")
+ }
+ if ns == currentNS {
+ return nil, errors.New("process running in the same user namespace")
+ }
+
+ for {
+ nextFd, err := getParentUserNs(fd)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot get parent user namespace")
+ }
+
+ ns, err = readUserNsFd(nextFd)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot read user namespace")
+ }
+
+ if ns == currentNS {
+ syscall.Close(int(nextFd))
+
+ // Drop O_CLOEXEC for the fd.
+ _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_SETFD, 0)
+ if errno != 0 {
+ syscall.Close(int(fd))
+ return nil, errno
+ }
+
+ return os.NewFile(fd, "userns child"), nil
+ }
+ syscall.Close(int(fd))
+ fd = nextFd
+ }
+}
diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go
index 11dfd5aa4..93b04adfd 100644
--- a/pkg/rootless/rootless_unsupported.go
+++ b/pkg/rootless/rootless_unsupported.go
@@ -3,6 +3,8 @@
package rootless
import (
+ "os"
+
"github.com/pkg/errors"
)
@@ -30,3 +32,8 @@ func SetSkipStorageSetup(bool) {
func SkipStorageSetup() bool {
return false
}
+
+// GetUserNSForPid returns an open FD for the first direct child user namespace that created the process
+func GetUserNSForPid(pid uint) (*os.File, error) {
+ return nil, errors.New("this function is not supported on this os")
+}