7

I observed the following phenomenon that I can not explain. After adding the CAP_SYS_ADMIN capability, unshare is no longer able to write to /proc/self/setgroups.

In fact, writing to this file requires the capability, but that is achieved by changing the user namespace. So why does adding the capability to the parent process prevents writing to this file?

me@myhost:~$ unshare -r
root@myhost:~# exit
logout

me@myhost:~$ sudo setcap cap_sys_admin=ep /usr/bin/unshare
me@myhost:~$ unshare -r
unshare: cannot open /proc/self/setgroups: Permission denied

me@myhost:~$ sudo setcap cap_sys_admin= /usr/bin/unshare
me@myhost:~$ unshare -r
root@myhost:~#

By the way: I am running Ubuntu 16.04.4 LTS with kernel version 4.4 and the version of util-linux (including unshare) is 2.27.1.

10
+50

What's happening here is that your "unshare" process does not have access to write to the setgroups (and uid_maps, 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 (uid_maps and gid_maps behave exactly the same.)

First, with a binary that doesn't have additional capabilities:

$ cp /usr/bin/sleep .
$ ./sleep 10 &
[1] 11209
$ ls -l /proc/11209/setgroups
-rw-r--r--. 1 myuser myuser 0 Jul 31 12:33 /proc/11209/setgroups
[1]+  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 &
[1] 11220
$ ls -l /proc/11220/setgroups 
-rw-r--r--. 1 root root 0 Jul 31 12:34 /proc/11220/setgroups
[1]+  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
-bash-4.4$ 

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!)

  • 1
    thanks for mentioning Ambient capabilities, for example that's interesting to avoid limitations described there to bind to a privileged port. – A.B Aug 2 '18 at 18:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.