< Bubblewrap

Bubblewrap/Examples

Usage examples

dhcpcd

Create a simple dhcpcd sandbox:

  • Determine available kernel namespaces
$ ls /proc/self/ns 
cgroup  ipc  mnt  net  pid  uts
Note: The absence of user indicates that the kernel has been built with CONFIG_USER_NS=n or is user namespace restricted.
  • Bind as read-write the entire host / directory to / in the sandbox
  • Mount a new devtmpfs filesystem to /dev in the sandbox
  • Create new IPC and control group namespaces
  • Create a new UTS namespace and set dhcpcd as the hostname
# /usr/bin/bwrap --bind / / --dev /dev --unshare-ipc --unshare-cgroup --unshare-uts --hostname dhcpcd /usr/bin/dhcpcd -q -b

Unbound

Create a more granular and complex Unbound sandbox:

  • Bind as read-only the system /usr directory to /usr in the sandbox
  • Create a symbolic link from the system /usr/lib directory to /lib64 in the sandbox
  • Bind as read-only the system /etc directory to /etc in the sandbox
  • Create empty /var and /run directories within the sandbox
  • Mount a new devtmpfs filesystem to /dev in the sandbox
  • Create new IPC and PID and control group namespaces
  • Create a new UTS namespace and set unbound as the hostname
# /usr/bin/bwrap --ro-bind /usr /usr --symlink usr/lib /lib64 --ro-bind /etc /etc --dir /var --dir /run --dev /dev --unshare-ipc --unshare-pid --unshare-cgroup --unshare-uts --hostname unbound /usr/bin/unbound -d

Desktop

Leverage Bubblewrap within desktop entries:

  • Bind as read-write the entire host / directory to / in the sandbox
  • Re-bind as read-only the /var and /etc directories in the sandbox
  • Mount a new devtmpfs filesystem to /dev in the sandbox
  • Create a tmpfs filesystem over the sandboxed /run directory
  • Disable network access by creating new network namespace
[Desktop Entry]
Name=nano Editor
Exec=bwrap --bind / / --dev /dev --tmpfs /run --unshare-net  st -e nano -o . %f
Type=Application
MimeType=text/plain;
  • Example MuPDF desktop entry incorporating a shell wrapper:
[Desktop Entry]
Name=MuPDF
Exec=mupdf.sh %f
Icon=application-pdf.svg
Type=Application
MimeType=application/pdf;application/x-pdf;

MuPDF

The power and flexibility of bwrap is best revealed when used to create an environment within a shell wrapper:

  • Bind as read-only the host /usr/bin directory to /usr/bin in the sandbox
  • Bind as read-only the host /usr/lib directory to /usr/lib in the sandbox
  • Create a symbolic link from the system /usr/lib directory to /lib64 in the sandbox
  • Create a tmpfs filesystem overlaying /usr/lib/gcc in the sandbox
    • This effectively blacklists the contents of /usr/lib/gcc from appearing in the sandbox
  • Create a new tmpfs filesystem as the directory in the sandbox
  • Bind as read-only an file and Documents directory into the sandbox
    • This effectively whitelists the file and Documents directory with recursion
  • Create a new tmpfs filesystem as the directory in the sandbox
  • Whitelist the X11 socket by binding it into the sandbox as read-only
  • Clone and create private containers for all namespaces supported by the running kernel
    • If the kernel does not support non-privileged user namespaces, skip its creation and continue
  • Do not place network components into a private namespace
    • This allows for network access to follow URI hyperlinks
#!/bin/sh
#~/bwrap/mupdf.sh
(exec bwrap \
--ro-bind /usr/bin /usr/bin \
--ro-bind /usr/lib /usr/lib \
--symlink usr/lib /lib64 \
--tmpfs /usr/lib/gcc \
--tmpfs $HOME \
--ro-bind $HOME/.Xauthority $HOME/.Xauthority \
--ro-bind $HOME/Documents $HOME/Documents \
--tmpfs /tmp \
--ro-bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X0 \
 --unshare-all \
--share-net \
/usr/bin/mupdf "$@")
$ bwrap \
--ro-bind /usr/bin /usr/bin \
--ro-bind /usr/lib /usr/lib \
--symlink usr/lib /lib64 \
--tmpfs /usr/lib/gcc \
--tmpfs $HOME \
--ro-bind $HOME/.Xauthority $HOME/.Xauthority \
--ro-bind $HOME/Desktop $HOME/Desktop \
--tmpfs /tmp \
--ro-bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X0 \
--unshare-all \
--share-net \
 /usr/bin/sh
bash-4.4$ ls -AF
.Xauthority  Documents/

Perhaps the most important rule to consider when building a bubblewrapped filesystem is that commands are executed in the order they appear. From the MuPDF example above:

  • A tmpfs system is created followed by the bind mounting of an file and a Documents directory:
--tmpfs $HOME \
--ro-bind $HOME/.Xauthority $HOME/.Xauthority \
--ro-bind $HOME/Documents $HOME/Documents \
bash-4.4$ ls -a
.  ..  .Xauthority  Desktop
  • A tmpfs filesystem is created after the bind mounting of and overlays it so that only the Documents directory is visible within the sandbox:
--ro-bind $HOME/.Xauthority $HOME/.Xauthority \
--tmpfs $HOME \
--ro-bind $HOME/Desktop $HOME/Desktop \
bash-4.4$ ls -a
.  ..  Desktop

p7zip

Applications which have not yet been patched against known vulnerabilities constitute prime candidates for bubblewrapping:

  • Bind as read-only the host executable path to the sandbox
  • Create a symbolic link from the system /usr/lib directory to /lib64 in the sandbox
  • Blacklist the sandboxed contents of and with tmpfs overlays
  • Mount a new devtmpfs filesystem to /dev in the sandbox
  • Bind as read-write the host directory to the directory in the sandbox
    • 7za will only run in the host directory and/or its subdirectories when called from the shell wrapper
  • Create new cgroup/IPC/network/PID/UTS namespaces for the application and its processes
    • If the kernel does not support non-privileged user namespaces, skip its creation and continue
    • Creation of a new network namespace prevents the sandbox from obtaining network access
  • Add a custom or an arbitrary hostname to the sandbox such as p7zip
  • Unset the XAUTHORITY environment variable to hide the location of the X11 connection cookie
    • 7za does not need to connect to an X11 display server to function properly
  • Start a new terminal session to prevent keyboard input from escaping the sandbox
#!/bin/sh
#~/bwrap/pz7ip.sh
(exec bwrap \
--ro-bind /usr/bin/7za /usr/bin/7za \
--symlink usr/lib /lib64 \
--tmpfs /usr/lib/modules \
--tmpfs /usr/lib/systemd \
--dev /dev \
--bind /sandbox /sandbox \
--unshare-all \
--hostname p7zip \
--unsetenv XAUTHORITY \
--new-session \
/usr/bin/7za "$@")
bwrap \
--ro-bind /usr/bin/7za /usr/bin/7za \
--ro-bind /usr/bin/ls /usr/bin/ls \
--ro-bind /usr/bin/sh /usr/bin/sh \
--symlink usr/lib /lib64 \
--tmpfs /usr/lib/modules \
--tmpfs /usr/lib/systemd \
--dev /dev \
--bind /sandbox /sandbox \
--unshare-all \
--hostname p7zip \
--unsetenv XAUTHORITY \
--new-session \
/usr/bin/sh
bash: no job control in this shell
bash-4.4$ ls -AF         
dev/  lib64@  usr/
bash-4.4$ ls -l /usr/lib/modules 
total 0
bash-4.4$ ls -l /usr/lib/systemd
total 0
bash-4.4$ ls -AF /dev
console  full  null  ptmx@  pts/  random  shm/  stderr@  stdin@  stdout@  tty  urandom  zero
bash-4.4$ ls -A /usr/bin
7za  ls  sh

Firefox

Network facing applications with large surface attack areas are also ideal candidates to be bubblewrapped:

  • Transmission included in the sandbox to launch with magnet and torrent links
  • Example wrap supports audio (PulseAudio) and printing (CUPS/Avahi) under GNOME (Wayland)
    • Paths in should reflect the variable
  • Full paths are used to allow for keyboard bindings in environments which do not support variable expansion.
  • WebRenderer and hardware (accelerated) compositing support included
 bwrap \
--symlink usr/lib /lib \
--symlink usr/lib64 /lib64 \
--symlink usr/bin /bin \
--symlink usr/bin /sbin \
--ro-bind /usr/lib /usr/lib \
--ro-bind /usr/lib64 /usr/lib64 \
--ro-bind /usr/bin /usr/bin \
--ro-bind /usr/lib/firefox /usr/lib/firefox \
--ro-bind /usr/share/applications /usr/share/applications \
--ro-bind /usr/share/gtk-3.0 /usr/share/gtk-3.0 \
--ro-bind /usr/share/fontconfig /usr/share/fontconfig \
--ro-bind /usr/share/icu /usr/share/icu \
--ro-bind /usr/share/drirc.d /usr/share/drirc.d \
--ro-bind /usr/share/fonts /usr/share/fonts \
--ro-bind /usr/share/glib-2.0 /usr/share/glib-2.0 \
--ro-bind /usr/share/glvnd /usr/share/glvnd \
--ro-bind /usr/share/icons /usr/share/icons \
--ro-bind /usr/share/libdrm /usr/share/libdrm \
--ro-bind /usr/share/mime /usr/share/mime \
--ro-bind /usr/share/X11/xkb /usr/share/X11/xkb \
--ro-bind /usr/share/icons /usr/share/icons \
--ro-bind /usr/share/mime /usr/share/mime \
--ro-bind /etc/fonts /etc/fonts \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--ro-bind /usr/share/ca-certificates /usr/share/ca-certificates \
--ro-bind /etc/ssl /etc/ssl \
--ro-bind /etc/ca-certificates /etc/ca-certificates \
--dir /run/user/"$(id -u)" \
--ro-bind /run/user/"$(id -u)"/pulse /run/user/"$(id -u)"/pulse \
--ro-bind /run/user/"$(id -u)"/wayland-1 /run/user/"$(id -u)"/wayland-1 \
--dev /dev \
--dev-bind /dev/dri /dev/dri \
--ro-bind /sys/dev/char /sys/dev/char \
--ro-bind /sys/devices/pci0000:00 /sys/devices/pci0000:00 \
--proc /proc \
--tmpfs /tmp \
--bind /home/example/.mozilla /home/example/.mozilla \
--bind /home/example/.config/transmission /home/example/.config/transmission \
--bind /home/example/Downloads /home/example/Downloads \
--setenv HOME /home/example \
--setenv GTK_THEME Adwaita:dark \
--setenv MOZ_ENABLE_WAYLAND 1 \
--setenv PATH /usr/bin \
--hostname RESTRICTED \
--unshare-all \
--share-net \
--die-with-parent \
--new-session \
/usr/bin/firefox

Enhancing privacy

  • Further restrictions can be made by removing specific entries
    • Remove the following entry to remove audio support:
--ro-bind /run/user/"$(id -u)"/pulse /run/user/"$(id -u)"/pulse \
  • represents an arbitrary location defined by the user to hold desired profile information
    • This allows for the use of a sanitized profile copied into via a script/cron job or manually e.g.
$ cp -pR ~/.mozilla /sandbox/

The location can be a network share, a USB mount, or a local filesystem or ramfs/tmpfs location

  • Set to obscure the actual
  • Set new user ID and group ID values
bwrap \
....
--bind /sandbox/.mozilla /home/r/.mozilla \
--bind /sandbox/Downloads /home/r/Downloads \
...
--setenv HOME /home/r \
...
--uid 200 --gid 400 \
...
/usr/bin/firefox --no-remote --private-window

Chromium

A simple chromium sandbox on wayland and with pipewire:

  • PipeWire:
    • If you are not using pipewire, feel free to remove this line
  • --bind $HOME/.config/chromium $HOME/.config/chromium \ mounts your chromium configuration directory in the sandbox as readable and writable
  • --bind $HOME/Downloads $HOME/Downloads \ mounts your ~/Downloads directory in the sandbox as readable and writable
  • This example can be further improved for more isolation.

Skype for Linux

should be started with  instead of , because the latter is just a wrapper script which forks the main process in the background and terminates, which conflicts with the  bwrap option.

The following example provides these features:

  • ensures that all environment variables are unset.
  • Network is shared with the host (), is bind-mounted.
  • Xorg access: bind the socket, set $DISPLAY.
  • D-Bus: bind the socket, set $DBUS_SESSION_BUS_ADDRESS.
  • Audio: bind the PulseAudio socket.
  • Video: dev-bind the device.

The directory on the host where you want to keep the Skype profile can be configured with .

Filesystem isolation

To further hide the contents of the file system (such as those in /var, /usr/bin and /usr/lib) and to sandbox even the installation of software, pacman can be made to install Arch packages into isolated filesystem trees.

In order to use pacman for installing software into the filesystem trees, you will need to install and .

Suppose you want to install the package with pacman into an isolated filesystem tree. You should prepare your tree like this:

$ MYPACKAGE=xterm
$ mkdir -p ~/sandboxes/${MYPACKAGE}/files/var/lib/pacman
$ mkdir -p ~/sandboxes/${MYPACKAGE}/files/etc
$ cp /etc/pacman.conf ~/sandboxes/${MYPACKAGE}/files/etc/pacman.conf

You may want to edit ~/sandboxes/${MYPACKAGE}/files/etc/pacman.conf and adjust the pacman configuration used:

  • Remove any undesired custom repositories and , , and settings that are needed only for the host system.
  • You may need to remove the option so pacman will not complain about errors finding the root filesystem for checking disk space.

Then install the base group along with the needed fakeroot into the isolated filesystem tree:

$ fakechroot fakeroot pacman -Syu \
    --root ~/sandboxes/${MYPACKAGE}/files \
    --dbpath ~/sandboxes/${MYPACKAGE}/files/var/lib/pacman \
    --config ~/sandboxes/${MYPACKAGE}/files/etc/pacman.conf \
    base fakeroot

Since you will be repeatedly calling bubblewrap with the same options, make an alias:

$ alias bw-install='bwrap                        \
     --bind ~/sandboxes/${MYPACKAGE}/files/ /    \
     --ro-bind /etc/resolv.conf /etc/resolv.conf \
     --tmpfs /tmp                                \
     --proc /proc                                \
     --dev /dev                                  \
     --chdir /                                   '

You will need to set up the locales by editing and running:

$ bw-install locale-gen

Then set up pacman’s keyring:

$ bw-install fakeroot pacman-key --init
$ bw-install fakeroot pacman-key --populate

Now you can install the desired package.

$ bw-install fakeroot pacman -S ${MYPACKAGE}

If the pacman command fails here, try running the command for populating the keyring again.

Congratulations. You now have an isolated filesystem tree containing . You can use again to upgrade your filesystem tree.

You can now run your software with bubblewrap. should be in this case.

$ bwrap                                          \
     --ro-bind ~/sandboxes/${MYPACKAGE}/files/ / \
     --ro-bind /etc/resolv.conf /etc/resolv.conf \
     --tmpfs /tmp                                \
     --proc /proc                                \
     --dev /dev                                  \
     --chdir /                                   \
     command

Note that some files can be shared between packages. You can hardlink to all files of an existing parent filesystem tree to reuse them in a new tree:

$ cp -al ~/sandboxes/${MYPARENTPACKAGE} ~/sandboxes/${MYPACKAGE}

Then proceed with the installation as usual by calling pacman from bw-install fakechroot fakeroot pacman ….

This article is issued from Archlinux. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.