alexlarsson/xdg-app

flatpak-builder requires unprivileged user namespaces, even with setuid bwrap

smcv opened this issue · 9 comments

smcv commented

With Debian's kernel (unprivileged user namespaces disabled at runtime), a setuid-root bwrap, and some extra -v and less >/dev/null in the tests:

$ gnome-desktop-testing-runner flatpak/test-builder
Running test: /usr/share/installed-tests/flatpak/test-builder.sh.test
1..3
Downloading sources
Initializing build dir
Committing stage init to cache
Starting build of org.test.Hello2
========================================================================
Building module test in /var/tmp/test-flatpak-P5hthk/.flatpak-builder/build/test-1
========================================================================
Running: flatpak build --nofilesystem=host --filesystem=/var/tmp/test-flatpak-P5hthk/.flatpak-builder/build/test-1 /var/tmp/test-flatpak-P5hthk/appdir /bin/sh -c mkdir /app/cleanup/
unshare user ns: Operation not permitted
error: Child process exited with code 1
Test flatpak/test-builder.sh.test failed: Child process exited with code 1
SUMMARY: total: 1 passed: 0 skipped: 0 failed: 1

I believe the minimal reproducer might be --dev /dev --unshare-user:

$ /usr/lib/flatpak/bwrap --ro-bind / / /bin/true; echo $?
0
$ /usr/lib/flatpak/bwrap --ro-bind / / --unshare-user /bin/true; echo $?
0
$ /usr/lib/flatpak/bwrap --ro-bind / / --dev /dev /bin/true; echo $?
0
$ /usr/lib/flatpak/bwrap --ro-bind / / --dev /dev --unshare-user /bin/true; echo $?
unshare user ns: Operation not permitted
1

Any ideas? (@cgwalters?)

I'm using Debian, which has a patch to make unprivileged user namespaces runtime-configurable and off by default, because our kernel maintainer doesn't trust them to be secure. I believe many other distributions either apply a similar patch (e.g. Ubuntu), or disable unprivileged user namespaces completely (e.g. Arch Linux).

So, its failing in an "interesting" place. The initial clone() with CLONE_NEWUSER succeeded, but to work around some bullshit in the kernel we have to do a second unshare CLONE_NEWUSER.

The problem is that we want to do a user namespace mapping from the user to the user, all other things unmapped. But, that means we can't mount devpts, because atm (see patches on kernel list) it needs uid 0 to be mapped in the namespace (because the dev/pts/ptmx file is owned by root).

So, what we do is map the uid to 0 first, mount the devpts and then map 0 back to the real uid. Its this second unshare that fails.

It seems to work here though, so i don't know why its not working for you. Maybe because of some detail in how debian disables user namespaces.

One option in the privileged case is to do a two-user mapping, the real uid to itself, and nobody to 0, then the devpts mount should work. This is not possible in the unprivileged user namespace case though, that can only map a single user.

smcv commented

nobody to 0

This seems alarming, I suspect some daemons still run as nobody. Is mapping 0 to 0, and the real uid to itself, feasible?

The exact patch used in Debian is https://sources.debian.net/src/linux/4.5.4-1/debian/patches/debian/add-sysctl-to-disallow-unprivileged-CLONE_NEWUSER-by-default.patch/. The intention is that by default, our kernel won't let unprivileged users create user namespaces because our kernel maintainer considers it to be a vulnerability waiting to happen (and sadly he's probably right), but you can toggle it to Fedora-like behaviour at runtime.

Not just waiting to happen but has happened, see CVE-2016-3135 linked in the bubblewrap readme. But in the long term userns will be hardened.

+smcv: Mapping 0 to 0 is also possible obviously, but is that more secure? The reason we don't want to map all users is to not leak information about anything. For instance if a file on the host is owned by root it will be visible as owned by root in the sandbox, whereas if we map nobody to 0 then files owned by nobody will be shown as owned by root and everything else as unmapped (nfsnobody).

I guess it could be a bit weird to report "wrong" ownership id though, so mapping 0 to 0 might make sense due to this. Its not like the user could become uid 0 anyway, due to no-new-privileges being set.

I tried to map:

0 0 1\n
1000 1000 1\n

But I only got permission denied writing the uid map file, even though i have CAP_SETUID. I don't understand why...

smcv commented

Mapping 0 to 0 is also possible obviously, but is that more secure?

My concern was that if nobody in the outer namespace is mapped to uid 0 in the inner namespace, then processes in the inner namespace might treat processes running as nobody in the outer namespace as being particularly privileged, for instance receiving IPC requests that are demonstrably coming from (what the inner namespace considers to be) uid 0, and obeying those requests because after all, surely uid 0 is privileged?

(For instance, the AT-SPI accessibility bus allows connections from either its own uid or uid 0, so that the sudo gedit ... anti-pattern is compatible with assistive technologies.)

I think I'd feel more confident about this if the "outer" uid that is mapped to 0 was either 0, or a system user allocated by the (flatpak or) bubblewrap package, that will not run any unprivileged processes (or more likely, no processes at all). If someone good at security can explain how I'm just being paranoid and it's actually fine, that's also fine as a solution of course :-)

While flatpak embeds bubblewrap as a submodule (i.e. until flatpak/bubblewrap mature a bit), they could share the system user, perhaps using _bwrap or _bubblewrap as its default username. The flatpak and bubblewrap maintainers in any given distribution would have to make sure they agree on that name and its semantics, but those maintainers are going to have to work together anyway.

(The underscore prefix originates in OpenBSD, and is recommended by systemd in the systemd-sysusers documentation. It's also one of several proposals for how to name system users in Debian, although unfortunately there's no consensus on it.)

This issue was moved to flatpak/flatpak#2