Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions bind-mount.c
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,15 @@ bind_mount (int proc_fd,
be safe to ignore because its not something the user can access. */
if (errno != EACCES)
{
/* And if we don't need a security boundary, we can also
* ignore other remount errors for submounts. */
if (options & BIND_FAIL_OPEN)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified that having the check under the for loop that start with i = 1 seem to be enough:

  if (recursive)
    {
      for (i = 1; mount_tab[i].mountpoint != NULL; i++)
        {
           ...

This seems to cover also host mount points like /hdd directly under /, because even when --bind / / is used mount_tab[0].mountpoint will be /newroot, and so the code will handle /newroot/hdd

And we can assume that /newroot is not on an automount?

So we shouldn't nee to add the check also for mount_tab[0].mountpoint.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we can assume that /newroot is not on an automount?

Normally, yes: it's on a tmpfs, look for comment /* Create a tmpfs which we will use as / in the namespace */.

If we're mounting an automount onto the root (like --bind /mnt/am / where /mnt/am is an automount) then I think mount_tab[0].mountpoint might be the automounter? But in that case, I think mount(2) would trigger the automount, mount the underlying filesystem, and either succeed or fail as appropriate. Please could you confirm that?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's reasonable to say that if /mnt/bad is a broken automount, then bwrap --not-a-security-boundary ... --bind /mnt/bad $DEST ... should still fail: even though we're not imposing a security boundary, we still asked to mount /mnt/bad onto $DEST, and if we're unable to do so, we should fail.

Conversely, if /mnt/bad is a broken automount and we mount /mnt onto $DEST successfully, it's a lot more reasonable to succeed even though were unable to remount /mnt/bad as nodev.

{
warn ("Can't remount %s submount (%s), ignoring error",
mount_tab[i].mountpoint, strerror (errno));
continue;
}

if (failing_path != NULL)
*failing_path = xstrdup (mount_tab[i].mountpoint);

Expand Down
1 change: 1 addition & 0 deletions bind-mount.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ typedef enum {
BIND_READONLY = (1 << 0),
BIND_DEVICES = (1 << 2),
BIND_RECURSIVE = (1 << 3),
BIND_FAIL_OPEN = (1 << 4),
} bind_option_t;

typedef enum
Expand Down
22 changes: 19 additions & 3 deletions bubblewrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ static int opt_tmp_overlay_count = 0;
static int next_perms = -1;
static size_t next_size_arg = 0;
static int next_overlay_src_count = 0;
static bool opt_not_a_security_boundary = false;

#define CAP_TO_MASK_0(x) (1L << ((x) & 31))
#define CAP_TO_MASK_1(x) CAP_TO_MASK_0(x - 32)
Expand Down Expand Up @@ -350,6 +351,8 @@ usage (int ecode, FILE *out)
" --perms OCTAL Set permissions of next argument (--bind-data, --file, etc.)\n"
" --size BYTES Set size of next argument (only for --tmpfs)\n"
" --chmod OCTAL PATH Change permissions of PATH (must already exist)\n"
" --not-a-security-boundary Do not fail hard when some sandbox setup steps fail;\n"
" use only when the sandbox is not a security boundary\n"
);
exit (ecode);
}
Expand Down Expand Up @@ -1003,9 +1006,18 @@ setup_newroot (bool unshare_pid)
else if (ensure_file (dest, 0444) != 0)
die_with_error ("Can't create file at %s", op->dest);

setup_op_bind_mount ((op->type == SETUP_RO_BIND_MOUNT ? BIND_READONLY : 0) |
(op->type == SETUP_DEV_BIND_MOUNT ? BIND_DEVICES : 0),
source, dest);
bind_option_t bind_flags = 0;

if (opt_not_a_security_boundary)
bind_flags |= BIND_FAIL_OPEN;

if (op->type == SETUP_RO_BIND_MOUNT)
bind_flags |= BIND_READONLY;

if (op->type == SETUP_DEV_BIND_MOUNT)
bind_flags |= BIND_DEVICES;

setup_op_bind_mount (bind_flags, source, dest);

if (op->fd >= 0)
{
Expand Down Expand Up @@ -2427,6 +2439,10 @@ parse_args_recurse (int *argcp,
argv += 2;
argc -= 2;
}
else if (strcmp (arg, "--not-a-security-boundary") == 0)
{
opt_not_a_security_boundary = true;
}
else if (strcmp (arg, "--") == 0)
{
argv += 1;
Expand Down
27 changes: 27 additions & 0 deletions bwrap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,33 @@
command line. Please be careful to the order they are specified.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--not-a-security-boundary</option></term>
<listitem>
<para>
Declare that this invocation of <command>bwrap</command> is not
intended to create a security boundary between the sandbox and the
host system. When this option is given, certain non-fatal sandbox
setup failures (such as a bind mount failing because an automounter
did not respond in time) will produce a warning and will be skipped,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, technically it's not the bind mount that is failing in the scenario that motivated this option. That scenario is something like this:

  • user wants to bwrap --ro-bind (or bwrap --bind), let's say /mnt onto /mnt
  • bwrap --ro-bind implies that they want the whole /mnt hierarchy to be mounted ro, and similarly either bwrap --ro-bind or bwrap --bind implies that they want the whole /mnt hierarchy to be mounted nodev
  • but mount(2) can't do that atomically, so we can only achieve that[1] with the C equivalent of:
    1. mount --bind /oldroot/mnt /newroot/mnt
    2. mount -o remount,ro,nodev,nosuid /newroot/mnt
    3. mount -o remount,ro,nodev,nosuid /newroot/mnt/bad
    4. mount -o remount,ro,nodev,nosuid /newroot/mnt/good
    5. ...

"A bind mount failing" would be the mount --bind-equivalent, but that's not what is failing here: what is failing here is the remount of /newroot/mnt/bad, specifically.

So I think it would be more accurate to say something like

          setup failures (such as a subdirectory remount failing because an automounter
          did not respond in time) will produce a warning and will be skipped,

If you're touching this text anyway, consider also doing semantic line-breaks after each sentence, after each comma, before and after the part in parentheses and so on, even if the line-length limit has not yet been reached, to allow later edits with minimal diff. Ordinary flowing text in Docbook gets line-wrapped automatically, so that won't affect the rendered output.


[1] assuming we don't want to increase our minimum kernel version, which I think we don't. In newer kernels we'd be able to use the "new mount APIs", like open_tree(2), mount_setattr(2) and move_mount(2), but using those APIs exclusively would mean losing the ability to run on old host OSs like Ubuntu 20.04.

rather than causing <command>bwrap</command> to exit with an error.
Comment thread
smcv marked this conversation as resolved.
The effect of this option might be extended to make other sandbox
setup operations non-fatal in future releases of bubblewrap.
</para>
<para>
This option is intended for callers such as
<application>xdg-dbus-proxy</application> or Steam that use
<command>bwrap</command> to adjust the filesystem layout for a
process, but do not rely on it to create a security boundary.
</para>
<para>
Other operations that are fundamental to establishing the sandbox
(creating namespaces, <function>pivot_root</function>,
dropping capabilities) will still cause a hard failure
regardless of this option.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

Expand Down
9 changes: 9 additions & 0 deletions tests/test-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -689,4 +689,13 @@ else
fi
fi

# Smoke-test --not-a-security-boundary
#
# Setting up an unavailable automount and triggering the right conditions is
# complicated to do here, but we can at least check that the option is there,
# and that it stays there.

$RUN --not-a-security-boundary true
ok "Accepts --not-a-security-boundary"

done_testing
Loading