<!--
SPDX-FileCopyrightText: (c) 2026 Daniel Hast

SPDX-License-Identifier: Apache-2.0 OR MIT
-->

# bwrap-restricted

**bwrap-restricted** exposes a configurable, restricted subset of the interface
to Linux namespace functionality provided by [bubblewrap].

`bwrap-restricted` acts as a wrapper around the `bwrap` executable; it parses
the arguments to `bwrap`, scans them for forbidden options, and executes `bwrap`
with the given arguments only if no forbidden options are present. Additionally,
extra options can be inserted between the options and the command arguments.

[bubblewrap]: https://github.com/containers/bubblewrap

## Configuration

The configuration is read from a TOML file located at
`/etc/bwrap-restricted/config.toml`. There are three valid keys:

- `bwrap_path`: a string with the absolute path to the `bwrap` executable.
  Defaults to `/usr/bin/bwrap` if omitted.
- `forbidden_options`: a list of `bwrap` options that are forbidden and will
  cause `bwrap-restricted` to exit with an error if present. Defaults to an
  empty list if omitted.
- `extra_options`: a list of extra arguments that are inserted after the `bwrap`
  options and before the command arguments. Defaults to an empty list if omitted.

## Example

The following configuration file denies the ability to add capabilities in the
user namespace, requires sandbox setup failures to be treated as hard failures,
forces all existing capabilities to be dropped, makes the sandbox start in a
new terminal session, and requires that nested user namespaces be disallowed:

```toml
forbidden_options = ["--cap-add", "--not-a-security-boundary"]
extra_options = ["--cap-drop", "ALL", "--new-session", "--assert-userns-disabled"]
```

Here are some examples with the above configuration:

```sh
# Running this command:
bwrap-restricted --ro-bind /usr /usr -- foo bar
# is equivalent to running:
/usr/bin/bwrap --ro-bind /usr /usr --cap-drop ALL --new-session --assert-userns-disabled -- foo bar

# This command is rejected with an error message:
bwrap-restricted --cap-add CAP_SYS_ADMIN -- foo bar

# --cap-drop is allowed, and the extra options are added afterward regardless:
bwrap-restricted --cap-drop CAP_SYS_ADMIN -- foo bar
# is equivalent to running:
/usr/bin/bwrap --cap-drop CAP_SYS_ADMIN --cap-drop ALL --new-session --assert-userns-disabled -- foo bar

# File descriptors passed to --args are scanned as well:
bwrap-restricted --args 4 foo bar
# will be rejected with an error message if the null-separated arguments in the
# file descriptor include a forbidden option. Otherwise, it's equivalent to:
/usr/bin/bwrap --args 4 --cap-drop ALL --new-session --assert-userns-disabled foo bar
```

## Usage

If the system administrator wants to expose a restricted subset of user
namespace functionality to a given user or process, they can prevent directly
executing `bwrap` while allowing it to be executed via `bwrap-restricted`. For
example, on a system with SELinux, `bwrap-restricted` could be run in an SELinux
domain that's allowed to create user namespaces, while other domains are denied
permission to create user namespaces.

## Installation

RPM builds are available in a [Copr repo]. This includes the main
`bwrap-restricted` package, a `bwrap-restricted-selinux` subpackage with an
SELinux policy module, and a `bwrap-restricted-shim` subpackage that makes
`/usr/bin/bwrap` into a symlink to `/usr/bin/bwrap-restricted` (to allow it to
be used in place of bubblewrap by programs that aren't aware of it).

[Copr repo]: https://copr.fedorainfracloud.org/coprs/amaranth/bwrap-restricted/

Alternatively, bwrap-restricted can be installed manually using Meson:

1. Clone this git repository.
2. Run `meson setup build -Dprefix=/usr` and `meson compile -C build` from the
   repository root.
3. Install the files by running `meson install -C build` as root.
4. If your system uses Fedora's SELinux policy, run the following command as
   root to install the SELinux policy module:
```
semodule -i /usr/share/selinux/packages/targeted/bwrap-restricted.pp.bz2
```
