From f3719dae383da0571610434d80b451a05a6fac39 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 19 May 2026 11:11:45 +0200 Subject: [PATCH 1/2] bubblewrap: Rework how flags are passed to setup_op_bind_mount() Rework how flags are passed to setup_op_bind_mount() when calling it in setup_newroot(); no functional changes are made, this is just in preparation for adding add more flags in future commits and still keep the code readable. Signed-off-by: Antonio Ospite --- bubblewrap.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/bubblewrap.c b/bubblewrap.c index 2fb926c4..ed028be9 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -1003,9 +1003,15 @@ 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 (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) { From d983f7791392fcb45802e17e05fe7d67dbbca89f Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 28 Apr 2026 17:46:27 +0000 Subject: [PATCH 2/2] bubblewrap: Add `--not-a-security-boundary` flag to enable fail-open behavior Some callers of bwrap (e.g. xdg-dbus-proxy, Steam Runtime) use it purely to adjust filesystem layout, without any expectation of a security boundary between the sandbox and the host. For these callers, hard failures during sandbox setup (such as an automount timeout on a bind source) are unnecessarily fatal. So add a new `--not-a-security-boundary` option that can be used to relax the bubblewrap behavior in these specific cases, and allow it to "fail-open". The behavior can be tested by setting up an unavailable automount and check that: 1. bubblewrap succeeds even when accessing the unavailable automount fails; 2. remount flags like `nosuid,nodev` are not applied to the unavailable automount. ``` $ sudo sh -c 'echo "UUID=00000000-0000-0000-0000-000000000000 /mnt/bad ext4 defaults,noatime,nofail,inode_readahead_blks=64,nobarrier,x-systemd.automount 0 1" >> /etc/fstab' $ sudo systemctl daemon-reload $ sudo systemctl restart mnt-bad.automount $ mount | grep /mnt/bad systemd-1 on /mnt/bad type autofs (rw,relatime,fd=76,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=15854) $ ls /mnt/bad &>/dev/null & $ ./_build/bwrap --not-a-security-boundary --bind / / grep /mnt /proc/self/mountinfo bwrap: Can't remount /newroot/mnt/bad submount (No such device), ignoring error 860 769 0:41 / /mnt/bad rw,relatime master:45 - autofs systemd-1 rw,fd=107,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=144522 ``` steamrt/tasks#535 Resolves: https://github.com/containers/bubblewrap/issues/653 Helps: https://github.com/flatpak/flatpak/issues/5112 Helps: https://github.com/ValveSoftware/steam-for-linux/issues/10571 Helps: https://github.com/ValveSoftware/steam-runtime/issues/766 Signed-off-by: Antonio Ospite --- bind-mount.c | 9 +++++++++ bind-mount.h | 1 + bubblewrap.c | 10 ++++++++++ bwrap.xml | 27 +++++++++++++++++++++++++++ tests/test-run.sh | 9 +++++++++ 5 files changed, 56 insertions(+) diff --git a/bind-mount.c b/bind-mount.c index a2e1ac6c..a7a729dd 100644 --- a/bind-mount.c +++ b/bind-mount.c @@ -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) + { + 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); diff --git a/bind-mount.h b/bind-mount.h index 8a361fbd..8f2d5108 100644 --- a/bind-mount.h +++ b/bind-mount.h @@ -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 diff --git a/bubblewrap.c b/bubblewrap.c index ed028be9..3b9719c4 100644 --- a/bubblewrap.c +++ b/bubblewrap.c @@ -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) @@ -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); } @@ -1005,6 +1008,9 @@ setup_newroot (bool unshare_pid) 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; @@ -2433,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; diff --git a/bwrap.xml b/bwrap.xml index 61fb0445..ea9d118d 100644 --- a/bwrap.xml +++ b/bwrap.xml @@ -614,6 +614,33 @@ command line. Please be careful to the order they are specified. + + + + + Declare that this invocation of bwrap 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, + rather than causing bwrap to exit with an error. + The effect of this option might be extended to make other sandbox + setup operations non-fatal in future releases of bubblewrap. + + + This option is intended for callers such as + xdg-dbus-proxy or Steam that use + bwrap to adjust the filesystem layout for a + process, but do not rely on it to create a security boundary. + + + Other operations that are fundamental to establishing the sandbox + (creating namespaces, pivot_root, + dropping capabilities) will still cause a hard failure + regardless of this option. + + + diff --git a/tests/test-run.sh b/tests/test-run.sh index 1003e8c4..2d0d307d 100755 --- a/tests/test-run.sh +++ b/tests/test-run.sh @@ -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