Bubblewrap/Examples

== Firefox ==
 set -u
If the shell tries to expand an unset parameter, it will error (with a
few exceptions).
 XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
 XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
Take value if already set, else fallback to the XDG default.
 NEW_HOME='/home/user'
User to appear as.
 NEW_XDG_CACHE_HOME="${NEW_HOME}${XDG_CACHE_HOME#"$HOME"}"
 for the new user.
 mkdir -pm 0700 "${XDG_DATA_HOME}/firefox/"
Make sure the new (real) home for Firefox data exist.
 --unshare-all \
Unshare all possible namespaces.
 --share-net \
Retain the network namespace.
 --new-session \
New terminal session for the sandbox.
 --die-with-parent \
Child process dies when parent dies.
 --clearenv \
Unset all environment variables (except for ).
 --setenv HOME "$NEW_HOME" \
Pass the path to for .
 --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \
Specify the Wayland display to run clients on.
 --setenv XDG_CACHE_HOME "$NEW_XDG_CACHE_HOME" \
User-specific non-essential (cached) data.
 --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR" \
User-specific non-essential runtime files and other file objects.
 --hostname localhost \
Use custom hostname in the sandbox.
 --dev /dev/ \
New devtmpfs, access to special or device files.
 --ro-bind /etc/fonts/ /etc/fonts/ \
System font configuration directory.
 --ro-bind /etc/resolv.conf /etc/resolv.conf \
Needed for DNS resolution.
 --bind-try "${XDG_CACHE_HOME}/mozilla/" "${NEW_XDG_CACHE_HOME}/mozilla/" \
Per-user Mozilla cache.
 ...
 XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
 XDG_CONFIG_HOME "${XDG_CONFIG_HOME:-$HOME/.config}"
 XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
 ...
 NEW_XDG_CACHE_HOME="${NEW_HOME}${XDG_CACHE_HOME#"$HOME"}"
 NEW_XDG_CONFIG_HOME="${NEW_HOME}${XDG_CONFIG_HOME#"$HOME"}"
 
 ...
 --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \
 --setenv XDG_CONFIG_HOME "$NEW_XDG_CONFIG_HOME" \
 --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR" \
 ...
 --bind-try "${XDG_CACHE_HOME}/mozilla/" "${NEW_XDG_CACHE_HOME}/mozilla/" \
 --ro-bind-try "${XDG_CONFIG_HOME}/fontconfig/" "${NEW_XDG_CONFIG_HOME}/fontconfig/" \
 --bind "${XDG_DATA_HOME}/firefox/" "${NEW_HOME}/.mozilla/" \
 ...
(Optional) Per-user font configuration directory.
 ...
 XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
 XDG_CONFIG_HOME "${XDG_CONFIG_HOME:-$HOME/.config}"
 XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
 ...
 NEW_XDG_CACHE_HOME="${NEW_HOME}${XDG_CACHE_HOME#"$HOME"}"
 NEW_XDG_CONFIG_HOME="${NEW_HOME}${XDG_CONFIG_HOME#"$HOME"}"
 
 ...
 --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \
 --setenv XDG_CONFIG_HOME "$NEW_XDG_CONFIG_HOME" \
 --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR" \
 ...
 --bind-try "${XDG_CACHE_HOME}/mozilla/" "${XDG_CACHE_HOME}/mozilla/" \
 --ro-bind-try "${XDG_CONFIG_HOME}/user-dirs.dirs" "${NEW_XDG_CONFIG_HOME}/user-dirs.dirs" \
 --bind "${XDG_DATA_HOME}/firefox/" "${NEW_HOME}/.mozilla/" \
 ...
(Optional) If you modify "well known" user directories, like
, you need this to have Firefox pick it up.
 --bind "${XDG_DATA_HOME}/firefox/" "${NEW_HOME}/.mozilla/" \
 is the location of Firefox data. Shown to Firefox as .
 ...
 NEW_XDG_CACHE_HOME="${NEW_HOME}${XDG_CACHE_HOME#"$HOME"}"
 NEW_XDG_DATA_HOME="${NEW_HOME}${XDG_DATA_HOME#"$HOME"}"
 
 ...
 --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \
 --setenv XDG_DATA_HOME "$NEW_XDG_DATA_HOME" \
 --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR" \
 ...
 --bind "${XDG_DATA_HOME}/firefox/" "${NEW_HOME}/.mozilla/" \
 --ro-bind-try "${XDG_DATA_HOME}/fonts/" "${NEW_XDG_DATA_HOME}/fonts/" \
 --bind-try "${HOME}/Downloads/" "${NEW_HOME}/Downloads/" \
 ...
(Optional) Per-user directory scanned for font files.
 --bind-try "${HOME}/Downloads/" "${NEW_HOME}/Downloads/" \
Default directory.
 --ro-bind /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1 \
 --ro-bind /lib/libblkid.so.1 /lib/libblkid.so.1 \
 --ro-bind /lib/libcrypto.so.3 /lib/libcrypto.so.3 \
 --ro-bind /lib/libmount.so.1 /lib/libmount.so.1 \
 --ro-bind /lib/libz.so.1 /lib/libz.so.1 \
Shared libraries.
 --proc /proc/ \
New procfs, provides information about running processes and the kernel.
 --ro-bind "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" \
Bind the Wayland socket file.
 --ro-bind /usr/lib/ /usr/lib/ \
Object files and libraries.
 --ro-bind /usr/share/X11/xkb/ /usr/share/X11/xkb/ \
XKB is a keyboard keymap support library.
 --ro-bind /usr/share/fontconfig/ /usr/share/fontconfig/ \
Font presets.
 --ro-bind /usr/share/fonts/ /usr/share/fonts/ \
Global directory scanned for font files.
 --ro-bind /usr/share/glib-2.0/ /usr/share/glib-2.0/ \
Needed for "Save Page As…", "Export|Import Bookmarks File", among others.
 --ro-bind /usr/share/icons/ /usr/share/icons/ \
Global icons directory.
 --ro-bind /usr/share/icu/ /usr/share/icu/ \
International Components for Unicode (ICU) provides support for Unicode
and globalization.
 ...
 --ro-bind /usr/share/icu/ /usr/share/icu/ \
 --ro-bind /usr/share/libdrm/ /usr/share/libdrm/ \
 --ro-bind /usr/share/mime/ /usr/share/mime/ \
 ...
(Optional) Direct Rendering Manager (DRM), Linux kernel subsystem for
interfacing with GPUs of video cards. Programs can use this to have the
GPU do hardware-accelerated 3D rendering and video decoding.
 --ro-bind /usr/share/mime/ /usr/share/mime/ \
Global XDG MIME directory.
 /usr/lib/firefox/firefox
Call Firefox.
=== PipeWire audio ===
=== Pulse audio ===
 ...
 --proc /proc/ \
 --ro-bind "${XDG_RUNTIME_DIR}/pulse/" "${XDG_RUNTIME_DIR}/pulse/" \
 --ro-bind "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" \
 ...
(Optional) Pulse audio.
=== Optional(?) stuff ===
 ...
 --proc /proc/ \
 --ro-bind /sys/bus/pci/ /sys/bus/pci/ \
 --ro-bind "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" \
 ...
Information about PCI bus type.
Without this you get
 Crash Annotation GraphicsCriticalError: |[0][GFX1-]: glxtest: cannot access /sys/bus/pci (t=0.177033) [GFX1-]: glxtest: cannot access /sys/bus/pci
but it still seems to work.
 ...
 --proc /proc/ \
 --ro-bind /sys/devices/pci0000:00/ /sys/devices/pci0000:00/ \
 --ro-bind "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" \
 ...
Contains a filesystem representation of the kernel device tree.
With but without this you get:
 Crash Annotation GraphicsCriticalError: |[0][GFX1-]: glxtest: ManageChildProcess failed
 (t=0.189558) [GFX1-]: glxtest: ManageChildProcess failed
 Crash Annotation GraphicsCriticalError: |[0][GFX1-]: glxtest: ManageChildProcess failed
 (t=0.189558) |[1][GFX1-]: No GPUs detected via PCI
 (t=0.18958) [GFX1-]: No GPUs detected via PCI
but it still seems to work.
== imv ==
 --ro-bind /bin/sh /bin/sh \
Needed to use and have various information in the window
title.
 ...
 
 XDG_CACHE_HOME"${XDG_CACHE_HOME:-$HOME/.cache}"
 XDG_CONFIG_HOME"${XDG_CONFIG_HOME:-$HOME/.config}"
 XDG_DATA_HOME"${XDG_DATA_HOME:-$HOME/.local/share}"
 
 ...
 --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \
 --setenv XDG_CACHE_HOME "$XDG_CACHE_HOME" \
 --setenv XDG_CONFIG_HOME "$XDG_CONFIG_HOME" \
 --setenv XDG_DATA_HOME "$XDG_DATA_HOME" \
 --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR" \
 ...
 --dev-bind /dev/dri/renderD128 /dev/dri/renderD128 \
 --ro-bind /etc/fonts/ /etc/fonts/ \
 --bind-try "${XDG_CACHE_HOME}/fontconfig/" "${XDG_CACHE_HOME}/fontconfig/" \
 --ro-bind-try "${XDG_CONFIG_HOME}/fontconfig/" "${XDG_CONFIG_HOME}/fontconfig/" \
 --ro-bind-try "${XDG_CONFIG_HOME}/imv/config" "${XDG_CONFIG_HOME}/imv/config" \
 --ro-bind-try "${XDG_DATA_HOME}/fonts/" "${XDG_DATA_HOME}/fonts/" \
 --ro-bind /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1 \
 ...
 --ro-bind /usr/share/X11/xkb/ /usr/share/X11/xkb/ \
 --ro-bind /usr/share/fonts/ /usr/share/fonts/ \
 --ro-bind /usr/share/icu/ /usr/share/icu/ \
 --ro-bind "${1:-./}" "$(realpath "${1:-./}")" \
 ...
(Optional) To use commands in .
 --ro-bind /sys/dev/char/ /sys/dev/char/ \
Access to character devices.
 --ro-bind /sys/devices/pci0000:00/ /sys/devices/pci0000:00/ \
Access to PCI resources.
 --ro-bind "${1:-./}" "$(realpath "${1:-./}")" \
Get the absolute pathname using , so you can pass a
relative argument and still bind the argument. If you don't pass in
anything it will default to , the current directory.
== KeePassXC ==
 PASSWORD_DATABASE="${HOME}/password database"
Directory containing your password database.
 --setenv XDG_SESSION_TYPE "$XDG_SESSION_TYPE" \
Session type, since we are assuming Wayland only, this will be "wayland".
 --ro-bind /sys/dev/char/ /sys/dev/char/ \
Access to character devices.
 --ro-bind /sys/devices/pci0000:00/ /sys/devices/pci0000:00/ \
Access to PCI resources.
 --bind "$PASSWORD_DATABASE/" "$NEW_PASSWORD_DATABASE/" \
Bind password database.
== mpv ==
 --ro-bind /sys/dev/char/ /sys/dev/char/ \
Access to character devices.
 --ro-bind /sys/devices/pci0000:00/ /sys/devices/pci0000:00/ \
Access to PCI resources.
 --ro-bind "$1" "$(realpath "$1")" \
Get the absolute pathname using , so you can pass a
relative argument and still bind the argument.
 --player-operation-mode=pseudo-gui \
Because is used, this is needed to have a way to
control when it would otherwise not show a GUI.
 --title='bwrap | ${media-title}' \
Set the window title, showing at a glance that you are using .
=== mpv-net ===
If you want to use to stream over the Internet, you will need
a few things more.
 --setenv PATH /usr/bin/ \
 --ro-bind /etc/ssl/certs/ /etc/ssl/certs/ \
Certificate authorities that are trusted.
 --ro-bind /sys/dev/char/ /sys/dev/char/ \
Access to character devices.
 --ro-bind /sys/devices/pci0000:00/ /sys/devices/pci0000:00/ \
Access to PCI resources.
 "$@"
This is so you can pass more options to , for example:
 $ bwrap-mpv-net --video=no --sub=no 'URL1' 'URL2'
=== Pulse audio ===
 --ro-bind "${XDG_RUNTIME_DIR}/pulse" "${XDG_RUNTIME_DIR}/pulse" \
Pulse audio.
=== Screenshots ===
The default screenshots directory is (which is
, which will fallback to if
 doesn't exist). That means that by default
 won't allow screenshots unless you change a few things.
Do the following to allow an XDG approved screenshots directory:
 ...
 XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
 XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
 
 ...
 NEW_XDG_CONFIG_HOME="${NEW_HOME}${XDG_CONFIG_HOME#"$HOME"}"
 NEW_XDG_DATA_HOME="${NEW_HOME}${XDG_DATA_HOME#"$HOME"}"
 
 mkdir -pm 0700 "${XDG_DATA_HOME}/mpv/"
 
 ...
 --setenv XDG_CONFIG_HOME "$NEW_XDG_CONFIG_HOME" \
 --setenv XDG_DATA_HOME "$NEW_XDG_DATA_HOME" \
 --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR" \
 ...
 --ro-bind-try "${XDG_CONFIG_HOME}/mpv/mpv.conf" "${NEW_XDG_CONFIG_HOME}/mpv/mpv.conf" \
 --bind "${XDG_DATA_HOME}/mpv/" "${NEW_XDG_DATA_HOME}/mpv/" \
 --ro-bind /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1 \
 ...
Now make use that directory:
== yt-dlp ==
 --bind "${HOME}/Downloads/" "${NEW_HOME}/Downloads/" \
Directory for writing the files. This should match that
is either in or passed on
the command line.
 /usr/bin/yt-dlp "$@"
This is so you can pass more options to , for example:
 $ bwrap-yt-dlp --no-playlist 'URL1' 'URL2'
== zathura ==
 mkdir -pm 0700 "${XDG_DATA_HOME}/zathura/"
Have to premake the directory for data.
 --bind "${XDG_DATA_HOME}/zathura/" "${XDG_DATA_HOME}/zathura/" \
Allow writing of: bookmarks, history, input history.
 --ro-bind /usr/share/misc/magic.mgc /usr/share/misc/magic.mgc \
Used for identifying what type a file should be. Read the
 man page for more information.
 --ro-bind "$1" "$(realpath "$1")" \
Get the absolute pathname using , so you can pass a
relative argument and still bind the argument.
Category:Security