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".
unshare
command can do what's in this answer with--map-user=
and--map-group=
. – A.B Apr 21 '22 at 04:04/proc/$ID/uid_map
instead of using/proc/self/uid_map
in the child process or doing the same with the custom "revertuid". – Compholio Apr 27 '22 at 22:52