Bubblewrap/Examples

From Alpine Linux
Revision as of 00:09, 6 September 2023 by Encode (talk | contribs) (imv: remove note about needing to pass the source for 'config' if it's a symbolic link. That does not appear to be needed.)
This material is work-in-progress ...

Someone more experienced needs to look over this.
(Last edited by Encode on 6 Sep 2023.)

Todo: Since bubblewrap can make use of seccomp, restrictive versions should be added.


Note: This page assumes you have already read over Bubblewrap. To try and avoid duplicates, everything will be explained for Firefox and only when it differs (non obviously) for everything else. Where applicable this also assumes: Wayland only + PipeWire.
Note: imv, mpv and zathura currently only accept 1 (mandatory, except imv) argument. This should (hopefully) be temporary, until I figure out how to pass multiple arguments (without including everything else).

Firefox

Contents of ~/.local/bin/bwrap-firefox

#!/usr/bin/env sh # Firefox wrapped in bwrap with network access. set -u XDG_CACHE_HOME="${XDG_CACHE_HOME:=$HOME/.cache}" XDG_DATA_HOME="${XDG_DATA_HOME:=$HOME/.local/share}" NEW_HOME='/home/user' NEW_XDG_CACHE_HOME="${NEW_HOME}${XDG_CACHE_HOME#"$HOME"}" mkdir -pm 0700 "${XDG_DATA_HOME}/firefox/" /usr/bin/bwrap \ --unshare-all \ --share-net \ --new-session \ --die-with-parent \ --clearenv \ --setenv HOME "$NEW_HOME" \ --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \ --setenv XDG_CACHE_HOME "$NEW_XDG_CACHE_HOME" \ --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR" \ --hostname localhost \ --dev /dev/ \ --ro-bind /etc/fonts/ /etc/fonts/ \ --ro-bind /etc/resolv.conf /etc/resolv.conf \ --bind-try "${XDG_CACHE_HOME}/mozilla/" "${NEW_XDG_CACHE_HOME}/mozilla/" \ --bind "${XDG_DATA_HOME}/firefox/" "${NEW_HOME}/.mozilla/" \ --bind-try "${HOME}/Downloads/" "${NEW_HOME}/Downloads/" \ --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/libmount.so.1 /lib/libmount.so.1 \ --ro-bind /lib/libz.so.1 /lib/libz.so.1 \ --proc /proc/ \ --ro-bind "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" \ --ro-bind /usr/lib/ /usr/lib/ \ --ro-bind /usr/share/X11/xkb/ /usr/share/X11/xkb/ \ --ro-bind /usr/share/fontconfig/ /usr/share/fontconfig/ \ --ro-bind /usr/share/fonts/ /usr/share/fonts/ \ --ro-bind /usr/share/glib-2.0/ /usr/share/glib-2.0/ \ --ro-bind /usr/share/icons/ /usr/share/icons/ \ --ro-bind /usr/share/icu/ /usr/share/icu/ \ --ro-bind /usr/share/mime/ /usr/share/mime/ \ /usr/lib/firefox/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"}"

"$XDG_CACHE_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 bwrap parent dies.

--clearenv \

Unset all environment variables (except for "$PWD").

--setenv HOME "$NEW_HOME" \

Pass the path to "$NEW_HOME" for "$HOME".

--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 ~/Downloads/, you need this to have Firefox pick it up.

Note: If you use "${XDG_CONFIG_HOME}/user-dirs.dirs" you should also add the corresponding path(s).

For example if you set XDG_DOWNLOAD_DIR to "${HOME}/downloads/" you would also add:

 ...
 --bind "${XDG_DATA_HOME}/firefox/" "${NEW_HOME}/.mozilla/" \
 --bind-try "${HOME}/downloads/" "${NEW_HOME}/downloads/" \
 --ro-bind /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1 \
 ...
--bind "${XDG_DATA_HOME}/firefox/" "${NEW_HOME}/.mozilla/" \

"${XDG_DATA_HOME}/firefox/" is the location of Firefox data. Shown to Firefox as "${NEW_HOME}/.mozilla/".

Note: This has the added benefit of getting ~/.mozilla/ out of your "$HOME", and conforming more to XDG. This may one day not be necessary: Support for the Freedesktop.org XDG Base Directory Specification (2004-09-14).
...
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 ~/Downloads/ 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/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.

Note: It is not worth the time to limit this, the churn is too great.
--ro-bind /usr/share/X11/xkb/ /usr/share/X11/xkb/ \

XKB is a keyboard keymap support library.

Note: Even tho the path has */X11/* Wayland uses it too.
--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.

Tip: If you use multiple profiles you can have:
/usr/lib/firefox/firefox -P "$@"
this will allow you to pass a profile name and go into that specific one or not pass anything and get prompted for which to choose.

PipeWire audio

Todo:


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

This material is work-in-progress ...

Are these needed?
(Last edited by Encode on 6 Sep 2023.)

...
--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 --ro-bind /sys/bus/pci/ /sys/bus/pci/ \ 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

Contents of ~/.local/bin/bwrap-imv

#!/usr/bin/env sh # imv wrapped in bwrap. set -u XDG_CONFIG_HOME="${XDG_CONFIG_HOME:=$HOME/.config}" /usr/bin/bwrap \ --unshare-all \ --new-session \ --die-with-parent \ --clearenv \ --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \ --setenv XDG_CONFIG_HOME "$XDG_CONFIG_HOME" \ --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR" \ --ro-bind /bin/sh /bin/sh \ --dev-bind /dev/dri/renderD128 /dev/dri/renderD128 \ --ro-bind-try "${XDG_CONFIG_HOME}/imv/config" "${XDG_CONFIG_HOME}/imv/config" \ --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/libmount.so.1 /lib/libmount.so.1 \ --ro-bind /lib/libz.so.1 /lib/libz.so.1 \ --ro-bind /sys/dev/char/ /sys/dev/char/ \ --ro-bind /sys/devices/pci0000:00/ /sys/devices/pci0000:00/ \ --ro-bind "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" \ --ro-bind /usr/lib/ /usr/lib/ \ --ro-bind /usr/libexec/imv-wayland /usr/libexec/imv-wayland \ --ro-bind /usr/share/X11/xkb/ /usr/share/X11/xkb/ \ --ro-bind "${1:-./}" "$(realpath "${1:-./}")" \ /usr/libexec/imv-wayland "${1:-./}"
--ro-bind /bin/sh /bin/sh \

(Technically optional?) Needed to have the name of the file 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 imv.

--ro-bind "${1:-./}" "$(realpath "${1:-./}")" \

Get the absolute pathname using realpath, 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.

Warning: If you don't pass anything and it defaults to the current directory, it will have everything under that directory shown to imv, recursively.


mpv

Contents of ~/.local/bin/bwrap-mpv

#!/usr/bin/env sh # mpv wrapped in bwrap. set -u if [ "$#" != 1 ] then printf 'Run mpv wrapped in bwrap. Usage: $ bwrap-mpv VIDEO\n' exit 1 fi XDG_CACHE_HOME="${XDG_CACHE_HOME:=$HOME/.cache}" XDG_CONFIG_HOME="${XDG_CONFIG_HOME:=$HOME/.config}" NEW_HOME='/home/user' NEW_XDG_CACHE_HOME="${NEW_HOME}${XDG_CACHE_HOME#"$HOME"}" NEW_XDG_CONFIG_HOME="${NEW_HOME}${XDG_CONFIG_HOME#"$HOME"}" /usr/bin/bwrap \ --unshare-all \ --new-session \ --die-with-parent \ --clearenv \ --setenv HOME "$NEW_HOME" \ --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \ --setenv XDG_CACHE_HOME "$NEW_XDG_CACHE_HOME" \ --setenv XDG_CONFIG_HOME "$NEW_XDG_CONFIG_HOME" \ --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR" \ --hostname localhost \ --dev-bind /dev/dri/renderD128 /dev/dri/renderD128 \ --ro-bind /etc/fonts/ /etc/fonts/ \ --ro-bind /etc/mpv/ /etc/mpv/ \ --bind-try "${XDG_CACHE_HOME}/mesa_shader_cache/" "${NEW_XDG_CACHE_HOME}/mesa_shader_cache/" \ --ro-bind-try "${XDG_CONFIG_HOME}/mpv/mpv.conf" "${NEW_XDG_CONFIG_HOME}/mpv/mpv.conf" \ --ro-bind /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1 \ --ro-bind /lib/libacl.so.1 /lib/libacl.so.1 \ --ro-bind /lib/libcrypto.so.3 /lib/libcrypto.so.3 \ --ro-bind /lib/libssl.so.3 /lib/libssl.so.3 \ --ro-bind /lib/libz.so.1 /lib/libz.so.1 \ --proc /proc/ \ --ro-bind /sys/dev/char/ /sys/dev/char/ \ --ro-bind /sys/devices/pci0000:00/ /sys/devices/pci0000:00/ \ --ro-bind "${XDG_RUNTIME_DIR}/pipewire-0" "${XDG_RUNTIME_DIR}/pipewire-0" \ --ro-bind "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" \ --ro-bind /usr/bin/mpv /usr/bin/mpv \ --ro-bind /usr/lib/ /usr/lib/ \ --ro-bind /usr/share/X11/xkb/ /usr/share/X11/xkb/ \ --ro-bind /usr/share/fontconfig/ /usr/share/fontconfig/ \ --ro-bind /usr/share/fonts/ /usr/share/fonts/ \ --ro-bind /usr/share/pipewire/ /usr/share/pipewire/ \ --ro-bind "$1" "$(realpath "$1")" \ /usr/bin/mpv \ --player-operation-mode=pseudo-gui \ --title='bwrap | ${media-title}' \ "$1"
--ro-bind "$1" "$(realpath "$1")" \

Get the absolute pathname using realpath, so you can pass a relative argument and still bind the argument.

--player-operation-mode=pseudo-gui \

Because --new-session is used, this is needed to have a way to control mpv when it would otherwise not show a GUI.

Tip: If you followed Bubblewrap#.desktop_integration, you should remove it from "${XDG_DATA_HOME}/applications/bwrap-mpv.desktop".
--title='bwrap | ${media-title}' \

Set the window title, showing at a glance that you are using bwrap.

mpv-net

If you want to use mpv to stream over the Internet, you will need a few things more.

Tip: You can use both bwrap-mpv and bwrap-mpv-net. bwrap-mpv for local stuff and bwrap-mpv-net for watching over the Internet.

Contents of ~/.local/bin/bwrap-mpv-net

#!/usr/bin/env sh # mpv wrapped in bwrap with network access. set -u XDG_CACHE_HOME="${XDG_CACHE_HOME:=$HOME/.cache}" XDG_CONFIG_HOME="${XDG_CONFIG_HOME:=$HOME/.config}" NEW_HOME='/home/user' NEW_XDG_CACHE_HOME="${NEW_HOME}${XDG_CACHE_HOME#"$HOME"}" NEW_XDG_CONFIG_HOME="${NEW_HOME}${XDG_CONFIG_HOME#"$HOME"}" /usr/bin/bwrap \ --unshare-all \ --share-net \ --new-session \ --die-with-parent \ --clearenv \ --setenv HOME "$NEW_HOME" \ --setenv PATH /usr/bin/ \ --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \ --setenv XDG_CACHE_HOME "$NEW_XDG_CACHE_HOME" \ --setenv XDG_CONFIG_HOME "$NEW_XDG_CONFIG_HOME" \ --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR" \ --hostname localhost \ --dev /dev/ \ --dev-bind /dev/dri/renderD128 /dev/dri/renderD128 \ --ro-bind /etc/fonts/ /etc/fonts/ \ --ro-bind /etc/mpv/ /etc/mpv/ \ --ro-bind /etc/resolv.conf /etc/resolv.conf \ --ro-bind /etc/ssl/certs/ /etc/ssl/certs/ \ --bind-try "${XDG_CACHE_HOME}/mesa_shader_cache/" "${NEW_XDG_CACHE_HOME}/mesa_shader_cache/" \ --ro-bind-try "${XDG_CONFIG_HOME}/mpv/mpv.conf" "${NEW_XDG_CONFIG_HOME}/mpv/mpv.conf" \ --ro-bind /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1 \ --ro-bind /lib/libacl.so.1 /lib/libacl.so.1 \ --ro-bind /lib/libcrypto.so.3 /lib/libcrypto.so.3 \ --ro-bind /lib/libssl.so.3 /lib/libssl.so.3 \ --ro-bind /lib/libz.so.1 /lib/libz.so.1 \ --proc /proc/ \ --ro-bind /sys/dev/char/ /sys/dev/char/ \ --ro-bind /sys/devices/pci0000:00/ /sys/devices/pci0000:00/ \ --ro-bind "${XDG_RUNTIME_DIR}/pipewire-0" "${XDG_RUNTIME_DIR}/pipewire-0" \ --ro-bind "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" \ --ro-bind /usr/bin/mpv /usr/bin/mpv \ --ro-bind /usr/bin/python3 /usr/bin/python3 \ --ro-bind /usr/bin/yt-dlp /usr/bin/yt-dlp \ --ro-bind /usr/lib/ /usr/lib/ \ --ro-bind /usr/share/X11/xkb/ /usr/share/X11/xkb/ \ --ro-bind /usr/share/fontconfig/ /usr/share/fontconfig/ \ --ro-bind /usr/share/fonts/ /usr/share/fonts/ \ --ro-bind /usr/share/pipewire/ /usr/share/pipewire/ \ /usr/bin/mpv \ --player-operation-mode=pseudo-gui \ --title='bwrap | ${media-title}' \ "$@"
--setenv PATH /usr/bin/ \
Todo: Document why this is needed.


--ro-bind /etc/ssl/certs/ /etc/ssl/certs/ \

Certificate authorities that are trusted.

"$@"

This is so you can pass more options to bwrap-mpv-net, 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 "$XDG_DESKTOP_DIR" (which is "${HOME}/Desktop/", which will fallback to "$HOME/" if "${HOME}/Desktop/" doesn't exist). That means that by default bwrap-mpv[-net] 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 mpv use that directory:

Contents of "${XDG_CONFIG_HOME}/mpv/mpv.conf"

... # Something like this, change to match "$XDG_DATA_HOME" screenshot-template="~/.local/share/mpv/screenshots/%F [%p] %02n" ...

yt-dlp

Contents of ~/.local/bin/bwrap-yt-dlp

#!/usr/bin/env sh # yt-dlp wrapped in bwrap with network access. set -u XDG_CONFIG_HOME="${XDG_CONFIG_HOME:=$HOME/.config}" NEW_HOME='/home/user' NEW_XDG_CONFIG_HOME="${NEW_HOME}${XDG_CONFIG_HOME#"$HOME"}" mkdir -pm 0700 "${HOME}/Downloads/" /usr/bin/bwrap \ --unshare-all \ --share-net \ --new-session \ --die-with-parent \ --clearenv \ --setenv HOME "$NEW_HOME" \ --setenv XDG_CONFIG_HOME "$NEW_XDG_CONFIG_HOME" \ --hostname localhost \ --ro-bind /etc/resolv.conf /etc/resolv.conf \ --ro-bind /etc/ssl/certs/ /etc/ssl/certs/ \ --ro-bind-try "${XDG_CONFIG_HOME}/yt-dlp/config" "${NEW_XDG_CONFIG_HOME}/yt-dlp/config" \ --bind "${HOME}/Downloads/" "${NEW_HOME}/Downloads/" \ --ro-bind /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1 \ --ro-bind /lib/libcrypto.so.3 /lib/libcrypto.so.3 \ --ro-bind /lib/libssl.so.3 /lib/libssl.so.3 \ --ro-bind /lib/libz.so.1 /lib/libz.so.1 \ --ro-bind /usr/bin/ffmpeg /usr/bin/ffmpeg \ --ro-bind /usr/bin/python3 /usr/bin/python3 \ --ro-bind /usr/bin/yt-dlp /usr/bin/yt-dlp \ --ro-bind /usr/lib/ /usr/lib/ \ /usr/bin/yt-dlp "$@"
--bind "${HOME}/Downloads/" "${NEW_HOME}/Downloads/" \

Directory for writing the files. This should match --output that is either in "${XDG_CONFIG_HOME}/yt-dlp/config" or passed on the command line.

/usr/bin/yt-dlp "$@"

This is so you can pass more options to bwrap-yt-dlp, for example:

$ bwrap-yt-dlp --no-playlist 'URL1' 'URL2'

zathura

Contents of ~/.local/bin/bwrap-zathura

#!/usr/bin/env sh # zathura wrapped in bwrap. set -u if [ "$#" != 1 ] then printf 'Run zathura wrapped in bwrap. Usage: $ bwrap-zathura PDF\n' exit 1 fi XDG_CONFIG_HOME="${XDG_CONFIG_HOME:=$HOME/.config}" XDG_DATA_HOME="${XDG_DATA_HOME:=$HOME/.local/share}" mkdir -pm 0700 "${XDG_DATA_HOME}/zathura/" /usr/bin/bwrap \ --unshare-all \ --new-session \ --die-with-parent \ --clearenv \ --setenv HOME "$HOME" \ --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY" \ --setenv XDG_CONFIG_HOME "$XDG_CONFIG_HOME" \ --setenv XDG_DATA_HOME "$XDG_DATA_HOME" \ --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR" \ --ro-bind /etc/fonts/ /etc/fonts/ \ --ro-bind-try "${XDG_CONFIG_HOME}/zathura/zathurarc" "${XDG_CONFIG_HOME}/zathura/zathurarc" \ --bind "${XDG_DATA_HOME}/zathura/" "${XDG_DATA_HOME}/zathura/" \ --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/libmount.so.1 /lib/libmount.so.1 \ --ro-bind /lib/libz.so.1 /lib/libz.so.1 \ --ro-bind "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" \ --ro-bind /usr/bin/zathura /usr/bin/zathura \ --ro-bind /usr/lib/ /usr/lib/ \ --ro-bind /usr/share/X11/xkb/ /usr/share/X11/xkb/ \ --ro-bind /usr/share/fonts/ /usr/share/fonts/ \ --ro-bind /usr/share/misc/magic.mgc /usr/share/misc/magic.mgc \ --ro-bind "$1" "$(realpath "$1")" \ /usr/bin/zathura "$1"
mkdir -pm 0700 "${XDG_DATA_HOME}/zathura/"

Have to premake the directory for zathura 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 file(1) man page for more information.

--ro-bind "$1" "$(realpath "$1")" \

Get the absolute pathname using realpath, so you can pass a relative argument and still bind the argument.