Linux – unshare –map-root-user switch to original uid/username after setup

linuxnamespacerootunshareusers

I'm using unshare to create per process mounts, which is working perfectly fine by

unshare -m --map-root-user

However, after having created my bind-mounts by

mount --bind src dst

I want to change the UID to my original user, so that whoami (and others) echoes my username like echo $USER does.

I have already tried the answer of
Simulate chroot with unshare

However, doing su – user1 after chroot /, I get

su: Authentication failure
(Ignored)
setgid: Invalid argument

I have tested this on Ubuntu 18.04 Beta, Debian stretch, openSUSE-Leap-42.3.
It's all the same. I guess something has changed in the kernel since this answer was working.

What is a working and correct way to do that (of course without beeing real root)?

Best Answer

The unshare(1) command can't do it:

-r, --map-root-user
[...] As a mere convenience feature, it does not support more sophisticated use cases, such as mapping multiple ranges of UIDs and GIDs.

Supplementary groups if any (video, ...) will be lost anyway (or mapped to nogroup).

By changing again into a 2nd new user namespace, it's possible to revert back the mapping. This requires a custom program, since unshare(1) won't do it. Here's a very minimalistic C program as proof of concept (one user only: uid/gid 1000/1000, zero failure check). Let's call it revertuid.c:

#define _GNU_SOURCE
#include <sched.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <unistd.h>

int main(int argc, char *argv[]) {
    int fd;

    unshare(CLONE_NEWUSER);
    fd=open("/proc/self/setgroups",O_WRONLY);
    write(fd,"deny",4);
    close(fd);
    fd=open("/proc/self/uid_map",O_WRONLY);
    write(fd,"1000 0 1",8);
    close(fd);
    fd=open("/proc/self/gid_map",O_WRONLY);
    write(fd,"1000 0 1",8);
    close(fd);
    execvp(argv[1],argv+1);
}

It's just doing the reverse mapping of the mapping done by unshare -r -m, which was unavoidable, to be able to be root and use mount, as seen with:

$ strace unshare -r -m /bin/sleep 1 2>&1 |sed -n '/^unshare/,/^execve/p'
unshare(CLONE_NEWNS|CLONE_NEWUSER)      = 0
open("/proc/self/setgroups", O_WRONLY)  = 3
write(3, "deny", 4)                     = 4
close(3)                                = 0
open("/proc/self/uid_map", O_WRONLY)    = 3
write(3, "0 1000 1", 8)                 = 8
close(3)                                = 0
open("/proc/self/gid_map", O_WRONLY)    = 3
write(3, "0 1000 1", 8)                 = 8
close(3)                                = 0
execve("/bin/sleep", ["/bin/sleep", "1"], [/* 18 vars */]) = 0

So that gives:

user@stretch-amd64:~$ gcc -o revertuid revertuid.c
user@stretch-amd64:~$ mkdir -p /tmp/src /tmp/dst
user@stretch-amd64:~$ touch /tmp/src/file
user@stretch-amd64:~$ ls /tmp/dst
user@stretch-amd64:~$ id
uid=1000(user) gid=1000(user) groups=1000(user)
user@stretch-amd64:~$ unshare -r -m
root@stretch-amd64:~# mount --bind /tmp/src /tmp/dst
root@stretch-amd64:~# ls /tmp/dst
file
root@stretch-amd64:~# exec ./revertuid bash
user@stretch-amd64:~$ ls /tmp/dst
file
user@stretch-amd64:~$ id
uid=1000(user) gid=1000(user) groups=1000(user)

Or shorter:

user@stretch-amd64:~$ unshare -r -m sh -c 'mount --bind /tmp/src /tmp/dst; exec ./revertuid bash'
user@stretch-amd64:~$ ls /tmp/dst
file

The behaviour probably changed after kernel 3.19 as seen in user_namespaces(7):

The /proc/[pid]/setgroups file was added in Linux 3.19, but was backported to many earlier stable kernel series, because it addresses a security issue. The issue concerned files with permissions such as "rwx---rwx".

Related Question