create, rootless: join the userns of ns:PATH

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>

Closes: #1507
Approved by: rhatdan
This commit is contained in:
Giuseppe Scrivano
2018-09-19 14:09:46 +02:00
committed by Atomic Bot
parent 1c73404fe1
commit 6d1eecf7cf
3 changed files with 74 additions and 9 deletions

View File

@ -838,6 +838,13 @@ func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *l
}
}
namespacesStr := []string{string(createConfig.IpcMode), string(createConfig.NetMode), string(createConfig.UsernsMode), string(createConfig.PidMode), string(createConfig.UtsMode)}
for _, i := range namespacesStr {
if cc.IsNS(i) {
return rootless.JoinNSPath(cc.NS(i))
}
}
namespaces := []namespace{createConfig.IpcMode, createConfig.NetMode, createConfig.UsernsMode, createConfig.PidMode, createConfig.UtsMode}
for _, i := range namespaces {
if i.IsContainer() {

View File

@ -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")

View File

@ -36,3 +36,9 @@ func SkipStorageSetup() bool {
func JoinNS(pid uint) (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 os")
}