Release 0.4.0
- Add support for reusing existing namespaces with --userns and --pidns
- Stores namespace info in status json
- In setuid mode pid 1 is now marked dumpable
- Now builds with musl libc
This enables these options in this case and also ensures we set[ug]id
to the destination ids early in entering the namespace because
otherwise creating files during sandbox setup fails if the real user
id isn't mapped in the destination user namespace (and to make us
actually be that user/group).
This allows a sandbox to share a pid namespace with another sandbox.
For this to work the namespace passed in must be owned by the user
namespace that bwrap is using, which implies either that you pass in
--userns pointing, or run under that user namespace already. In the
former case you'd typically take the userns from a running bwrap
--unshare-user instance, whereas the second case happens when using
bwrap in the setuid mode without user namespaces.
If both --unshare-pid and --pidns are specified then we first
switch to the pid namespace, and then unshare from there. This is
useful if you want a pid-isolated sandob that is visible to another
sandbox.
The implementation is a bit tricky, as it needs to fork() in order
to activate the setns():ed pid namespaces, which means we have to
pass through the final pid via a socket to make the kernel translate
the pid to the initial pid namespace for us to waitpid() on it.
This allows you to reuse an existing user namespace to set up all the
other namespaces, entering that instead of creating a new one. The
reason you want to do this is that you can then also reuse other
namespaces that are owned by the user namespace. Typically you use
this to partially re-enter a previoulsy created bubblewrap sandbox.
This also adds --userns2 which is similar to --userns, but this is
switched into at the end instead of the start. Bubblewrap sometimes
creates nested such user namespaces[1], and to be able to reuse such a
setup we need to similarly reuse both namespaces via --userns2.
Technically using setns() is probably safe even in the privileged
case, because we got passed in a file descriptor to the namespace, and
that can only be gotten if you have ptrace permissions against the
target, and then you could do whatever to the namespace
anyway. However, for practical reasons this isn't useable for bwrap,
because (as described in a comment in acquire_privs()) setuid mode
causes root to own the namespaces that it creates. So as you will not
be able to access these namespaces for reuse anyway, its best to
disable it (in case of unexpected security issues).
[1] This is to work around an issue with mounting devpts without uid 0
mapped in the user namespace, where the outer namespace owns all the
other namespaces but the inner one has the right mappings.
Now that we're properly getting rid of root in these we can mark it
dumpable, which enables use of some /proc files, like /proc/$pid/root that
was previously not accessible for pid1 in the sandbox.
It turns out we have this check in drop_privs():
if (getuid () == 0 && setuid (opt_sandbox_uid) < 0)
Which is supposed to drop back to the regular uid in the case
we're in setuid mode and we're in the monitor_child() or do_init()
processes.
Unfortunately we're setuid, not plain root, so uid is not 0, but euid is zero.
This caused the monitoring processes to be running partially as root
which shows up weird in /proc.
Fix this by checking euid for 0 instead.
Make sure the namespace information that is written to info.json
and json-status.json matches the namespace id inside the sandbox.
Closes: #323
Approved by: alexlarsson
When writing the info-fd or the json-status-fd and a new pid name-
space is created, write the namespace id to the info/json. The
namespace id is encoded in the inode (and device) number of the
symbolic link in /proc/<pid>/ns/<namespace>, see namespaces(7)
for more information.
We obtain the information before we drop privileges, which is needed
when bwrap is run as setuid because then the namespace information
is not readable after having dropped the privileges.
Additionally we no retain the CAP_SYS_PTRACE capability because
that is needed to de-reference the links in /proc/<pid>/ns.
The user namespace information is omitted, because at the time when
we obtain the namespace id information, the user namespace is not yet
the new one, i.e. unshare (CLONE_NEWUSER), comes later.
Closes: #323
Approved by: alexlarsson
When optional cgroup unsharing was requested and the test for it
succeeds, set opt_unshare_cgroup to TRUE so it can be used later
to inspect if cgroup unsharing is enabled.
Closes: #323
Approved by: alexlarsson
Fedora 27 has been EOL for a while now... bump it to 29.
It'd be nice to test against FCOS, though it's not currently supported
by PAPR. It's in maintenance mode, but I might add support for it as a
stopgap. Medium-term though, I want to deprecate PAPR in favour of other
CI solutions.
Anyway, for now hopefully this should fix the CI on this repo.
Closes: #327
Approved by: alexlarsson
An attacker could pre-create /tmp/.bubblewrap-$UID and make it a
non-directory, non-symlink (in which case mounting our tmpfs would fail,
causing denial of service), or make it a symlink under their control
(potentially allowing bad things if the protected_symlinks sysctl is
not enabled).
Instead, temporarily mount the tmpfs on a directory that we are sure
exists and is not attacker-controlled. /tmp (the directory itself, not
a subdirectory) will do.
Fixes: #304
Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=923557
Signed-off-by: Simon McVittie <smcv@debian.org>
Closes: #305
Approved by: cgwalters
The exit code is only reported if it exited after a successful exec.
This is accomplished with a pipe, where the write end is closed on exec.
To distinguish between pipe-close pre-exec and at-exec,
data is written to the pipe immediately before calling exec
so if it is closed before exec the pipe is empty
and if it is closed during exec it contains a 1 byte value.
To further distinguish between a successful exec and a failed exec,
on exec failure a second value is written.
Signed-off-by: Richard Maw <richard.maw@codethink.co.uk>
Closes: #257
Closes: #293
Approved by: cgwalters
For the non-suid case, we were assuming that the host system would have
merged /usr (e.g. /bin -> /usr/bin). This isn't yet the case for all
distros, so let's handle both.
Closes: #290
Approved by: smcv
These ignore source files not existing which allows bwrap using
applications to avoid repeatedly checking if files exist.
Closes: #283
Approved by: alexlarsson
Some variables like base_path ("/run/user/%d/.bubblewrap") are
declared with the cleanup attribute in main(), but this cleanup is not
run when in the parent process, since it calls exit() in monitor_child().
Use return statements instead of exit() so that cleanup attributes
will be run.
Closes: #271
Approved by: smcv
Keep a reference to the previous working directory and use it for the
umount.
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
Closes: #256
Approved by: cgwalters
Skip these mounts when the process will keep CAP_SYS_ADMIN as it will
anyway able to umount them.
This fix the case of running bwrap inside of a bwrap with a new pid
namespace and mount /proc.
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
Closes: #256
Approved by: cgwalters
This is preparatory work for supporting recursive bwrap. Without this, using
`mount()` on the second `/` won't work, since it won't be a mount point.
Closes: #256
Approved by: cgwalters
This shouldn't matter unless someone wants to run an inadvisably-named
executable, but it's best-practice for commands that pass on some
of their arguments to a subsequent command.
It allows an invocation like:
bwrap --ro-bind /container / -- "$@"
to search PATH in the container for an executable named according to
"$1", even if $1 has a pathological value like
"--this-has-a-stupid-name--", or even a value that might be
deliberately trying to break bwrap's parsing like "--bind".
Fixes: #259
Signed-off-by: Simon McVittie <smcv@collabora.com>
Closes: #261
Approved by: cgwalters
For NFS mounts if we call mkdir() on a read-only mount (such as when
we've created a read-only bind mount) the kernel will nor return EEXIST
even when the directory exists, instead returning EROFS.
So, we add (and use) an ensure_dir() helper that stats before calling
mkdir.
Closes: #258
Approved by: giuseppe
According to PEP 394, the python command is meant to be Python 2
until at least 2020, so in practice this script will be run with
Python 2 for now (except on Arch Linux); but it seems good to be
more future-proof.
In Python 3, os.write() takes a bytestring (bytes object), not a
text string (str/unicode object). In Python 2 ≥ 2.6, the b'' syntax
is supported and gives a str object, because that was a bytestring
in Python 2; either way, b'1' is an acceptable argument to os.write().
In Python ≥ 3.4, the result of os.pipe() is close-on-exec
(non-inheritable) by default, so undo that where needed.
Signed-off-by: Simon McVittie <smcv@collabora.com>
Closes: #246
Approved by: giuseppe
This means we can use it with an installed bwrap, which seems a more
common use of a demo script than a just-compiled bwrap, and is
consistent with the shell scripts.
Signed-off-by: Simon McVittie <smcv@collabora.com>
Closes: #246
Approved by: giuseppe