What's happening here is that your "unshare" process does not have access to write to the
gid_maps) files on the external user namespace.
In that namespace, the pseudo-files in
/proc/<PID> will be root-owned, and as if your effective uid is still that of your own user, you will not have access to write to those files.
You can visualize that easily by running a process in background (such as
sleep) and inspecting the permissions of the
setgroups file while it's running (
gid_maps behave exactly the same.)
First, with a binary that doesn't have additional capabilities:
$ cp /usr/bin/sleep .
$ ./sleep 10 &
$ ls -l /proc/11209/setgroups
-rw-r--r--. 1 myuser myuser 0 Jul 31 12:33 /proc/11209/setgroups
+ Done ./sleep 10
Then, let's add some file capabilities to it and see the ownership change:
$ sudo setcap cap_net_bind_service=ep sleep
$ ./sleep 10 &
$ ls -l /proc/11220/setgroups
-rw-r--r--. 1 root root 0 Jul 31 12:34 /proc/11220/setgroups
+ Done ./sleep 10
The ownership of the files in
/proc/<PID> is controlled by the "dumpable" flag inside the Linux kernel, which is used to prevent leaking information from a privileged process to an unprivileged user.
And considering you're running
unshare with additional capabilities but still with a non-root effective uid, write access to these pseudo-files which are owned by root is prevented through the normal access controls of the operating system.
You can inspect the value of the "dumpable" flag by using the
PR_GET_DUMPABLE command of the prctl(2) syscall.
In the description for
PR_SET_DUMPABLE you'll also find:
Normally, this flag is set to 1. However, it is reset to the current value contained in the file
/proc/sys/fs/suid_dumpable (which by default has the value 0), in the following circumstances:
- The process executes (
execve(2)) a program that has file capabilities (see
capabilities(7)), but only if the permitted capabilities gained exceed those already permitted for the process.
Which is exactly your case, with an
unshare binary that has file capabilities.
Interestingly, using a root owned setuid binary for
unshare doesn't run into this exact issue. Yes, it will clear the "dumpable" flags and the files in
/proc/<PID> will be owned by root. But considering your effective uid is also root when running a setuid binary, the access will be allowed.
In that case, you end up running into a different problem, which has to do with
unshare's logic not being able to handle that particular setup (probably something to do with the effective uid and the actual uid not matching):
$ cp /usr/bin/unshare .
$ sudo setcap -r unshare
$ sudo chown root:root unshare
$ sudo chmod 4711 unshare
$ ./unshare -r
-bash: cannot set uid to 65534: effective uid 0: Invalid argument
Also notice that the clearing of the "dumpable" flag was triggered by running a binary with file capabilities set. If you get additional capabilities in a different way, you might not run into this trouble. For instance, using "ambient" capabilities is a good way to get additional capabilities outside the namespace, while still not running into trouble when unsharing into a new user namespace.
(Unfortunately, there is not a lot of tooling around ambient capabilities yet.
libcap only ships support in latest git, past version 2.25, which is the one currently shipped in most distros.
capsh is fairly low-level, so it's not that easy to get it right either. See here for details on using latest
capsh to add ambient capabilities. I also tried
systemd-run but couldn't really manage to get ambient capabilities set up there either. Anyways, something for you to look into, if you do need capabilities, non-root user & user namespaces together!)